• 使用 Entity Framework Core 时,通过代码自动 Migration


    使用 Entity Framework Core 时,通过代码自动 Migration

    一 介绍

      在使用 Entity Framework Core (下面就叫 EF Core 吧)进行开发时,如果模型有变动,我们要在用 EF Core 提供的命令行工具进行手工迁移,然后再运行程序。但是为了效率,我想能不能在程序的入口处进行 Migration 呢?从个人经验来说应该是可以,因为 EF Tool 虽然提供了 CLI 但是它最终也是被程序解析这些命令。下面就开始分析,如何通过代码进行 Migration 。

    二 分析

      首先我们要先了解,在使用 EF Core 的 CLI 时,要执行两个步骤:

        第一步:生成 Migration 文件;

        第二步:更新变更项到数据库;

      既然是先生成 Migration 文件再更新,那么在 EF Core 里面一定有对应的模块做这件事情。下面我们看一下 EF Core 项目的结构。从中我们确实找到关于 Migration 的模块。在 Migrations/Design 目录的类名称上我们可以看出来,它就是生成 Migration 文件的。这里先到这儿。

      

      找到了生成 Migration 文件的入口,我们再来找一下如何通过代码将这些变更更新到数据库中。

      在使用 EF Core 的时候,我们都要通过继承 DbContext 来编写自己的 DbContext 子类。在 DbContext 类中我们找到了一个 Database 属性。如下图所示:

      

      然后查看了 DatabaseFacde 这个类,并没有发现执行迁移相关的函数。通过代码搜索,我在 RelationalDatabaseFacadeExtensions 这个类中有一个 Migration() 扩展方法。通过注释的解析,我也确定了它就是执行 Migration 文件,并将变更更新到数据库。

      这两个步骤对应的代码我们都找到了,下面我们就编写一段儿代码,完成自动将模型变更更新到数据库的功能。

     1 public class AutoMigration
     2 {
     3     private readonly IServiceProvider _serviceProvider;
     4     private InformationDbContext _context;
     5 
     6     public AutoMigration(IServiceProvider serviceProvider)
     7     {
     8         _serviceProvider = serviceProvider;
     9         _context = serviceProvider.GetService<InformationDbContext>();
    10     }
    11 
    12     public void Migrator()
    13     {
    14         var path = Path.Combine(AppContext.BaseDirectory, "..\..\..\Migrations\");
    15         if (!Directory.Exists(path))
    16         {
    17             Directory.CreateDirectory(path);
    18         }
    19         else
    20         {
    21             Directory.GetFiles(path).ToList().ForEach(File.Delete);
    22         }
    23 
    24         using (_context)
    25         {
    26             var services = ((IInfrastructure<IServiceProvider>) _context).Instance;
    27             var codeHelper = new CSharpHelper();
    28             var scaffolder = ActivatorUtilities.CreateInstance<MigrationsScaffolder>(services,
    29                 new CSharpMigrationsGenerator(codeHelper, new CSharpMigrationOperationGenerator(codeHelper),
    30                     new CSharpSnapshotGenerator(codeHelper)));
    31             
    32             var projectDir = Path.Combine(path, "..\");
    33             var migrationAssembly = new MigrationsAssembly(new CurrentDbContext(_context), _context.Options, new MigrationsIdGenerator());
    34             scaffolder.GetType().GetField("_migrationsAssembly", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(scaffolder, migrationAssembly);
    35 
    36             var readonlyDic = new ReadOnlyDictionary<string,TypeInfo>(new Dictionary<string, TypeInfo>());
    37             migrationAssembly.GetType().GetField("_migrations", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(migrationAssembly, new LazyRef<IReadOnlyDictionary<string, TypeInfo>>(readonlyDic));
    38             var migration = scaffolder.ScaffoldMigration("Information.Migrations", "Information");
    39 
    40             scaffolder.Save(projectDir, migration, path);
    41 
    42             //另外一种保存方式
    43             //File.WriteAllText($"Migrations\{migration.MigrationId}{migration.FileExtension}", migration.MigrationCode);
    44             //File.WriteAllText("Migrations\" +
    45             //    migration.MigrationId + ".Designer" + migration.FileExtension,
    46             //    migration.MetadataCode);
    47             //File.WriteAllText("Migrations\" + migration.SnapshotName + migration.FileExtension,
    48             //    migration.SnapshotCode);
    49         }
    50 
    51         using(_context = (InformationDbContext)_serviceProvider.GetService<IDbContext>())
    52         {
    53             _context.Database.Migrate();
    54         }
    55     }
    56 }

      另外一个注意点:我们需要指定一下迁移文件所在项目。

    1 services.AddDbContext<InformationDbContext>(opt =>
    2 {
    3     var connectionString = configuration["ConnectionStrings:DefaultConnection"];
    4     opt.UseSqlServer(connectionString, optionBuilder =>
    5     {
    6         optionBuilder.MigrationsAssembly("Information");
    7     });
    8 });

    三 总结

      通过上面的分析可以知道,其实我们就是把 CLI 的两个命令通过代码实现了一下。在 Startup 文件中进行调用即可。

    原文链接:https://www.cnblogs.com/jRoger/p/entity-framework-core-auto-migration.html

  • 相关阅读:
    37 web自动化实战三 前置后置条件 (fixture yield知识点 conftest.py )
    36 web自动化实战二 pytest用例筛选 断言 生成测试报告 数据驱动
    35 web自动化 pytest框架详述
    性能测试jmeter 监控技术
    性能测试jmeter-接口实战2 函数助手 (随机生成手机号,压测手机号等数据库校验不能重复的接口)
    性能测试jmeter-接口实战1 项目中的关联
    性能测试值jmeter 的基本使用(关联 )
    34 selenium JS操作 文件上传 项目分析
    D. Road to Post Office 解析(思維)
    C. Bank Hacking 解析(思維)
  • 原文地址:https://www.cnblogs.com/1175429393wljblog/p/10845565.html
Copyright © 2020-2023  润新知