EFCore生產環境數據庫升級方案


這里之所以推薦使用生成SQL的方式來應用遷移,是因為將遷移生成SQL腳本后,更具靈活性,主要有以下幾點好處:

1 、我們可以根據需要來在遷移生成的SQL腳本基礎上進行刪減或者增加腳本

2、可以直接將腳本發給數據庫管理員進行升級。

3、可以檢查遷移生成所生成的SQL腳本的正確性,避免破壞性的升級。

一、環境准備

  1. 安裝efcore cli 命令行工具:打開程序包管理控制台,輸入:dotnet tool install --global dotnet-ef,如果已安裝請跳過此步驟。
  2. 新建一個asp.net core mvc 項目和一個類庫項目,如下圖

二、創建DbContext

首先往類庫項目添加EFCore 相關的Nuget包以及應用遷移的dbup-sqlserver包, Microsoft.EntityFrameworkCore.SqlServer、Microsoft.EntityFrameworkCore.Design、dbup-sqlserver,如下圖所示:

然后定義一個實體模型UserInfo和DbContext

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Text;

namespace EFMigrations.Models
{
    public class UserInfo
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int UserId { get; set; }
        public string UserName { get; set; }
        public string Password { get; set; }
        public string Email { get; set; }
    }
}
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Text;

namespace EFMigrations.Models
{
    public class MyDbContext : DbContext
    {
        /// <summary>
        /// 這里一定要聲明一個接收DbContextOptions參數的構造函數,否則無法正常添加遷移。
        /// </summary>
        /// <param name="options"></param>
        public MyDbContext(DbContextOptions<MyDbContext> options) : base(options)
        {
        }
        public DbSet<UserInfo> UserInfos { get; set; }
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            base.OnConfiguring(optionsBuilder);
        }
    }
}

 

三、配置Web項目

1 添加Microsoft.EntityFrameworkCore.Design  Nuget包的引用,引用后,記得重新生成下項目。

2 打開appsettings.Development.json,配置數據庫連接字符串,如下所示:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "ConnectionStr": "Server=.;Database=TestDb;User Id=sa;Password=xxxx"
  }
}

3 打開Startup.cs 文件,將DbContext注冊到服務容器中。

 

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<MyDbContext>(builder=> {
                builder.UseSqlServer(Configuration["ConnectionStrings:ConnectionStr"]);
            });
            services.AddControllersWithViews();
        }

 

三、添加遷移

先看下遷移命令的使用說明,打開程序包管理控制台,輸入以下命令來查看遷移生成命令的使用方式:

dotnet ef migrations add --help

 

 

-s 選項:表示生成遷移時要啟動的項目,這里是EFMigrations.Web

-p 選項:指定要存放遷移文件的項目根目錄,正常就是DbContext類所在的項目。

-o 選項:可以額外指定遷移文件要存放的目錄,不指定該選項則默認生成到-p 選項所在項目的Migrations文件夾下

打開程序包管理控制台,輸入命令:dotnet ef migrations add InitDatabase -s ./EFMigrations.Web -p ./EFMigrations.Models,來生成首個遷移,這里遷移的名稱可以自定義,不一定要叫InitDatabase.成功后如下圖所示:

// <auto-generated />
using EFMigrations.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;

namespace EFMigrations.Models.Migrations
{
    [DbContext(typeof(MyDbContext))]
    partial class MyDbContextModelSnapshot : ModelSnapshot
    {
        protected override void BuildModel(ModelBuilder modelBuilder)
        {
#pragma warning disable 612, 618
            modelBuilder
                .HasAnnotation("ProductVersion", "3.1.18")
                .HasAnnotation("Relational:MaxIdentifierLength", 128)
                .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

            modelBuilder.Entity("EFMigrations.Models.UserInfo", b =>
                {
                    b.Property<int>("UserId")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("int")
                        .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

                    b.Property<string>("Email")
                        .HasColumnType("nvarchar(max)");

                    b.Property<string>("Password")
                        .HasColumnType("nvarchar(max)");

                    b.Property<string>("UserName")
                        .HasColumnType("nvarchar(max)");

                    b.HasKey("UserId");

                    b.ToTable("UserInfos");
                });
#pragma warning restore 612, 618
        }
    }
}

可以看到MyDbContextModelSnapshot.cs 文件中的內容此時也記錄了InitDatabase 遷移中的更改,即EF每次生成遷移時是將遷移快照文件(這里是:MyDbContextmodelSnapshot.cs)中的更改和現有模型進行比較來決定新的遷移中要包含哪些更改的。

 

四、遷移轉換成SQL腳本

先來看下將ef 遷移轉換成數據庫腳本的命令說明,打開程序包管理控制台,輸入以下命令來查看EF遷移轉SQL腳本命令的用法:

dotnet ef migrations script --help

FROM 參數:表示從哪個遷移開始生成SQL腳本,如果是從第一個遷移開始生成,那么這個參數傳0,否則第一個遷移無法生成SQL腳本。

TO 參數:表示SQL腳本生成截止到哪一個遷移,在這個遷移后面添加的遷移將不會生成SQL腳本。

-s 選項:指定生成SQL腳本時的啟動項目的csproj文件的路徑,可以是一個相對於當前目錄的相對路徑(程序包管理控制台打開時默認當前目錄是在.sln文件所在目錄),啟動項目指的是包含Main函數的可執行的項目,這里就是EFMigrations.Web

-p 選項:指定遷移所在的項目,一般該路徑指定為包含DbContext類的項目的根目錄,和 -s 一樣,也是使用相對於當前目錄的相對路徑來指定,可以使用

dir 命令 來查看當前所在目錄。

-o 選項:用於指定將SQL腳本生成到那個路徑下,可以是一個相對目錄。

 

輸入 cd ./EFMigrations.Models 進入到包含DbContext類的項目根目錄下,這樣我們就可以不用特意指定DbContext 所在的項目路徑。

輸入 dotnet ef migrations script 0 20210826142318_InitDatabase -s ../EFMigrations.Web -o ./SqlScripts/InitDatabase.sql

上面這個命令表示將20210826142318_InitDatabase 這個遷移生成的SQL存放到EFMigrations.Models/SqlScripts 目錄下。

成功后,可以看到如下圖所示:

注意:刪除遷移盡量使用dotnet ef migrations remove 來刪除,盡量不要手動刪除遷移文件,因為如果手動刪除遷移文件,必須手動將遷移快照(MyDbContextModelSnapShot.cs)文件里關於這個遷移的變更一起刪除,否則無法再次生成這個遷移。

 

需要將.sql 腳本設置為內嵌資源,鼠標右擊.sql腳本,選擇屬性->生成操作->嵌入的資源,按照如下設置:

五、首個遷移應用

1、在EFMigrations.Models 項目下添加 ApplicationBuilderExtensions類

using DbUp;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;

namespace EFMigrations.Models
{
    public static class ApplicationBuilderExtensions
    {
        public static void DbMigrate(this IApplicationBuilder builder, IConfiguration configuration,ILogger logger)
        {
            string connectionString = configuration["ConnectionStrings:ConnectionStr"];
            //如果數據庫還不存在,則創建
            EnsureDatabase.For.SqlDatabase(connectionString);

            //這里將會去找當前程序集中所包含的所有.sql 腳本,並且找出未被升級的sql腳本進行升級。
            var upgrader = DeployChanges.To
            .SqlDatabase(connectionString)
            .WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly())
            .LogToConsole()
            .Build();

            var result = upgrader.PerformUpgrade();

            if (!result.Successful)
            {
                logger.LogInformation(result.Error, "數據庫升級失敗");
            }
        }
    }
}

 

2、打開EFMigrations.Web 下的Startup.cs文件,修改Configure方法如下:

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env,ILogger<Startup> logger)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }
            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });

            //這里開始應用數據庫遷移
            app.DbMigrate(Configuration,logger);
        }

成功后,可看到已經創建了數據庫TestDb 和 表 UserInfo

 

這里SchemaVersions 是db-sqlserver 創建的用於記錄那些SQL腳本已被應用的表。

六、變更遷移應用

上面僅僅只是創建了一個遷移,假設我們這個時候又往UserInfo實體新增了幾個字段,那么我們要這個模型的變更同步到正式環境的數據庫改怎么做呢?

1、 生成遷移

 dotnet ef migrations add UserInfo_AddColumns -p ./EFMigrations.Models/EFMigrations.Models.csproj -s ./EFMigrations.Web/

2、遷移轉換成SQL腳本,並嵌入到程序集,然后將程序集編譯發布到正式環境

cd ./EFMigrations.Models

dotnet ef migrations script 20210826142318_InitDatabase 20210830142733_UserInfo_AddColumns -o ./SqlScripts/UserInfo_AddColumns.sql -s ../EFMigrations.Web/ 

這里-o 后面一定要指定sql文件名,如果只指定目錄如:-o ./SqlScripts/ 則會提示路徑找不到。

另外需要注意的是:FROM 參數指定的遷移(20210826142318_InitDatabase )是會被排除在外的,不會生成相應的SQL腳本。

生成SQL腳本后記得將該腳本的生成操作設置為嵌入資源。

3、重啟網站即可。

可以看到,重啟網站后,字段創建成功了。

dbup 官方文檔地址:https://dbup.readthedocs.io/en/latest/#getting-started

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM