【问题标题】:Only sources that implement IAsyncEnumerable can be used for Entity Framework asynchronous operations只有实现 IAsyncEnumerable 的源才能用于实体框架异步操作
【发布时间】:2016-08-26 15:18:34
【问题描述】:

我正在使用 EF 6.1.3 和 .NET Framework 4.6.1 实现模型。 此模型由 ASPNET 应用程序和 ASPNET CORE 应用程序使用,因此它使用 System.Data.Entity 并且它位于单独的程序集 mymodel.dll 中。

这是模型

using System.Data.Entity;
public partial class MyDbContext : DbContext
{    
        public virtual DbSet<Athlete> Athletes{ get; set; }
}
public partial class Athlete
{
    public Athlete()
    {
    }
    //...
    public string Country { get; set; }
}

我正在使用 .NET Framework 4.6 开发在 aspnet 核心中实现的 MVC 应用程序。它引用了 EF 6.1.3,以便可以使用该模型。

public class MyViewModel
{
    public IList<Athlete> ItalianAthletes{ get; set; }
}

using Microsoft.EntityFrameworkCore;
//solution: comment the previous line and use instead System.Data.Entity;
public class MyController : Controller
{
    private readonly MyDbContext _context;
    //...
    public IActionResult Index()
    {
       MyViewModel myvm = new MyViewModel();
       var result = _context.Athletes.Where(a=>a.Country=="Italy").ToList();
       myvm.ItalianAthletes = result ;
       return View(myvm);
    }
}

... 它按预期工作。

现在将 Index 方法更改为异步

public async Task<IActionResult> Index()
{
   MyViewModel myvm = new MyViewModel();
   var result = _context.Athletes.Where(a=>a.Country=="Italy").ToListAsync();
   await result; //at this point an exception is thrown
   //...
}

InvalidOperationException:源 IQueryable 未实现 IAsyncEnumerable。只有实现 IAsyncEnumerable 的源才能用于实体框架异步操作。

删除 Where() 子句问题依然存在,所以问题似乎与 ToListAsync();

var result = _context.Users.ToListAsync();

仔细阅读异常的文本,我了解到“ToList() 生成的 IQueryable 没有实现 IAsyncEnumerable”,但这对我来说没有意义,因为所有这些行为都是 ToListAsync() 内部的;

有人可以帮助我更好地了解幕后发生的事情吗?我该怎么做才能使 ToListAsync() 按预期工作?

提前感谢您的任何评论

【问题讨论】:

标签: c# entity-framework asynchronous


【解决方案1】:

如果你使用的是 Entity Framework Core,那么你必须使用这个命名空间:

using Microsoft.EntityFrameworkCore;

而不是

using System.Data.Entity;

【讨论】:

  • 这是最准确的答案和最新的答案,必须投票作为解决方案。
  • 这个答案是纯金
【解决方案2】:

你会想做这两件事中的一件。

在两个程序集中引用 EF nuget 包。这是因为这个ToListAsync() 操作实际上是通过您的 EF DbContext 调用的,而这不能从没有引用 EF NugetPackage 的项目中完成。如果已经出现这种情况,请确保您在代码顶部的 using 语句中引用了命名空间 System.Data.Entity

using System.Data.Entity;

因为这是您要调用的扩展方法ToListAsync 的位置。


将从 EF 检索的代码包装在使用 EF 的项目中的服务中,使调用异步,然后从您的 asp.net mvc 项目中调用它。这将是我的首选,因为它添加了一个很好的抽象层,并使您的代码更易于测试/维护。

第二个选项的代码示例

public interface IAthleteService {
    Task<List<Athlete>> GetAthletesByCountryAsync(string country, CancellationToken token);
}

public class AthleteService : IAthleteService {
    private MyDbContext _context;
    public async Task<List<Athlete>> GetAthletesByCountryAsync(string country, CancellationToken token)
    {
        return await _context.Athletes.Where(athlete => athlete.Country == country).ToListAsync(token).ConfigureAwait(false);
    }
}

public class MyController : Controller
{
    private readonly IAthleteService _service;
    //...
    public async Task<IActionResult> Index(CancellationToken token)
    {
       MyViewModel myvm = new MyViewModel();
       myvm.ItalianAthletes = await _service.GetAthletesByCountryAsync("Italy", token).ConfigureAwait(true);
       // rest of code
    }   
}

注意事项:

  • 我使用了 CancellationToken,它允许取消异步操作。这完全是可选的。
  • 我使用了ConfigureAwait,这允许您指定在操作恢复时是否应该重新捕获相同的线程上下文。它节省了不这样做的资源(通过 false),但您只能在可能的情况下这样做。在上面的示例中,它是在库中完成的。同样在上面的示例中,它不是从 Controller 完成的,因为您需要与线程关联的 Http 上下文(传递 true)。
  • 我没有考虑资源清理(例如使 AthleteService 一次性清理 DbContext)或任何依赖项注入。

【讨论】:

  • 两个程序集都已经在引用 EF。 6.1.3(编辑我的问题)
  • @RobertoVanoli - 你确定using System.Data.Entity; 包含在你的控制器代码文件的顶部吗?此命名空间包含您要调用的扩展方法。
  • 你猜我引用了错误的 EF 是对的。实际上,我在控制器中使用了 Microsoft.EntityFrameworkCore,而是使用 System.Data.Entity 现在调用了正确的 ToListAsync() 方法(不清楚为什么 ToList() 版本之前可以工作)。 PS:我会为您的额外建议添加+1,但我的声誉目前不允许我投票:-(。无论如何谢谢你,问候
  • @RobertoVanoli - 很好,很高兴你成功了!您现在应该可以投票了,因为您的代表超过 15 人(我在 20 或 30 分钟前投票赞成您的问题)。
  • 我代表你这样做了@RobertoVanoli 很好的解决方案 :)
【解决方案3】:

这个话题很老,但由于相同或不同的原因,我在 2021 年遇到了同样的错误。

简而言之:把.ToListAsync()去掉,换成.ToList()

我在一个单元测试项目中使用 EntityFrameworkCoreMock.Moq 在我的 .NET5 项目中遇到了这个问题,但由于这个错误而失败。我问了一个朋友,并被告知要摆脱 ToListAsync()。出于这个原因,我决定也摆脱 AsyncFixer。

我对那个解决方案也不是很满意,并且讨厌在运行时因为奇怪的原因而失败的代码。但这就是我最终得到的解决方案。

【讨论】:

  • 老兄,我没有任何不尊重的意思,但是您对问题的“解决方案”是有效的,因为它不会异步运行。我认为您错过了使用异步操作的全部要点,这确实值得研究。
  • 正如所说的“我对那个解决方案并不满意”。那时我尝试了一切,并在这里记录了我最终做了什么。不是获得最多支持的提案,但可能对寻求快速修复的人有所帮助。
猜你喜欢
  • 1970-01-01
  • 2015-04-17
  • 2020-03-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-02
  • 1970-01-01
  • 2014-12-27
相关资源
最近更新 更多