我目前正在为我的ASP实现存储库模式。VNext应用程序。我希望这些方法是异步的和可过滤的。所以我设计了如下的接口方法:
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();
当传递谓词时,我只得到没有异步选项的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()
调用相同的行为?
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>
不会有相同的行为。这将从db服务器加载行而不进行任何过滤,并在db客户端评估每一行,直到找到匹配。这会增加很多开销。