0

I'm using a Task to send an Email in the background. Despite the fact that the PDF (included in the mail as attachment) is generated (GeneratePdf()). I get following exception:

enter image description here

Below is the code that calls GetRegistrationOfChild: enter image description here

EDIT Below is the code where the dbContext is loaded (Startup.cs, method ConfigureServices). Afterwards its resolved with DI in the constructor.

services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

user2810895
  • 1,284
  • 3
  • 19
  • 33
  • Where does `dbContext` come from? – ViRuSTriNiTy Dec 04 '17 at 10:57
  • You're not actually showing us where the code is called from, the missing bit in the middle is key here. Are you closing/disposing the context before it gets a chance to retrieve the data? – DavidG Dec 04 '17 at 11:01
  • I edited the description. The `dbContext` is handled by the DI in the constructor. I'm not closing or disposing the context. – user2810895 Dec 04 '17 at 11:04
  • What happens when you break that into multiple queries? – lloyd Dec 04 '17 at 11:07
  • The `dbContext` will be disposed by the container at the end of the web request. `GeneratePdf` is running **after** the web request (because of `Task.Run`), and thus after the `dbContext` is disposed. Which IoC container are you using? – mjwills Dec 04 '17 at 11:08
  • @mjwills I'm using the default IoC container provided by .net core 2 – user2810895 Dec 04 '17 at 11:09
  • Note that it is not reliable to perform background tasks this way (service can be restarted and your mail will never be sent). – Evk Dec 04 '17 at 11:15

1 Answers1

2

There's a lot of problems here, actually. Your core problem is likely inside the SendRegisterConfirmationAsync method. However, you haven't provided the code for that method. If I had to guess, you're using using statements in a not thread-safe way, based on the error message.

However, it's also entirely possible this is just due to handling async improperly. You're calling an async method from a sync method. Since SendEmail returns void, you're either swallowing the return value of the async method or the async method is async void (which you should pretty much never do). In either case, since you're not awaiting the result of the async method, the rest of your code is moving on potentially taking dependencies with it. For example, things like your DbContext are request-scoped, so if that async method utilizes your DbContext, but doesn't finish before the response is sent, the context will be disposed right from under it, causing exceptions. If you use async, you need to go async all the way.

Further, there's really no such thing as a true "background" task in a web application context. Task.Run merely pulls another thread from the same pool your requests are being handled from. You may allow one thread to return, but you're still sitting on another, so the best case scenario is that you've bought yourself nothing. The worst case scenario is that you've now effectively halved your server's throughput and killed your ability to scale.

If you want to do something in the background, offload it to a background process, i.e. outside the context of your web application. You can use something like Hangfire or Revalee.

Chris Pratt
  • 232,153
  • 36
  • 385
  • 444