只有实现IAsyncEnumerable的源才能用于Entity Framework异步操作 [英] Only sources that implement IAsyncEnumerable can be used for Entity Framework asynchronous operations

查看:151
本文介绍了只有实现IAsyncEnumerable的源才能用于Entity Framework异步操作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



这是模型

 ,使用System.Data.Entity; 
公共部分类MyDbContext:DbContext
{
公共虚拟DbSet< Athlete>运动员{组; }
}
公共部分类运动员
{
公共运动员()
{
}
// ...
公共字符串Country {get;组; }
}

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

 公共类MyViewModel 
{
public IList< Athlete>意大利运动员{组; }
}

使用Microsoft.EntityFrameworkCore;
//解决方案:注释上一行,而改用System.Data.Entity;
公共类MyController:控制器
{
private readonly MyDbContext _context;
// ...
public IActionResult Index()
{
MyViewModel myvm = new MyViewModel();
var result = _context.Athletes.Where(a => a.Country == Italy)。ToList();
myvm.ItalianAthletes =结果;
return View(myvm);
}
}

...,它可以正常工作。



现在将Index方法更改为异步

 公共异步任务< IActionResult> Index()
{
MyViewModel myvm = new MyViewModel();
var result = _context.Athletes.Where(a => a.Country == Italy)。ToListAsync();
等待结果; //此时会引发异常
// ...
}



< blockquote>

InvalidOperationException:源IQueryable没有实现IAsyncEnumerable。只有实现IAsyncEnumerable的源才可以用于Entity Framework异步操作。


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

  var结果= _context.Users.ToListAsync(); 

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



有人可以帮助我更好地了解幕后的情况吗?而我该怎么做才能使ToListAsync()像预期的那样工作?



在此先感谢您的任何评论

解决方案

您将要执行以下两项操作之一。



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

 使用System.Data.Entity; 

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






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



第二个选项的代码示例

 公共接口IAthleteService {
Task< List< Athlete>> GetAthletesByCountryAsync(字符串国家,CancellationToken令牌);
}

公共类AthleteService:IAthleteService {
private MyDbContext _context;
public async Task< List< Athlete>> GetAthletesByCountryAsync(字符串国家,CancellationToken令牌)
{
return wait _context.Athletes.Where(athlete => sportser.Country == country).ToListAsync(token).ConfigureAwait(false);
}
}

公共类MyController:控制器
{
private readonly IAthleteService _service;
// ...
公共异步任务< IActionResult>索引(CancellationToken令牌)
{
MyViewModel myvm = new MyViewModel();
myvm.ItalianAthletes =等待_service.GetAthletesByCountryAsync(意大利,令牌).ConfigureAwait(true);
//其余代码
}
}

注意:




  • 我使用了CancellationToken,它允许取消异步操作。这是完全可选的。

  • 我使用了ConfigureAwait,它允许您指定在恢复操作时是否应重新捕获同一线程上下文。它节省了不执行此操作的资源(传递false),但是您只能在可能的情况下执行此操作。在上面的示例中,它是在库中完成的。同样在上面的示例中,由于您需要与该线程关联的Http上下文(传递true),因此无法从Controller中完成。

  • 我没有考虑清理资源(例如使AthleteService可用于清理DbContext)或任何依赖项注入。


I'm implementing a Model using EF 6.1.3 and .NET Framework 4.6.1. This model is used by an ASPNET app and by an ASPNET CORE app, for that reason it uses System.Data.Entity and it is located in a separate assembly mymodel.dll.

This is the model

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; }
}

I'm developing the MVC app that is implemented in aspnet core with .NET Framework 4.6. It references EF 6.1.3 so that the model can be used.

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);
    }
}

... and it works as expected.

Now changing the Index method to 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: The source IQueryable doesn't implement IAsyncEnumerable. Only sources that implement IAsyncEnumerable can be used for Entity Framework asynchronous operations.

Removing the Where() clause the problem persists, so the problem seems related to ToListAsync();

var result = _context.Users.ToListAsync();

Carefully reading the text of the exception I understand that "the IQueryable generated by ToList() doesnt implement IAsyncEnumerable " but this doesnt make sense to me because all that behavior is internal to ToListAsync();

Someone can help me to better understand what's happening here under the hood? and what can I do so that ToListAsync() works as expected ?

thank you in advance for any comment

解决方案

You will want to do one of these 2 things.

Reference the EF nuget package in both assemblies. This is because this ToListAsync() operation is actually being called through to your EF DbContext and this cannot be done from a project that has no reference to the EF NugetPackage. If this is already the case make sure you are referencing the namespace System.Data.Entity in the using statements at the top of the code:

using System.Data.Entity;

as this is the location of the extension method ToListAsync you want to call.


Wrap the code that retrieves from EF in a service in your project that uses EF, make the call async, and call that from your asp.net mvc project. This would be my preferred choice as it adds a nice layer of abstraction and makes your code easier to test/maintain.

Code example for 2nd option

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
    }   
}

Notes:

  • I used a CancellationToken, it allows for the cancellation of an async operation. This is completely optional.
  • I used ConfigureAwait, this allows you to specify whethere the same thread context should be recaptured when the operation resumes. It saves resources to not do it (pass false) but you can only do that when its possible. In the above example its done in the library. Also in the example above it is not done from the Controller because you need the Http context associated with the thread (pass true).
  • I did not take into account cleaning up of resources (like making AthleteService disposable to cleanup the DbContext) or any injection of dependencies.

这篇关于只有实现IAsyncEnumerable的源才能用于Entity Framework异步操作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆