为什么DbSet <tentity>没有实现EnumerableAsync </tentity>

时间:2014-08-08 13:11:02

标签: entity-framework async-await ienumerable entity-framework-6 c#-5.0

在Entity framework 6.1.1中,IDbSet表示可以从数据库查询的实体集合,其具体实现是DbSet,如

中所述。

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

4 个答案:

答案 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)

因为您不能拥有Enumerablethat class is static。您可以拥有IEnumerable,但List,Array和Dictionaries都会实现它,那么您的基础类型会在ToEnumerableAsync()中出现什么?

你可以轻松做到

IEnumerable<TEntity> result = await yourDbSet.ToListAsync();

答案 2 :(得分:1)

虽然问题已得到解答,但如果您因为不同原因想要与IEnumerable进行协方差,也可能会找到此主题。

例如,您可能希望创建一个明确不公开具体列表实现的接口。同时从同步实施迁移,大量使用IListIEnumerable而不是List[]

在这种情况下,你可以这样做:

query.ToListAsync().ContinueWith(task => task.Result.AsEnumerable())

请注意,Result很危险,可能导致await死锁。但是,如果你正在使用await,那么首先应该没有这个问题,因为IEnumerable e = await query.ToListAsync()工作得很好。

答案 3 :(得分:0)

通过探索迭代异步源的正确方法,可以找到问题的答案“为什么......”。

您无法使用IEnumerableforeach,因为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的方法。