I am not sure if I am going about this correctly at all.
Background: I have a controller action GET foo() as an example. This foo() needs to go and call bar(), and bar() may take a verrrrrry long time. So, I need foo() to respond with "OK" before (or regardless of when) bar() completes.
A slight complexity is that bar() needs to access the DBContext and fetch some data from the DB. With my current implementation, I get a "DBContext System.ObjectDisposed " exception when I try to access the db via bar. Any ideas why and how I can around this please? I am really new to threading and tasks so I could be completely going about this wrong!
I use dependency injection to provide the DB context on startup
services.AddEntityFrameworkNpgsql()
.AddDbContext<MyDBContext>()
.BuildServiceProvider();
I then make call to foo() which in turn calls bar() using a new thread (maybe I am doing this wrong?):
public async Task<string> foo(string msg)
{
Thread x = new Thread(async () =>
{
await bar(msg);
});
x.IsBackground = true;
x.Start();
return "OK.";
}
So bar immediately tries to access DBContext to grab some entities and it throws the exception!
Unhandled Exception: System.ObjectDisposedException: Cannot access a disposed object. A common cause of this error is disposing a context that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling Dispose() on the context, or wrapping the context in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances. Object name: 'MyDBContext'.
If I take bar() out of the thread, it is fine but of course "OK" not returned until bar has finished with its very long process which is the problem I need to get around.
Many thanks for some guidance please.
EDIT with running code BUT it is still waiting for the Task.Run to complete before returning "OK." (almost there?)
public async Task<string> SendBigFile(byte[] fileBytes)
{
ServerLogger.Info("SendBigFile called.");
var task = Task.Run(async () =>
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var someProvider = scope.ServiceProvider.GetService<ISomeProvider>();
var fileProvider = scope.ServiceProvider.GetService<IFileProvider>();
await GoOffAndSend(someProvider, fileProvider, fileBytes);
}
});
ServerLogger.Info("Hello World, this should print and not wait for Task.Run."); //Unfortunately this is waiting even though GoOffAndSend takes at least 1 minute.
return "Ok"; //This is not returned until Task.Run finishes unfortunately... how do I "skip" to this immediately?
}
private async Task GoOffAndSend(ISomeProvider s, IFileProvider f, byte[] bytes)
{
// Some really long task that can take up to 1 minute that involves finding the file, doing weird stuff to it and then
using (var client = new HttpClient())
{
var response = await client.PostAsync("http://abcdef/somewhere", someContent);
}
}