Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Freeze a linq IQueryable (as a ToList().AsQueryable() would do)

Is there a way to freeze an IQueryable so that no additional joins will be added to the query when hitting the database? For example, I could do a .ToList() to freeze the query, but that has performance impacts because any filtering I do is on the middle layer and I don't have any performance gains from pre-filtering on the db server?


Edit for clarity:

I have an OData service which returns an IQueryable that the client can filter/sort/project as needed. I just want to prevent them from pulling more data out. I could do that by doing ToList().AsQueryable(), but that loses the advantage of lazyLoading, and with it, the whole purpose of allowing the client to filter the request.

One option that I looked at was to set: EnableQueryAttribute.AllowedQueryOptions to exclude Expand, however even if my initial Query had been expanded, the client is still prevented from selecting those parts.

like image 735
Jacob Eggers Avatar asked Jun 11 '15 01:06

Jacob Eggers


People also ask

What is the use of AsQueryable in LINQ?

AsQueryable() method is used to get an IQueryable reference. Let us see an example to find sum of integer values. Firstly, set an integer array. var arr = new int[] { 100, 200, 300, 400 };

What is the difference between AsEnumerable and AsQueryable?

AsEnumerable preserves deferred execution and does not build an often useless intermediate list. On the other hand, when forced execution of a LINQ query is desired, ToList can be a way to do that. AsQueryable can be used to make an enumerable collection accept expressions in LINQ statements.

What does AsEnumerable do on IQueryable?

What it does is that it converts an IQueryable to IEnumerable and there by splits the Linq statement into 2, one that executes on the Server and other, as local collection. For example, to fix our code above, we would need to change the statement as following. var output = Exams.

Why we use AsEnumerable in LINQ?

In LINQ, AsEnumerble() method is used to convert the specific type of given list to its IEnumerable() equivalent type.


1 Answers

Im assuming that you actually have an IQueryable<T> instead of a IQueryable.

If you don't want your client having access to all IQueryable<T> methods, don't return an IQueryable<T>. Since you want them to be able to only filter/sort/project, create a object witch contains an IQueryable<T> but only expose the desired methods, and use that:

public interface IDataResult<T>
{
    IDataResult<T> FilterBy(Expression<Func<T, bool>> predicate);

    IDataResult<TResult> ProjectTo<TResult>(Expression<Func<T, TResult>> predicate);

    IDataResult<T> SortBy<TKey>(Expression<Func<T, TKey>> keySelector);

    IDataResult<T> SortByDescending<TKey>(Expression<Func<T, TKey>> keySelector);

    List<T> ToList();

    IEnumerable<T> AsEnumerable();
}

public class DataResult<T> : IDataResult<T>
{
    private IQueryable<T> Query { get; set; }

    public DataResult(IQueryable<T> originalQuery)
    {
        this.Query = originalQuery;
    }

    public IDataResult<T> FilterBy(Expression<Func<T, bool>> predicate)
    {
        return new DataResult<T>(this.Query.Where(predicate));
    }

    public IDataResult<T> SortBy<TKey>(Expression<Func<T, TKey>> keySelector)
    {
        return new DataResult<T>(this.Query.OrderBy(keySelector));
    }

    public IDataResult<T> SortByDescending<TKey>(Expression<Func<T, TKey>> keySelector)
    {
        return new DataResult<T>(this.Query.OrderByDescending(keySelector));
    }

    public IDataResult<TResult> ProjectTo<TResult>(Expression<Func<T, TResult>> predicate)
    {
        return new DataResult<TResult>(this.Query.Select(predicate));
    }

    public List<T> ToList()
    {
        return this.Query.ToList();
    }

    public IEnumerable<T> AsEnumerable()
    {
        return this.Query.AsEnumerable();
    }
} 

That way you can also prevent EF and DB related dependencies creeping up on your application. Any change on IQueryable<T> methods will be contained within this class, instead of spread all over the place.

like image 53
Lucas Corsaletti Avatar answered Sep 24 '22 06:09

Lucas Corsaletti