tags:

views:

65

answers:

3

How would you suggest using AsEnumerable on a non-generic IQueryable?

I cannot use the Cast<T> or OfType<T> methods to get an IQueryable<object> before calling AsEnumerable, since these methods have their own explicit translation by the underlying IQueryProvider and will break the query translation if I use them with a non-mapped entity (obviously object is not mapped).

Right now, I have my own extension method for this (below), but I'm wondering if there's a way built into the framework.

public static IEnumerable AsEnumerable(this IQueryable queryable)
{
    foreach (var item in queryable)
    {
        yield return item;
    }
}

So, with the above extension method, I can now do:

IQueryable myQuery = // some query...

// this uses the built in AsEnumerable, but breaks the IQueryable's provider because object is not mapped to the underlying datasource (and cannot be)
var result = myQuery.Cast<object>().AsEnumerable().Select(x => ....);

// this works (and uses the extension method defined above), but I'm wondering if there's a way in the framework to accomplish this
var result = myQuery.AsEnumerable().Cast<object>().Select(x => ...);
+3  A: 

The type IQueryable inherits from the non-generic IEnumerable so this extension method doesn't seem to serve any purpose. Why not just use it as IEnumerable directly?

JaredPar
It does serve a purpose by forcing the queryable to enumerate and become in memory. I use it becuase I can then subsequently use Cast<T> or OfType<T> with the desired effect.
JeffN825
+2  A: 

JaredPar's answer in other words:

public static IEnumerable AsEnumerable(this IEnumerable source)
{
    return source;
}

Usage:

IQueryable queryable = // ...
IEnumerable enumerable = queryable.AsEnumerable();
IEnumerable<Foo> result = enumerable.Cast<Foo>();
//                                     ↑
//                           Enumerable.Cast<TResult>(this IEnumerable source)
dtb
This is not really correct, see above.
JeffN825
@JeffN825: This is *exactly* what the generic Enumerable.AsEnumerable<T> extension method does. You can use Reflector to confirm this.
dtb
@dtb, but it's an IQueryable. Of course that's what the Enumerable version does... Note the different signature: `public static IEnumerable<TSource> AsEnumerable<TSource>(this IEnumerable<TSource> source)`
Kirk Woll
@Kirk Woll: Good spot. Fixed!
dtb
So, does the generic AsEnumerable actually NOT cause the query to enumerate? As in, it's actually the SUBSEQUENT call (like to Select) which forces enumeration?
JeffN825
@JeffN825: Only `foreach` or aggregate methods like `.Count()` actually force execution. This is called **deferred execution**.
dtb
+2  A: 

Since the interface IQueryable inherits from IEnumerable why not:

IQueryable queryable;
IEnumerable<T> = (queryable as IEnumerable).Cast<T>();

Edit
There are two Cast<> extension methods:

public static IQueryable<TResult> Cast<TResult>(this IQueryable source)
public static IEnumerable<TResult> Cast<TResult>(this IEnumerable source)

Which one is called is statically decided by the compiler. So casting an IQueryable as an IEnumerable will cause the second extension method to be called, where it will be treated as an IEnumerable.

Greg
Hmmm...will that actually call the IQueryable implementation of Cast<T> or the IEnumerable implementation? That is, will the second line REALLY be just an IEnumerable, or will it still actually be an IQueryable?
JeffN825
@Jeff - Please see my edit
Greg
@JeffN825, the compiler would only know it's an IEnumerable, and so would link to the Enumerable.Cast<T> implementation.
Kirk Woll
Ahhh, so it's the extension method inference done by the compiler which makes it enumerate on the NEXT call which uses the System.Linq.Enumerable extension methods instead of the System.Linq.Queryable ones...
JeffN825