✅ Disposing dbContext with dependency injection
Hi!
I am developing an Avalonia UI app. I am displaying there some data, which is stored in the sqlite database. The data - rows in that matter - can be deleted or added, but once they are there - they are not modified (at least for now ;)).
I have a factory which gets data from db and then creates a view model passing there the data (and few other things). The factory itself I've registered in service container as a singleton and dbContext as a transient. dbContext is injected into the factory. As I am not tracking entities changes and not adding or deleting data in that place, I've configured the context with
optionsBuilder.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
.
I have not encountered any issues so far, however I am concerned about proper dbContext disposing. If a factory is singleton, even when dbContext is transient, once it's injected is still there, right? The view model can be created few times during app lifetime, rows can be added/removed in the meantime.
Can I leave it as it is or should I do smth about it? Eg. instead of injecting dbContext itself I can create and inject another factory which will instantiate dbContext via constructor each time needed, or maybe you can suggest other solutions?
Any help appreciated 😉16 Replies
a transient injected into a singleton will indeed be "captured" and hoisted up to a singleton lifecycle
dbcontexts are intended to be short-lived, on the scale of a single method call or a http request
so I would create and dispose your client on a short enough timeframe to get the data you need each time you need it
for that, you can either create your dbcontexts manually with new(), or use the dedicated dbcontextfactory (though that may be asp.net core only, I forget)
IDbContextFactory Interface (Microsoft.EntityFrameworkCore)
Defines a factory for creating DbContext instances.
It's not. I use that approach in an Avalonia app. eg.
that seems totally fine to me
though I think you can use "await using" since it's an async method
And for the DI configuration side (though you don't need NodaTime):
I'm pretty sure
await using
wasn't a feature when this was made.fair enough 🙂
Oh, maybe not.
await using
is a lot older than I thought. C# 8.i rarely remember to use it when my ide isn't configured to lint about it
since it's rarely intuitive what classes have implemented iasyncdisposable over plain idisposable
in my experience
@becquerel @Klarth thank you for your input! I'll use the factory then.
Btw. by
await using
you mean invoking CreateDbContextAsync?
@Klarth in you example, you've used .ConfigureAsync(false) because there was no need to keep that in UI thread, or did you have any other reasons?yeah, dbcontexts implement iasyncdisposable
so you can await their using statement
SaveChangesAsync
at the end.@Klarth why would you omitt at the end? Is it redundant at that point? I don't have much experience in async/await programming that's why I ask 😉
Nevermind on that.
When you
await
the call on the GUI side (without .ConfigureAwait(false)
), the continuation will run on the UI thread.it's much outside the topic but wouldn't one ConfigureAwait(false) in a row be enough to separate from UI thread?
So say
AddCompletions
is a library method (it effectively is), then you want .ConfigureAwait(false)
on each call so it can resume on any available thread.
Granted, ASP.NET Core doesn't have a SynchronizationContext
, so that happens no matter what. Well, unless you create your own SynchronizationContext
there for some reason.
It's still best practice to .ConfigureAwait(false)
each call in a library.Thank you Klarth. I'll close the post now, not to confuse any further readers, maybe we meet again in new threads ;D