首页 > 其他分享 >EntityFrameworkCore 模型自动更新(上)

EntityFrameworkCore 模型自动更新(上)

时间:2022-09-08 02:13:47浏览次数:85  
标签:模型 EntityFrameworkCore services 自动更新 var 迁移 上下文 数据库

话题

嗯,距离上一次写博文已经过去近整整十个月,还是有一些思考,但还是变得懒惰了,心思也不再那么专注,有点耗费时间,学习也有点停滞不前,那就顺其自然,随心所欲吧,等哪天心血来潮,想写了就写写

模型自动更新方案研究(上)

一般团队人数很少时,使用EF Core内置迁移基本已满足,满足的基本前提首先要生成迁移文件,然后和数据库进行对比,但团队人数一多,迁移文件等等涉及提交冲突等等,所以大部分情况下,我个人认为EF Core迁移基本没啥用,这玩意用不起来,尤其涉及到版本分支很多情况下,切换不同分支所使用的数据可能也会不同,我们常用MySql数据库,同时适配了人大金仓数据库、高斯数据库(GaussDb for opengauss),其对应的表结构有一些差异性,列类型也会有很大差异性,这对开发人员和测试人员来讲就是深深的折磨和痛苦,大部分时间会花在保持数据库表结构和模型一致,否则要么运行不起来,要么测试功能各种有问题,所以想想通过自动化方式来解决这个问题,本文还是分上下两篇写好了。

 

那么解决此问题的思路是怎样的呢?同时适配多套数据库,重写一套?那是不阔能的,既然我们可以通过dotnet ef命令来进行迁移,通过和数据库表结构进行对比,从而生成迁移文件,迁移文件包含即将要执行的差异性脚本,从这个角度来看的话,我们从迁移类反堆即可得到生成的脚本以及和数据库进行对比操作方法,初步设想理论上应该行得通,只需花时间了解下源码就好,通过前期两天的啃源码,最终啃出百把行代码即可自动更新模型到数据库,当然这个过程中还涉及一些要考虑的细节,我们一一再叙。接下来我们以MySql为例讲讲整个过程,其他数据库依葫芦画瓢就好,首先甩出如下代码:

var services = new ServiceCollection();

services.AddEntityFrameworkMySql();

services.AddEntityFrameworkDesignTimeServices();

services.AddDbContext<EfCoreDbContext>((serviceProvider, options) =>
{
    options.UseInternalServiceProvider(serviceProvider);

    options.UseMySql("server=localhost;Port=3306;Database=test;Username=root;Password=root;",ServerVersion.AutoDetect("server=localhost;Port=3306;Database=test;Username=root;Password=root;"));
});

services.AddScoped<IDatabaseModelFactory, MySqlDatabaseModelFactory>();

var serviceProvider = services.BuildServiceProvider();

EF Core有属于它自己的容器,所以我们将全局容器和上下文所属容器做了区分,同时呢,我们将迁移中要用到的操作依赖也手动添加,比如上面的设计服务,存在于 Microsoft.EntityFrameworkCore.Design 包内,最后将获取数据库表结构模型工厂手动注入即MySqlDatabaseModelFactory。接下来我们要获取模型定义以及属性一些定义等等,也就是我们最终要生成的目标模型结构

using var scope = _serviceProvider.CreateScope();

var currentServiceProvider = scope.ServiceProvider;

var context = (DbContext)currentServiceProvider.GetRequiredService<T>();

var connectionString = context.Database.GetDbConnection().ConnectionString;

var targetModel = context.GetService<IDesignTimeModel>().Model.GetRelationalModel();

if (targetModel == null)
{
    return Enumerable.Empty<MigrationOperation>().ToList();
}

接下来则是获取数据库表结构也就是数据库模型

var databaseFactory = currentServiceProvider.GetService<IDatabaseModelFactory>();

var factory = currentServiceProvider.GetService<IScaffoldingModelFactory>();

var tables = context.Model.GetEntityTypes().Select(e => e.GetTableName()).ToList();

if (!tables.Any())
{
    return Enumerable.Empty<MigrationOperation>().ToList();
}

// 仅查询当前上下文模型所映射表,否则比对数据库表差异时,将会删除非当前上下文所有表
var databaseModel = databaseFactory.Create(connectionString, new DatabaseModelFactoryOptions(tables: tables));

if (databaseModel == null)
{
    return Enumerable.Empty<MigrationOperation>().ToList();
}

这里稍微需要注意的是,若是有多个不同上下文,肯定只查询当前上下文所对应的模型结构,不然最后生成的脚本会将其他上下文对应的表结构给删除。紧接着,我们需要将数据库模型转换为上下文中的模型,即类型一致转换,这就演变成了我们的源模型

var model = factory.Create(databaseModel, new ModelReverseEngineerOptions());

if (model == null)
{
    return Enumerable.Empty<MigrationOperation>().ToList();
}

var soureModel = model.GetRelationalModel();

接下来自热而然就进行源模型和目标模型差异性比对,得到实际要进行的迁移操作

 var soureModel = model.GetRelationalModel();

//TODO Compare sourceModel vs targetModel

var modelDiffer = context.GetService<IMigrationsModelDiffer>();

var migrationOperations = modelDiffer.GetDifferences(soureModel, targetModel);

那接下来问题来了,拿到差异性迁移操作后,我们应该怎么处理呢?留着各位思考下,我们下篇见

总结

本文给出了自动更新模型思路以及整个完整实现逻辑,剩余内容我们下篇再叙,主要是没心情写,写不下去了,今天我们就到此为止~

标签:模型,EntityFrameworkCore,services,自动更新,var,迁移,上下文,数据库
From: https://www.cnblogs.com/CreateMyself/p/16667716.html

相关文章

  • 数据库原理:数据模型和关系数据库
    目录数据模型数据模型的分类数据模型的组成要素常用的数据模型层次模型网状结构关系模型关系模型概念笛卡尔积码的概念关系的概念关系模式关系操作关系完整性实体完整性参......
  • django框架之模型层-Ajax
    目录Ajax基本操作数据编码格式Ajax携带文件数据Ajax回调函数参数问题sweetalter介绍django自带的序列化组件图书管理系统Ajax基本操作1.Ajax:js自带的功能(学习的是jQuery......
  • 计算机的理论模型——图灵机
    1.图灵机的由来图灵机由英国数学家阿兰·麦席森·图灵(AlanMathisonTuring)于1936年提出的一种抽象的计算模型,即一切可计算问题都可以由一个虚拟的机器代替人类进行计算......
  • 【论文笔记】LayoutLMv2:将视觉信息加入到预训练阶段的跨模态文档预训练模型
    概述LayoutLMv2是对LayoutLM的改进,主要有以下几点区别:将视觉信息加入到了预训练阶段,而不是LayouLM中的微调阶段删除了MDC,添加了text-imagealignment和text-imgaematc......
  • Simulink集成模型测试太慢怎么办?
    Tips:现阶段模型开发大部分采用Simulink,为了验证模型实现了相关功能,需要对模型进行测试。模型测试(MiL)有单元测试和集成测试之分。单元测试中模型复杂度低,信号参数数量少,测......
  • A100 买不到了,只有小显卡怎么训大模型
    为了达到更好的训练效果,通常炼丹师们会使用更大的模型和更大的Batchsize,但因此带来的大显存占用,却成为不可避免的硬伤。尤其是如今GPU越来越贵,甚至还可能买不到............
  • 【论文笔记】LayoutLM:首次结合文本和版式信息的文档预训练模型
    概述LayoutLM是一个基于Bert,结合了文本和版式信息的文档预训练模型,在多个下游任务中都达到了当时SOTA的结果。模型模型的总体结构如图1所示:图1LayoutLM总体结构La......
  • 一篇文章教你如何用界面组件DevExpress WPF创建一个WPF视图模型
    DevExpressWPF拥有120+个控件和库,将帮助您交付满足甚至超出企业需求的高性能业务应用程序。通过DevExpressWPF能创建有着强大互动功能的XAML基础应用程序,这些应用程序专......
  • django框架-模型层
    正反向进阶操作#正向查询1.查询电话号码问1234的学校名称#res=models.School.objects.filter(schoolinfo_fo__phone='1234').values('name')#print(res)......
  • 04-Nginx进程模型解析
    Nginx进程模型解析master进程:主进程worker进程:工作进程默认是一个主进程,一个工作进程,Nginx的工作进程是可以通过配置文件进行修改的#工作进程数量worke......