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.
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.