11

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?

6
  • I'm not sure there's a real problem here. The ViewModel is the Unit of Work abstraction for desktop apps. And so if you don't want a transient DbContext, scope it to the ViewModel. Commented Jun 6, 2020 at 15:53
  • @DavidBrowne-Microsoft I have two problem here to use DbContext directly. First I don't think it's good idea to couple ViewModel in UI layer to DbContext in Model layer directly it makes problems if someday I want change my EF to for example Dapper. Second forms like Main Form is long-running and I don't think it's good idea to keep some DbContexts of forms ViewModels alive until termination of application. As I know it's advised to keep DbContext lifetime as short as possible. Commented Jun 6, 2020 at 16:09
  • ViewModel is not in the UI layer. That's the View. And for long-lived ViewModels, like the one supporting the main form, don't have database access, or use only transient DBContext instances. And whether you use the DbContext directly or wrap it in an additional repository layer is an unrelated concern, with its own set of tradeoffs. Commented Jun 6, 2020 at 16:11
  • 1
    Yes. Kind of like controllers. Typically longer-lived as a typical ViewModel handles Load,Display,Edit,Save end-to-end. But in terms of DbContext usage, similar. Commented Jun 6, 2020 at 16:59
  • 2
    DBContext should be instantiated, used and disposed rather than retained. Either that is directly in (say) a command of the viewmodel or in a method of a wrapper class which itself should be instantiated, used and disposed. Make viewmodels self tracking ( for changes ) and copy data from the vm to dto/EF models and vice versa. Commented Jun 8, 2020 at 17:16

2 Answers 2

8
+50

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();
        }
    }
}
Sign up to request clarification or add additional context in comments.

Comments

4

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
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.