只有实现了IAsyncEnumerable的源才能用于实体框架的异步操作
本文关键字:用于 实体 异步操作 框架 实现 IAsyncEnumerable | 更新日期: 2023-09-27 18:04:05
我正在使用EF 6.1.3和。net Framework 4.6.1实现一个模型。这个模型由asp.net应用程序和asp.net 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; }
}
我正在开发MVC应用程序,在asp.net core与。net Framework 4.6实现。它引用了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的源才能用于实体框架的异步操作。
删除Where()子句问题仍然存在,所以问题似乎与ToListAsync();
var result = _context.Users.ToListAsync();
仔细阅读异常的文本,我明白"IQueryable生成ToList()不实现IAsyncEnumerable",但这对我来说没有意义,因为所有的行为都是内部ToListAsync();
有人能帮我更好地理解这里发生了什么吗?我该怎么做才能使ToListAsync()按预期工作?提前感谢您的任何评论
如果你正在使用实体框架核心,那么你必须使用这个命名空间:
using Microsoft.EntityFrameworkCore;
不是using System.Data.Entity;
EF Core Solution
您需要使用这个using
语句。
using Microsoft.EntityFrameworkCore;
EF6解决方案
你会想做这两件事中的一件。
在两个程序集中引用EF核包。这是因为这个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)可以节省资源,但你只能在可能的情况下这样做。在上面的例子中,它在库中完成。同样在上面的例子中,它不是从控制器完成的,因为你需要与线程关联的Http上下文(传递true)。
- 我没有考虑到资源的清理(比如让AthleteService一次性清理DbContext)或任何依赖项的注入。
这个话题很老了,但是我在2021年因为相同或不同的原因遇到了同样的错误。
总之:去掉.ToListAsync()
,代之以.ToList()
我在我的。net 5项目中使用EntityFrameworkCoreMock遇到了这个问题。Moq在单元测试项目中失败,并出现此错误。我问了一个朋友,被告知要摆脱ToListAsync()。出于这个原因,我决定也摆脱AsyncFixer。
我对这个解决方案也不是很满意,我讨厌那些在运行时因为奇怪的原因而失败的代码。但这就是我最终的解决方案。