dotnet ef 用于开发/生产的单独数据库提供程序

Posted

技术标签:

【中文标题】dotnet ef 用于开发/生产的单独数据库提供程序【英文标题】:dotnet ef separate database provider for dev/prod 【发布时间】:2021-12-01 07:14:43 【问题描述】:

尝试在本地开发中使用sqlite,在生产中使用sqlserver。 如果我根据环境切换了提供程序,仍然存在仅为其中一个提供程序生成迁移的问题(sqlite 迁移在 sqlserver 上不起作用,反之亦然)。

在开发中使用单独的数据库而不是在产品中使用的可行方法是什么?

【问题讨论】:

不推荐,那你是在生产中测试 这也适用于拥有本地 vs 远程开发数据库,​​其中本地是 sqlite,远程开发是 sqlserver,所以不一定要在 prod 中测试 【参考方案1】:

诀窍是为 dev 创建一个单独的 dbcontext,让它继承主 dbcontext 并只更改连接字符串。 从那里开始,为主要 + 开发 dbcontexts 生成迁移,然后根据环境选择要使用的上下文。

代码设置

主 dbcontext 将使用依赖注入来获取包含您的连接字符串的配置。

using app.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;

namespace app.Data

    public class AppDbContext : DbContext
    
        protected readonly IConfiguration Configuration;

        public AppDbContext(IConfiguration config)
        
            Configuration = config;
        

        protected override void OnConfiguring(DbContextOptionsBuilder options)
        
            options.UseSqlServer(Configuration["SQLConnectionString"]);
        

        // whatever your app does
        public DbSet<Dog> Dogs  get; set; 
    

dev dbcontext 将只继承主要的,使用不同的配置块来指定 sqlite。

using app.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;

namespace app.Data

    public class DevAppDbContext : AppDbContext
    
        public DevAppDbContext(IConfiguration config) : base(config)
        
        

        protected override void OnConfiguring(DbContextOptionsBuilder options)
        
            options.UseSqlite(Configuration.GetConnectionString("app"));
        
    

我们将添加开发数据库连接字符串到我们的appsettings.Development.json

...
  "ConnectionStrings": 
    "app": "Data Source=app.db"
  

在 startup.cs 中,我们将通过依赖注入添加 IWebHostEnvironment 以获取环境。如果在 dev 中,那么我们将指定额外的泛型参数,将 dev dbcontext 指定为实现。

从 vscode 的文档中,方法签名是: public static IServiceCollection AddDbContext&lt;TContextService, TContextImplementation&gt;


        public Startup(IConfiguration configuration, IWebHostEnvironment env)
        
            _config = configuration;
            _env = env;
        

        private readonly IConfiguration _config;
        private readonly IWebHostEnvironment _env;

        public void ConfigureServices(IServiceCollection services)
        
            if (_env.IsDevelopment())
                services.AddDbContext<AppDbContext, DevAppDbContext>();
            else
                services.AddDbContext<AppDbContext>();
            services.AddDatabaseDeveloperPageExceptionFilter();

添加迁移

现在添加迁移需要切换环境并指定我们为其创建迁移的 dbcontext。这可以抽象为一个相当简单的 powershell 脚本:

[CmdletBinding()]
param(
    [string]$Name
);

$prev = $Env:ASPNETCORE_ENVIRONMENT;
Write-Host "Previous env=""$prev""";
try 
    Write-Host "Adding migration for production";
    $Env:ASPNETCORE_ENVIRONMENT = "Production";
    dotnet ef migrations add $Name `
        --context "AppDbContext" `
        --output-dir "Migrations/SqlServerMigrations";
        
    Write-Host "Adding migration for development";
    $Env:ASPNETCORE_ENVIRONMENT = "Development";
    dotnet ef migrations add $Name `
        --context "DevAppDbContext" `
        --output-dir "Migrations/SqliteMigrations";

finally 
    Write-Host "Restoring env=""$prev""";
    $Env:ASPNETCORE_ENVIRONMENT = $prev;

示例用法:

> .\scripts\add-migration.ps1 -Name init
Previous env=""
Adding migration for production
Build started...
Build succeeded.
Done. To undo this action, use 'ef migrations remove'
Adding migration for development
Build started...
Build succeeded.
Done. To undo this action, use 'ef migrations remove'
Restoring env=""

应用迁移

与创建迁移一样,我们需要根据环境切换上下文。不过这一次,我们只会更新用户在调用脚本时指定的单个环境。

[CmdletBinding()]
param(
    [string]
    [ValidateSet("Development", "Production")]
    $Environment
)

$context = @
    "Development" = "DevAppDbContext" 
    "Production"  = "AppDbContext"
.$Environment;

$prev=$Env:ASPNETCORE_ENVIRONMENT;
try 
    $Env:ASPNETCORE_ENVIRONMENT=$Environment;
    dotnet ef database update --context $context;

finally 
    Write-Host "Restoring env=""$prev""";
    $Env:ASPNETCORE_ENVIRONMENT = $prev;

使用示例:

> .\scripts\apply-migrations.ps1 -Environment Development
Setting environment to Development
Build started...
Build succeeded.
No migrations were applied. The database is already up to date.
Done.
Restoring env=""

结论

这应该足以开始使用 sqlite 进行本地开发,使用 sqlserver 进行实时开发。这种方法的一些限制是 sqlite 不支持 sqlserver 的所有功能,因此您必须同时设置一个 dev sqlserver db,或者需要决定避免使用这些功能。

进一步阅读

https://jasonwatmore.com/post/2020/01/03/aspnet-core-ef-core-migrations-for-multiple-databases-sqlite-and-sql-server https://docs.microsoft.com/en-us/aspnet/core/fundamentals/environments?view=aspnetcore-5.0 https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/providers?tabs=dotnet-core-cli https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/projects?tabs=dotnet-core-cli(这里没有使用这种方法)

【讨论】:

这是一个很好的答案。另一件需要提及的事情(在这种情况下)是 create and drop APIs,它是 EF Core 工具的一部分。如果我有多个提供程序并且一个仅特定于开发,我个人将其用于开发提供程序来代替迁移。我知道它的用例是有限的,但 OP 的特定场景可能会觉得很有用。如果您有一个快速变化的模型,它会让您的开发体验更加顺畅。通常在开发阶段的早期。只是一个想法!【参考方案2】:

我习惯在 web.config 文件中放置两个具有相同名称的连接字符串,并且总是必须对其中一个进行注释。 当您必须进行测试时,请注释 Prod 的连接字符串。 发布解决方案时注释 Dev 的连接字符串。

【讨论】:

但是调用 "UseSqlite" 对 sqlserver 连接字符串不起作用,那么仍然存在提供程序之间无法使用迁移的问题 同意。这肯定不是答案。

以上是关于dotnet ef 用于开发/生产的单独数据库提供程序的主要内容,如果未能解决你的问题,请参考以下文章

dotnet ef 迁移 name dotnet ef 数据库更新

如何调试 dotnet ef 数据库删除?

EF Core:一统SQL和NoSQL数据库

zsh:找不到命令:dotnet-ef

dotnet tool install --global dotnet-ef 失败

运行 `dotnet ef dbcontext scaffold` 时省略一列