C
C#•9h ago
Tom

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 😉
8 Replies
becquerel
becquerel•6h ago
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)
Klarth
Klarth•6h ago
It's not. I use that approach in an Avalonia app. eg.
private readonly IDbContextFactory<SomeContext> _dbFactory;

public CompletionService(IDbContextFactory<SomeContext> dbFactory)
{
_dbFactory = dbFactory;
}

public async Task AddCompletion(string familyName, string objectiveName, Instant completionTime)
{
using var context = _dbFactory.CreateDbContext();

var family = await GetOrCreateFamily(context, familyName).ConfigureAwait(false);
var objective = await GetOrCreateObjective(context, objectiveName, false, family).ConfigureAwait(false);
objective.LastCompletion = completionTime;

var completion = new Completion()
{
Family = family,
Objective = objective,
CompletionTime = completionTime
};

context.Completions.Add(completion);
await context.SaveChangesAsync().ConfigureAwait(false);
}
private readonly IDbContextFactory<SomeContext> _dbFactory;

public CompletionService(IDbContextFactory<SomeContext> dbFactory)
{
_dbFactory = dbFactory;
}

public async Task AddCompletion(string familyName, string objectiveName, Instant completionTime)
{
using var context = _dbFactory.CreateDbContext();

var family = await GetOrCreateFamily(context, familyName).ConfigureAwait(false);
var objective = await GetOrCreateObjective(context, objectiveName, false, family).ConfigureAwait(false);
objective.LastCompletion = completionTime;

var completion = new Completion()
{
Family = family,
Objective = objective,
CompletionTime = completionTime
};

context.Completions.Add(completion);
await context.SaveChangesAsync().ConfigureAwait(false);
}
becquerel
becquerel•6h ago
that seems totally fine to me though I think you can use "await using" since it's an async method
Klarth
Klarth•6h ago
And for the DI configuration side (though you don't need NodaTime):
public void ConfigureDbContext(IServiceCollection services)
{
services.AddDbContextFactory<SomeContext>(
options => options.UseSqlite(_connectionString, x => x.UseNodaTime())
);
}
public void ConfigureDbContext(IServiceCollection services)
{
services.AddDbContextFactory<SomeContext>(
options => options.UseSqlite(_connectionString, x => x.UseNodaTime())
);
}
I'm pretty sure await using wasn't a feature when this was made.
becquerel
becquerel•6h ago
fair enough 🙂
Klarth
Klarth•6h ago
Oh, maybe not. await using is a lot older than I thought. C# 8.
becquerel
becquerel•6h ago
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
Want results from more Discord servers?
Add your server