在Entity framework 6.1.1中,IDbSet表示可以从数据库查询的实体集合,其具体实现是DbSet,如
中所述。为什么这个接口或其具体实现不包含ToEnumerableAsync或AsEnumerableAsync的任何定义,但ToListAsync,ToArrayAsync,ToDictionaryAsync?
为了让您了解我遇到此问题的原因,我有以下一段代码,我希望将其设为异步:
public IQueryable<TEntity> GetQuery<TEntity>() where TEntity : class
{
string entityName = GetEntityName<TEntity>();
return ((IObjectContextAdapter)DbContext).ObjectContext.CreateQuery<TEntity>(entityName);
}
public IEnumerable<TEntity> Get<TEntity, TOrderBy>(Expression<Func<TEntity, TOrderBy>> orderBy, int pageIndex, int pageSize, SortOrder sortOrder = SortOrder.Ascending) where TEntity : class
{
if (sortOrder == SortOrder.Ascending)
{
return GetQuery<TEntity>().OrderBy(orderBy).Skip((pageIndex - 1) * pageSize).Take(pageSize).AsEnumerable();
}
return
GetQuery<TEntity>()
.OrderByDescending(orderBy)
.Skip((pageIndex - 1) * pageSize)
.Take(pageSize)
.AsEnumerable();
}
我想到的唯一实现如下:
public async Task<IEnumerable<TEntity>> GetAsync<TEntity, TOrderBy>(Expression<Func<TEntity, TOrderBy>> orderBy, int pageIndex, int pageSize, SortOrder sortOrder = SortOrder.Ascending) where TEntity : class
{
if (sortOrder == SortOrder.Ascending)
{
return await GetQuery<TEntity>().OrderBy(orderBy).Skip((pageIndex - 1) * pageSize).Take(pageSize).ToListAsync();
}
return
await GetQuery<TEntity>()
.OrderByDescending(orderBy)
.Skip((pageIndex - 1) * pageSize)
.Take(pageSize)
.ToListAsync();
}
这是使方法异步的正确方法吗?
以上方法属于以下链接中实现的通用存储库: Entity Framework POCO, Repository and Specification Pattern 要深入挖掘,您可以查看原始博客: Entity Framework POCO, Repository and Specification Pattern
答案 0 :(得分:9)
IEnumerable
的设计不允许将其与async
/ await
一起使用。 IEnumerator<T>.MoveNext()
无法返回调用者可以Task
的任何await
个对象,因为它有一个固定的返回类型bool
。
async
知晓的IEnumerable
版本为IDbAsyncEnumerable
,而DbQuery<T>
已经由DbSet<T>
实现,因此没有AsDbAsyncEnumerable()
扩展方法是必要的。
您的Get
版本IMO不需要Async
版本。它已经不会阻止,因为它没有做任何事情。只有当调用者开始使用返回的枚举时,才会查询数据库。我只是将返回类型更改为DbQuery<TEntity>
。 (这需要强制转换,但应该已经是返回的具体类型。)然后调用者可以决定是使用同步方法还是异步方法。
(实际上,经过仔细检查,我发现虽然您的问题是关于DbSet<T>
,但您实际上是在使用支持ObjectContext
而不是DbContext
。这可能会给您带来帮助ObjectQuery<T>
可查询而不是DbQuery<T>
可查询,答案将有所不同。如果您停止使用ObjectContext
,除非真的需要。)
答案 1 :(得分:3)
因为您不能拥有Enumerable
,that class is static。您可以拥有IEnumerable
,但List,Array和Dictionaries都会实现它,那么您的基础类型会在ToEnumerableAsync()
中出现什么?
你可以轻松做到
IEnumerable<TEntity> result = await yourDbSet.ToListAsync();
答案 2 :(得分:1)
虽然问题已得到解答,但如果您因为不同原因想要与IEnumerable
进行协方差,也可能会找到此主题。
例如,您可能希望创建一个明确不公开具体列表实现的接口。同时从同步实施迁移,大量使用IList
或IEnumerable
而不是List
或[]
。
在这种情况下,你可以这样做:
query.ToListAsync().ContinueWith(task => task.Result.AsEnumerable())
请注意,Result
很危险,可能导致await
死锁。但是,如果你正在使用await
,那么首先应该没有这个问题,因为IEnumerable e = await query.ToListAsync()
工作得很好。
答案 3 :(得分:0)
通过探索迭代异步源的正确方法,可以找到问题的答案“为什么......”。
您无法使用IEnumerable
到foreach
,因为foreach
并非异步识别。因此,在c#包含foreachAsync语句之类之前,必须使用带有委托给迭代处理程序的特殊函数。
var qry = (from x in db.myTable select f);
await qry.ForEachAsync(async (myRecord) => {
await DoSomethingAsync(myRecord);
});
注意:您需要using System.Data.Entity
才能使ForEachAsync可用
您的通用实现需要(注意它不是异步方法):
public IQueryable<TEntity> PagedQuery<TEntity, TOrderBy>(Expression<Func<TEntity, TOrderBy>> orderBy, int pageIndex, int pageSize, SortOrder sortOrder = SortOrder.Ascending) where TEntity : class
{
if (sortOrder == SortOrder.Ascending)
{
return GetQuery<TEntity>().OrderBy(orderBy).Skip((pageIndex - 1) * pageSize).Take(pageSize)();
}
return
GetQuery<TEntity>()
.OrderByDescending(orderBy)
.Skip((pageIndex - 1) * pageSize)
.Take(pageSize)();
}
使用:
var qry = PagedQuery(...);
await qry.ForEachAsync(async (myRecord) => {
await DoSomethingAsync(myRecord);
});
所以你的函数只是变成了一个方便的包装器,但不是那种使异步更容易使用async的方法。