我目前正在为我的ASP.VNext应用程序实现repository pattern。我希望这些方法是异步和可过滤的。所以我设计了以下接口方法:
Task<TEntity> GetOneAsync(Func<TEntity,bool> predicate);
并希望像这样实现它(使用私有DbContext实例ctx
):
public async Task<MyEntity> GetOneAsync(Func<MyEntity,bool> predicate)
{
// compiler error
return await ctx.MyEntities.Where(predicate).FirstOrDefaultAsync();
}
但是,我只能在对谓词进行硬编码时使用FirstOrDefaultAsync()
:
return await ctx.MyEntites.Where(e => e.Id == 1).FirstOrDefaultAsync();
传递谓词时,我只得到没有async选项的FirstOrDefault()
,所以为了使我的方法异步,我必须写
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;
}
我有两个问题:
为什么在传递谓词时无法访问FirstOrDefaultAsync()
方法?
使用await Task.Run(synchronousMethod)
的解决方案是否与调用FirstOrDefaultAsync()
的行为相同?
答案 0 :(得分:1)
FirstOrDefaultAsync
被定义为IQueryable<T>
的扩展方法。
ctx.MyEntities.Where(e => e.Id == 1)
返回IQueryable<MyEntity>
。
ctx.MyEntities.Where(predicate)
会返回IEnumerable<MyEntity>
,因为您正在调用Enumerable.Where
扩展方法,而不是Queryable.Where
方法。
要使其有效,请将predicate
从Func<MyEntity, bool>
更改为Expression<Func<MyEntity, bool>>
。这意味着predicate
不再只是一个提供所需结果的函数,而是一个对该函数的描述,然后实体框架可以转换为SQL。
不,在任务中使用Func<MyEntity, bool>
不会有相同的行为。这将从数据库服务器加载行而不进行任何过滤,并在数据库客户端评估每个行,直到找到匹配项。这会增加很多开销。