Casting the results of linq-to-sql
Working with interfaces, we commonly have a var or an IQueryable that is going to return a set of data objects that we will then cast to the interface and return as a List or IList, like this:
var items =
from t in SomeTable
where t.condition == true
select;
return items.ToList( ).Cast<SomeInterface>( ).ToList( );
NOTE: items.Cast( ).ToList( ) will compile, but will throw an InvalidCastException at run time.
Is there a better way? (I put the ToList/Cast/ToList in an extension method, but that's not really any better...)
return items.CastToList<SomeCl开发者_运维百科ass, SomeInterface>( );
Thanks!
What you're doing is correct (you can't call Cast
directly on the IQueryable
), but it's sub-optimal, because you're creating 2 lists when you really only need one. Use AsEnumerable
instead of the first ToList
:
return items.AsEnumerable().Cast<SomeInterface>().ToList();
Calling AsEnumerable
will cause Cast
to be evaluated on a IEnumerable
, not IQueryable
, so the cast will be done on the actual object returned from the DB, rather than directly in the DB.
What's the point of the first ToList()
call? Why not just items.Cast<SomeInterface>().ToList()
?
Alternately, you could do this instead:
var items = from t in SomeTable
where t.condition == true
select (SomeInterface) t;
return items.ToList();
If t
doesn't implement SomeInterface
it will still fail at runtime, though.
This will do the same thing, but won't fail; rather it will give you null
if the object doesn't implement the interface:
var items = from t in SomeTable
where t.condition == true
select t as SomeInterface;
return items.ToList();
A few solutions...
Instead of converting data after collecting it, you could consider make a projection while collecting it.
return items.Select(x=>x as SomeInterface).ToList();
If you do this frequently, an extension method may still be helpful. It doesn't have to be Tolist/Cast/ToList as you mentioned though. Cast will take an IEnumerable already, but you can create an overload that accepts an IQueryable, something like this (untested).
public IEnumerable<TResult> Cast<T, TResult>(this IQueryable<T> input)
{
return input.AsEnumerable().Cast<TResult>();
}
This would whittle your code down to this:
return items.Cast<SomeClass, SomeInterface>.ToList();
精彩评论