Why can't I use ToListAsync() when using a parameter for the predicate in EF7?

asp.net-core c# entity-framework-core


I'm currently implementing the repository pattern for my ASP.VNext application. I would like the methods to be asynchronous and filterable. So I have devised the following interface method:

Task<TEntity> GetOneAsync(Func<TEntity,bool> predicate);

and would like to implement it like this (with a private DbContext instance ctx):

public async Task<MyEntity> GetOneAsync(Func<MyEntity,bool> predicate) 
    // compiler error
    return await ctx.MyEntities.Where(predicate).FirstOrDefaultAsync();

However I can only use FirstOrDefaultAsync() when hardcoding the predicate like this:

return await ctx.MyEntites.Where(e => e.Id == 1).FirstOrDefaultAsync();

When passing the predicate i only get the FirstOrDefault() without the async option, so in order to make my method asynchronous I have to write

public async Task<MyEntity> GetOneAsync(Func<MyEntity,bool> predicate) 
    //save to a local variable to prevent calling a disposed DbContext
    var entities = await Task.Run(() => ctx.Contracts.Where(predicate).FirstOrDefault());
    return entities;

I have two questions regarding this:

  1. Why is it not possible to access the FirstOrDefaultAsync() method when passing a predicate?

  2. Does my solution using await Task.Run(synchronousMethod) achieve the same behavior as a call to FirstOrDefaultAsync() would?

11/9/2015 4:57:53 PM

Accepted Answer

FirstOrDefaultAsync is defined as an extension method for IQueryable<T>.

ctx.MyEntities.Where(e => e.Id == 1) returns IQueryable<MyEntity>.

ctx.MyEntities.Where(predicate) returns IEnumerable<MyEntity>, because you're calling the Enumerable.Where extension method, not the Queryable.Where one.

To make it work, change predicate from Func<MyEntity, bool> to Expression<Func<MyEntity, bool>>. This means predicate is no longer just a function that gives the result you want, but a description of that function, that Entity Framework can then translate to SQL.

And no, using Func<MyEntity, bool> within a task would not have the same behaviour. That would load rows from the db server without any filtering, and evaluate each and every one at the db client until a match is found. That would add a lot of overhead.

10/24/2015 12:30:29 PM

Related Questions


Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow