如何在桌面应用程序中使用 DbContext 和 DI?

Posted

技术标签:

【中文标题】如何在桌面应用程序中使用 DbContext 和 DI?【英文标题】:How to use DbContext with DI in desktop applications? 【发布时间】:2020-09-25 17:33:32 【问题描述】:

我一直在使用 Entity Framework 开发几个非 Web 应用程序,并且一直在努力寻找使用 DbContext 实现 Generic Repository 的正确方法。

我搜索了很多,很多文章都是关于具有短暂上下文的 Web 应用程序。在桌面方法中我找不到合适的方法。

一种方法是DbContext per ViewModel,但我不同意将 View 与 Repository 层耦合。

另一个是这样使用using子句的:

using(var context = new AppDbContext())

    // ...

但是这样我们就没有Unit of Work,也不能使用IoC Containers

那么在桌面应用程序中使用 DbContext 的最佳做法是什么?

【问题讨论】:

我不确定这里是否存在真正的问题。 ViewModel 桌面应用程序的工作单元抽象。因此,如果您不想要瞬态 DbContext,请将其范围限定为 ViewModel。 @DavidBrowne-Microsoft 我这里有两个直接使用 DbContext 的问题。首先,我认为将 UI 层中的 ViewModel 直接耦合到模型层中的 DbContext 并不是一个好主意,如果有一天我想将我的 EF 更改为例如 Dapper 会出现问题。像Main Form 这样的第二种形式是长期运行的,我认为在应用程序终止之前保持表单 ViewModel 的一些 DbContexts 处于活动状态并不是一个好主意。据我所知,建议尽可能缩短 DbContext 的生命周期。 ViewModel 不在 UI 层中。这就是视图。对于长期存在的 ViewModel,比如支持主窗体的 ViewModel,没有数据库访问权限,或者只使用临时 DBContext 实例。无论您是直接使用 DbContext 还是将其包装在额外的存储库层中,都是一个不相关的问题,它有自己的一组权衡。 是的。有点像控制器。通常寿命更长,因为典型的 ViewModel 处理端到端的加载、显示、编辑、保存。但就 DbContext 的使用而言,类似。 DBContext 应该被实例化、使用和处置而不是保留。要么直接在(比如说)视图模型的命令中,要么在本身应该被实例化、使用和处置的包装类的方法中。使视图模型自我跟踪(用于更改)并将数据从 vm 复制到 dto/EF 模型,反之亦然。 【参考方案1】:

DbContext 是短暂的:它本身就代表了一个工作单元。如果需要长期的对象状态管理,可以直接在Entity Framework中使用ObjectStateManager

为了确保访问DbContext,添加一个接口IDbContextFactory<TDbContext>(如果您只有一个DbContext 类型,则添加一个接口IMyDbContextFactory)并将其注入您的ViewModels 并使用一个短暂的DbContext来自它:

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();
        
    

【讨论】:

【参考方案2】:

Entity Framework Core 有一个内置的 IDbContextFactory 接口。

例如,如果使用 SQL Server,则在 ConfigureServices 方法中声明以下内容(在 WPF 中通常放在 App.xaml.cs 中)。

private static void ConfigureServices(IServiceCollection services)

    services.AddDbContextFactory<MyDbContext>(
        options =>
            options.UseSqlServer(MyConnectionString));

确保 MyDbContext 公开此构造函数:

public class MyDbContext : DbContext

    public MyDbContext(DbContextOptions<MyDbContext> options)
        : base(options)
    
    

之后,在将使用上下文的类中使用构造函数注入(可以在 ViewModel 层或 Model 层中,具体取决于您的架构):

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
    

【讨论】:

以上是关于如何在桌面应用程序中使用 DbContext 和 DI?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 T4 模板中使用 DbContext?

如何在控制器中使用多个 DBContext

如何使用 caliburn micro 在 Wpf 中注入 EF DbContext

dotnet 核心中的 Dbcontext 对象瞬态

如何使用autofac实现多个DbContext的注入

两个(几乎)并发 DbContext 导致问题:如何在 Controller 和 AuthorizeAttribute 之间共享