只有实现IAsyncEnumerable的源才能用于Entity Framework异步操作

时间:2016-08-26 15:18:35

标签: c# entity-framework asynchronous

我正在使用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方法更改为async

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的源才能用于Entity Framework异步操作。

删除问题仍然存在的Where()子句,因此问题似乎与ToListAsync();

有关
var result = _context.Users.ToListAsync();

仔细阅读异常文本,我理解&#34; ToList()生成的IQueryable没有实现IAsyncEnumerable&#34;但这对我没有意义,因为所有这些行为都是ToListAsync();

的内部行为

有人可以帮助我更好地了解引擎盖下发生的事情吗?我能做些什么让ToListAsync()按预期工作?

提前感谢您的任何评论

3 个答案:

答案 0 :(得分:10)

你会想做这两件事之一。

在两个程序集中引用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,这允许您指定在操作恢复时应该重新捕获相同的线程上下文。它节省了资源而没有这样做(传递假)但你只能在可能的情况下这样做。在上面的例子中它在库中完成。同样在上面的示例中,它不是从Controller完成的,因为您需要与线程关联的Http上下文(传递true)。
  • 我没有考虑清理资源(例如使AthleteService一次性清理DbContext)或任何注入依赖项。

答案 1 :(得分:4)

使用Entity Framework Core时,必须使用以下命名空间:

using Microsoft.EntityFrameworkCore;

代替

using System.Data.Entity;

答案 2 :(得分:0)

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

简而言之:去掉.ToListAsync()并用.ToList()替换它

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

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