【问题标题】:Why DbSet<TEntity> doesn't implement EnumerableAsync为什么 DbSet<TEntity> 不实现 EnumerableAsync
【发布时间】:2014-10-01 23:35:21
【问题描述】:

在实体框架 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

【问题讨论】:

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


    【解决方案1】:

    IEnumerable 的设计不允许它与async/await 一起使用。 IEnumerator&lt;T&gt;.MoveNext() 不能返回调用者可以await 的任何Task 对象,因为它有一个固定的返回类型bool

    async 感知版本的IEnumerableIDbAsyncEnumerable,它已经由DbQuery&lt;T&gt; 实现,DbSet&lt;T&gt; 派生自它,因此不需要AsDbAsyncEnumerable() 扩展方法来工作。

    您的Get 版本,IMO,不需要Async 版本。它已经没有阻塞,因为它没有做任何事情。只有当调用者开始使用返回的枚举时,才会查询数据库。我只需将返回类型更改为DbQuery&lt;TEntity&gt;。 (这需要强制转换,但应该已经是返回的具体类型。)然后调用者可以决定是使用同步方法还是异步方法。

    (实际上,经过仔细检查,我发现虽然您的问题是关于 DbSet&lt;T&gt;,但您实际上使用的是支持 ObjectContext 而不是 DbContext。这可能会给您 ObjectQuery&lt;T&gt; 可查询而不是DbQuery&lt;T&gt; 可查询,答案会有所不同。如果您停止使用 ObjectContext,您自己会更轻松,除非您真的需要。)

    【讨论】:

    • 重新思考这个问题我认为你有更正确的答案,我没想到作者想要一个延迟加载的异步枚举。
    • @hvd ,我更新了问题并添加了一些代码。你认为有更好的方法使非同步方法异步吗?
    • @MHOOS 我认为您根本不需要GetAsync,我认为您可以简单地修改您的Get 方法,并将其添加到我的答案中。
    • Nitpick:“MoveNext() 没有返回类型” 实际上,它返回bool
    • @svick 哎呀!是的,我没有完全说出我想说的话。 :)
    【解决方案2】:

    因为您不能拥有Enumerablethat class is static。你可以有一个IEnumerable,但是列表、数组和字典都实现了它,那么你的ToEnumerableAsync() 中的底层类型是什么?

    你可以轻松做到

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

    【讨论】:

    • 这可能会破坏 IEnumerable 的目的,即作为管道最小化峰值内存使用量。
    • IEnumerable 的目的只是为了定义一个接口。重要的是具体的实现,有很多类实现了IEnumerable,包括List&lt;T&gt;。我认为这是一种以异步方式查询列表的完全有效的方式。
    【解决方案3】:

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

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

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

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

    请记住,Result 很危险,可能会导致与await 发生死锁。但是如果你使用await,你应该不会有这个问题,因为IEnumerable e = await query.ToListAsync() 工作正常。

    【讨论】:

      【解决方案4】:

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

      您不能使用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);
      });
      

      所以你的函数只是一个方便的包装器,而不是让 foreach over async 更方便的包装器。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-10-31
        • 1970-01-01
        • 2011-11-17
        • 2023-04-05
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多