简体   繁体   中英

Non-Generic AsEnumerable

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 => ...);

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)

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 .

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?

To clarify, you are trying to add a "force expression evaluation" to your linq expression tree so that part of the expression tree is evaluated against the underlying provider (linq to SQL for example) and the rest is evaluated in memory (linq to objects). But you want to be able to specify the entire express tree without actually executing the query or reading any results into memory.

This is a good thing and it shows some insight into the way that Linq works. Good job.

The confusion I see here is your use of "AsEnumerable" as the method name. To me (and I think many people who thinq linq ) "AsEnumerable" is too similar to the "AsQueryable" method which is essentially a cast, not an actual evaluator. I propose you rename your method to "Evaluate". This is my Evaluate() method from my personal Linq extensions library.

    /// <summary>
    /// This call forces immediate evaluation of the expression tree. 
    /// Any earlier expressions are evaluated immediately against the underlying IQueryable (perhaps
    /// a Linq to SQL provider) while any later expressions are evaluated against the resulting object
    /// graph in memory.
    /// This is one way to determine whether expressions get evaluated by the underlying provider or
    /// by Linq to Objects in memory.
    /// </summary>
    public static IEnumerable<T> Evaluate<T>(this IEnumerable<T> expression)
    {
        foreach (var item in expression)
        {
            yield return item;
        }
    }

    /// <summary>
    /// This call forces immediate evaluation of the expression tree. 
    /// Any earlier expressions are evaluated immediately against the underlying IQueryable (perhaps
    /// a Linq to SQL provider) while any later expressions are evaluated against the resulting object
    /// graph in memory.
    /// This is one way to determine whether expressions get evaluated by the underlying provider or
    /// by Linq to Objects in memory.
    /// </summary>
    public static IEnumerable Evaluate(this IEnumerable expression)
    {
        foreach (var item in expression)
        {
            yield return item;
        }
    }

This allows you to write a query where some of the query is evaluated by SQL (for example) and the rest is evaluated in memory. A good thing.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM