• 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

    337901356
  • 相关阅读:
    Gitblit版本服务器环境部署记录
    LVS+Keepalived 高可用环境部署记录(主主和主从模式)
    LVM常规操作记录梳理 [扩容、缩容、快照等]
    MFS+Keepalived双机高可用热备方案操作记录
    Docker容器时间跟主机时间保持同步的操作记录
    搜狗拼音输入法LINUX版安装
    Android实现透明的颜色效果(zz)
    android.database.CursorIndexOutOfBoundsException:Index -1 requested, with a size of 1(zz)
    View 的setTag() 和 getTag()
    android 布局的两个属性 dither 和 tileMode
  • 原文地址:https://www.cnblogs.com/chenxinblogs/p/15208251.html
Copyright © 2020-2023  润新知