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.
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 };
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 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.
In LINQ, AsEnumerble() method is used to convert the specific type of given list to its IEnumerable() equivalent type.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With