You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何配置EF Core单DbContext并管理多数据库提供程序迁移?

问题描述

生产环境中有一个托管于ASP.NET Web API的.NET 6 Blazor WASM项目,采用标准C#分层架构(客户端、API、逻辑、数据及共享项目)。DbContext注入仓储后服务于逻辑层,请求完成后服务与DbContext会自动释放,运行状态稳定。

现需创建配套WinUI 3桌面应用,复用现有逻辑与数据层,但切换为SQLite数据提供程序。尝试继承现有DbContext创建新类后,仓储无法确定应使用的DbContext实例;参考官方「多提供程序单上下文」文档配置后,未生成SQLite对应的ModelSnapshot,执行update-database时仍生成SQL Server语法的SQL代码。


现有架构与配置

项目架构

采用分层架构,核心项目包括:

  • 客户端:Blazor WASM项目
  • API:ASP.NET Web API项目
  • Logic:业务逻辑层项目
  • Data:数据访问层(包含DbContext与仓储实现)
  • Data.Migrations:独立的数据库迁移项目
  • WinUI 3:配套桌面应用项目

Data.Migrations.csproj(仅用于迁移)

  • Migrations:SQL Server提供程序对应的迁移文件目录
  • Migrations/Sqlite:预留的SQLite提供程序迁移文件目录

Data.csproj(DbContext与仓储实现)

public class Context : DbContext
{
    protected readonly IConfiguration Configuration;

    public Context() { }

    public Context(DbContextOptions<Context> options)
        : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // 实体模型配置代码
    }

    // DbSet<T> 定义...
}

public interface IRepository<T> where T : class
{
    // 仓储接口方法定义
}

public class Repository<T> : IRepository<T> where T : class
{
    private DbSet<T> entities;

    public Repository()
    {
    }

    // 仓储接口方法实现
}

public class DbContextFactory : IDesignTimeDbContextFactory<Context>
{
    public Context CreateDbContext(string[] args)
    {
        var optionsBuilder = new DbContextOptionsBuilder<Context>();

        if (DbContextHelper.Provider == "SqlServer")
            optionsBuilder.UseSqlServer(DbContextHelper.ConnectionString,
                    x => x.MigrationsAssembly("Data.Migrations"));
        else if (DbContextHelper.Provider == "Sqlite")
            optionsBuilder.UseSqlite(DbContextHelper.ConnectionString,
                    x => x.MigrationsAssembly("Data.Migrations"));
        else
            throw new InvalidOperationException("未指定数据库提供程序");

        return new Context(optionsBuilder.Options);
    }
}

Logic.csproj(业务逻辑层)

public interface ICustomerService
{
    // 业务服务接口方法定义
}

public class CustomerService : ICustomerService
{
    protected readonly IRepository<Customer> _customerRepository;

    public CustomerService(IRepository<Customer> customerRepository)
    {
        _customerRepository = customerRepository;
    }

    // 业务服务接口方法实现
}

Api.csproj(Program类配置)

builder.Services.AddDbContext<Context>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("Context"),
    x => x.MigrationsAssembly("Data.Migrations"))
);

builder.Services.AddTransient<ICustomerService, CustomerService>();

builder.Services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
builder.Services.AddScoped<DbContext, Context>();

WinUi.csproj(App.xaml.cs配置)

public App()
{
    services.AddDbContext<InSpecContext, Context>(options =>
        options.UseSqlite(DbContextHelper.ConnectionString,
        x => x.MigrationsAssembly("Data.Migrations"))
    );
}

解决方案

1. 生成SQLite专属迁移与ModelSnapshot

通过指定提供程序和输出目录,强制生成SQLite对应的迁移文件与ModelSnapshot:

  • 生成SQLite初始迁移:
Add-Migration InitialCreate_Sqlite -Context Context -OutputDir Migrations/Sqlite -Provider Microsoft.EntityFrameworkCore.Sqlite
  • 执行SQLite数据库更新:
Update-Database -Context Context -Provider Microsoft.EntityFrameworkCore.Sqlite

2. 消除仓储的DbContext注入歧义

无需继承DbContext,通过依赖注入的配置差异实现多提供适配:

  • 修改WinUI项目的DbContext注册代码,直接注册Context而非继承类:
// WinUi.csproj App.xaml.cs
services.AddDbContext<Context>(options =>
    options.UseSqlite(DbContextHelper.ConnectionString,
    x => x.MigrationsAssembly("Data.Migrations"))
);
  • 调整仓储类构造函数,直接注入Context实例,避免依赖DbContext基类:
// Data.csproj 中修改Repository<T>
public class Repository<T> : IRepository<T> where T : class
{
    private readonly DbSet<T> _entities;

    public Repository(Context context)
    {
        _entities = context.Set<T>();
    }

    // 仓储接口方法实现
}

API项目注入SQL Server版本的Context,WinUI项目注入SQLite版本的Context,仓储会自动获取对应实例,无需额外区分。

3. 优化设计时DbContextFactory

修改DbContextFactory,通过命令行参数指定提供程序,避免依赖静态状态:

public class DbContextFactory : IDesignTimeDbContextFactory<Context>
{
    public Context CreateDbContext(string[] args)
    {
        var provider = args.Length > 0 ? args[0] : "SqlServer";
        var configuration = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json")
            .Build();

        var optionsBuilder = new DbContextOptionsBuilder<Context>();
        string connectionString = configuration.GetConnectionString($"{provider}Context");

        if (provider == "SqlServer")
            optionsBuilder.UseSqlServer(connectionString, x => x.MigrationsAssembly("Data.Migrations"));
        else if (provider == "Sqlite")
            optionsBuilder.UseSqlite(connectionString, x => x.MigrationsAssembly("Data.Migrations"));
        else
            throw new InvalidOperationException($"不支持的数据库提供程序:{provider}");

        return new Context(optionsBuilder.Options);
    }
}

生成SQLite迁移时可通过参数指定提供程序:

Add-Migration NewMigration_Sqlite -Context Context -OutputDir Migrations/Sqlite -Provider Microsoft.EntityFrameworkCore.Sqlite -Args "Sqlite"

内容的提问来源于stack exchange,提问作者A. Dixon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 11:42:03