I've been working on several non-web applications with Entity Framework
and always it was struggling for me to find a correct approach for implementing Generic Repository
with DbContext.
I've searched a lot, many of articles are about web applications which have short-living contexts. In desktop approaches I can't find suitable method.
One approach is DbContext per ViewModel
but I don't agree with coupling View with Repository layers.
Another one is using using
clause this way:
using(var context = new AppDbContext())
{
// ...
}
but this way we will not have Unit of Work
and also can't use IoC Containers
.
So what is the best practice for using DbContext in desktop applications?
A DbContext
is meant to be short-lived: it represents a unit-of-work in itself. If you need long-term object state management then you can use the ObjectStateManager
in Entity Framework directly.
For ensuring access to a DbContext
, add an interface IDbContextFactory<TDbContext>
(or just IMyDbContextFactory
if you only have a single DbContext
type) and inject that into your ViewModels and use a short-lived DbContext
from it:
interface IDbContextFactory<TDbContext>
where TDbContext : DbContext
{
TDbContext Create();
}
// Configure:
void ConfigureServices( YourContainer container )
{
container.RegisterSingleton( IDbContextFactory<YourDbContextType1>, // etc );
container.RegisterSingleton( IDbContextFactory<YourDbContextType2>, // etc );
container.RegisterSingleton( IDbContextFactory<YourDbContextType3>, // etc );
}
// Usage:
public class LongLivedViewModel
{
private readonly IDbContextFactory<YourDbContextType3> dbFactory;
public LongLivedViewModel( IDbContextFactory<YourDbContextType3> dbFactory)
{
this.dbFactory = dbFactory ?? throw new ArgumentNullException(nameof(dbFactory));
this.DoSomethingCommand = new RelayCommand( this.DoSomethingAsync )
}
public RelayCommand DoSomethingCommand { get; }
public async RelayCommand DoSomethingAsync()
{
using( YourDbContextType3 db = this.dbFactory.Create() )
{
// do stuff
await db.SaveChangesAsync();
}
}
}
Entity Framework Core has a built in IDbContextFactory interface.
If using SQL Server, for instance, you declare the following in the ConfigureServices method (which in WPF is generally put in App.xaml.cs).
private static void ConfigureServices(IServiceCollection services)
{
services.AddDbContextFactory<MyDbContext>(
options =>
options.UseSqlServer(MyConnectionString));
}
Make sure MyDbContext exposes this constructor:
public class MyDbContext : DbContext
{
public MyDbContext(DbContextOptions<MyDbContext> options)
: base(options)
{
}
}
After that use constructor injection in the class that will be using the context (which could be either in the ViewModel layer or the Model layer, depending on your architecture):
private readonly IDbContextFactory<MyDbContext> _contextFactory;
public ModelClass(IDbContextFactory<MyDbContext> contextFactory)
{
this._contextFactory = contextFactory;
}
public void DatabaseOperationMethod()
{
using (var context = this._contextFactory.CreateDbContext())
{
// Do database stuff
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With