Only sources that implement IAsyncEnumerable can be used for asynchronous Entity Framework operations.

I am implementing a model using EF 6.1.3 and the .NET Framework 4.6.1. This model is used by the ASPNET application and the ASPNET CORE application, therefore it uses System.Data.Entity and is located in a separate assembly mymodel.dll.

This is a 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 am developing an MVC application that is implemented in the aspnet core with the .NET Framework 4.6. It refers to 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 works as expected.

Now change 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 does not implement IAsyncEnumerable. Only sources that implement IAsyncEnumerable can be used for asynchronous Entity Framework operations.

Removing the Where () clause the problem persists, therefore the problem is related to ToListAsync ();

 var result = _context.Users.ToListAsync(); 

After carefully reading the exception text, I understand that "IQueryable generated by ToList () does not implement IAsyncEnumerable", but this does not make sense to me because all this behavior is internal to ToListAsync ();

Can someone help me better understand what is happening here under the hood? and what can I do to make ToListAsync () work as expected?

in advance for any comment

+7
c # asynchronous entity-framework
source share
1 answer

You need to do one of these two things.

Link to the EF nuget package in both builds. This is because this ToListAsync() operation is actually called through your EF DbContext, and this cannot be done from a project that does not have a reference to EF NugetPackage. If this is already the case, make sure you refer to the System.Data.Entity namespace in using operations at the top of the code:

 using System.Data.Entity; 

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


Wrap the code that is fetched from EF in a service in your project using EF, make an asynchronous call, and call it from your asp.net mvc project. This would be my preferred choice as it adds a good level of abstraction and makes your code easier to test / maintain.

Sample code for the second 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 CancellationToken, this allows to cancel the async operation. This is completely optional.
  • I used ConfigureAwait, this allows you to specify when the same thread context should be re-captured when the operation resumes. It saves resources in order not to do this (pass false), but you can only do this when possible. In the above example, this is done in the library. Also in the above example, this is not done from the controller, because you need the Http context associated with the stream (pass true).
  • I did not take into account resource cleanup (e.g. using AthleteService to clean up DbContext) or any dependency injection.
+10
source share

All Articles