Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use DbContext with DI in desktop applications?

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?

like image 563
Arman Ebrahimpour Avatar asked Jun 06 '20 14:06

Arman Ebrahimpour


2 Answers

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();
        }
    }
}
like image 105
Dai Avatar answered Sep 28 '22 07:09

Dai


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
    }
}
like image 34
GrantA Avatar answered Sep 28 '22 07:09

GrantA