Async/Await in Foreach Loop(That is looping ef models) Generates an Error

asp.net-core async-await c# entity-framework-core

Question

I have a foreach loop and the first time it goes around in the first item it works find but when it gets to the next item, I get this error.

System.InvalidOperationException
  HResult=0x80131509
  Message=Error generated for warning 'Microsoft.EntityFrameworkCore.Infrastructure.LazyLoadOnDisposedContextWarning: An attempt was made to lazy-load navigation property 'Company' on entity type 'RecurringInvoiceTemplateProxy' after the associated DbContext was disposed.'. This exception can be suppressed or logged by passing event ID 'CoreEventId.LazyLoadOnDisposedContextWarning' to the 'ConfigureWarnings' method in 'DbContext.OnConfiguring' or 'AddDbContext'.
  Source=Microsoft.EntityFrameworkCore
  StackTrace:
   at Microsoft.EntityFrameworkCore.Diagnostics.EventDefinition`2.Log[TLoggerCategory](IDiagnosticsLogger`1 logger, WarningBehavior warningBehavior, TParam1 arg1, TParam2 arg2, Exception exception)
   at Microsoft.EntityFrameworkCore.Internal.CoreLoggerExtensions.LazyLoadOnDisposedContextWarning(IDiagnosticsLogger`1 diagnostics, DbContext context, Object entityType, String navigationName)
   at Microsoft.EntityFrameworkCore.Internal.LazyLoader.ShouldLoad(Object entity, String navigationName, NavigationEntry& navigationEntry)
   at Microsoft.EntityFrameworkCore.Internal.LazyLoader.Load(Object entity, String navigationName)
   at Microsoft.EntityFrameworkCore.Proxies.Internal.LazyLoadingInterceptor.Intercept(IInvocation invocation)
   at Castle.DynamicProxy.AbstractInvocation.Proceed()
   at Castle.Proxies.RecurringInvoiceTemplateProxy.get_Company()
   at SourceLine.Api.Services.InvoicingService.<>c__DisplayClass48_0.<GenerateRecurringInvoicesAsync>b__0(CustomerDto x) in 1299
   at System.Linq.Enumerable.TryGetFirst[TSource](IEnumerable`1 source, Func`2 predicate, Boolean& found)
   at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 source, Func`2 predicate)
   at SourceLine.Api.Services.InvoicingService.<GenerateRecurringInvoicesAsync>d__48.MoveNext() in line 1299
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Threading.ThreadPoolWorkQueue.Dispatch()

my query

 return dbContext.RecurringInvoiceTemplates.Include(x => x.Company)
                                              .Include(x => x.RecurringInvoiceFrequency)
                                              .Where(x => x.NextRun.Date.CompareTo(currentTime.Date) <= 0).ToList();

Query it dies on

    foreach (var r in recurringInvoices){
     var foundCustomer = allCustomers.FirstOrDefault(x => x.Id == r.Company.Identifier);
}

Update

The problem might be actually is because of this

public async void Get(){

    var recurringInvoices = dbContext.RecurringInvoiceTemplates.Include(x => x.Company)
                                                  .Include(x => x.RecurringInvoiceFrequency)
                                                  .Where(x => 
    var allCustomer = new List<Dto>(){
        new Dto(){
            Id = 1
        }
    }

    foreach (var r in recurringInvoices)
    {
        //allcustomers is not en EF object, just a dto.
        var foundCustomer = allCustomers.FirstOrDefault(x => x.Id == r.Company.Identifier);

        if (foundCustomer != null)
        {

                var ApiKey = configuration["SendGrid:Key"];
                var sendGridClient = new SendGridClient(ApiKey);

                var msg = new SendGridMessage();
                msg.SetFrom(new EmailAddress("example@test.com, "Example User"));


                msg.SetTemplateId("d-f06bfad7374b4a3cb2ccbf846d8e96a4");


                var dynamicTemplateData = new SendInvoiceTemplateDto
                {
                    Subject = "a",
                    Body = "b"
                };

                msg.SetTemplateData(dynamicTemplateData);

                var response = await sendGridClient.SendEmailAsync(msg);
            }
        }
}

So it seems like since I am using async it disposes my dbContext?

Edit 2

I don't understand but when I use

 var response = sendGridClient.SendEmailAsync(msg);
response.Wait();

I don't get this problem anymore.

1
-2
3/1/2019 12:27:20 AM

Accepted Answer

So it seems like since I am using async it disposes my dbContext?

Your problem is due to async void. You should avoid async void. One of the problems caused by async void is that the caller of that method cannot know when it has completed. So, the caller just keeps on executing, eventually completing the HTTP request and tearing down the controller (and anything you have dependency-injected into it), disposing the DbContext and causing that exception because your code is still running.

The solution is to change the async void to async Task and have the caller await the Task returned from Get. If Get is a controller action, then you just have to change the async void to async Task; ASP.NET will automatically handle the Task correctly.

6
3/1/2019 12:52:40 AM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow