【ABP】项目示例(8)——数据迁移

news/2025/4/3 2:16:27/文章来源:https://www.cnblogs.com/rainseason/p/18677823

数据迁移

在上一章节中,已经展示了数据播种的用途之一,即单元测试中进行数据初始化,在这一章节中,实现数据播种的另一重要用途,即数据迁移
该项目使用的是代码优先的开发模式,需要将领域模型迁移到数据库中的数据模型

EF数据迁移

在程序包管理控制台选中General.Backend.EntityFrameworkCore,执行以下命令安装EF数据迁移相关的Nuget包

Install-Package Microsoft.EntityFrameworkCore.Design -v 8.0.4
Install-Package Microsoft.EntityFrameworkCore.Tools -v 8.0.4

数据迁移需要一个单独的启动项目来进行运行,创建名称为General.Backend.DbMigrator的控制台项目
General.Backend.DbMigrator添加项目引用General.Backend.EntityFrameworkCore
新建名称为appsettings.json的默认加载配置文件,配置MySql数据库连接信息,并将文件属性设置为始终复制到输出目录

{"ConnectionStrings": {"Default": "server=localhost;database=general;user=youruser;password=yourpassword;port=yourport;CharSet=utf8mb4;Allow User Variables=True;SslMode=none;AllowLoadLocalInfile=true;"}
}

在名称为General.Backend.EntityFrameworkCore的类库中,新建名称为GeneralDbContextFactory的数据迁移配置类,指定数据迁移的数据库连接信息和迁移类库

public class GeneralDbContextFactory : IDesignTimeDbContextFactory<GeneralDbContext>
{public GeneralDbContext CreateDbContext(string[] args){var configuration = BuildConfiguration();var builder = new DbContextOptionsBuilder<GeneralDbContext>().UseMySql(connectionString: configuration.GetConnectionString("Default"),serverVersion: MySqlServerVersion.LatestSupportedServerVersion,mySqlOptionsAction: optionsBuilder => optionsBuilder.MigrationsAssembly(Assembly.GetExecutingAssembly().FullName));return new GeneralDbContext(builder.Options);}private static IConfigurationRoot BuildConfiguration(){var builder = new ConfigurationBuilder().SetBasePath(Path.Combine(Directory.GetCurrentDirectory(), "../General.Backend.DbMigrator/")).AddJsonFile("appsettings.json", optional: false);return builder.Build();}
}

接下来进行数据迁移,选中General.Backend.DbMigrator作为启动项目,在程序包管理控制台选中General.Backend.EntityFrameworkCore,执行以下命令

Add-Migration Init
Update-Database

从图中可以看出已经成功进行了数据迁移,将领域模型映射到数据库中的表结构,生成对应的表
但是系统运行所依赖的初始数据并没有生成,这时就需要在数据迁移的过程中进行数据播种

ABP数据迁移

在名称为General.Backend.Domain的类库中的DataSeed文件夹下,新建名称为IGeneralDbSchemaMigrator的接口

public interface IGeneralDbSchemaMigrator
{public Task MigrateAsync();
}

在名称为General.Backend.EntityFrameworkCore的类库中,新建名称为GeneralDbSchemaMigrator的迁移类,实现数据库迁移

public class GeneralDbSchemaMigrator : IGeneralDbSchemaMigrator, ITransientDependency
{private readonly IServiceProvider _serviceProvider;public GeneralDbSchemaMigrator(IServiceProvider serviceProvider){_serviceProvider = serviceProvider;}public async Task MigrateAsync(){await _serviceProvider.GetRequiredService<GeneralDbContext>().Database.MigrateAsync();}
}

在名称为General.Backend.Domain的类库中的DataSeed文件夹下,新建名称为GeneralDbMigrationService的迁移服务类,所有实现了IGeneralDbSchemaMigrator接口的迁移类都将进行数据库迁移,然后进行数据播种

public class GeneralDbMigrationService : ITransientDependency
{private readonly IDataSeeder _dataSeeder;private readonly IEnumerable<IGeneralDbSchemaMigrator> _generalDbSchemaMigrators;public GeneralDbMigrationService(IDataSeeder dataSeeder,IEnumerable<IGeneralDbSchemaMigrator> generalDbSchemaMigrators){_dataSeeder = dataSeeder;_generalDbSchemaMigrators = generalDbSchemaMigrators;}public async Task MigrateAsync(){var initialMigrationAdded = AddInitialMigrationIfNotExist();if (initialMigrationAdded){return;}foreach (var migrator in _generalDbSchemaMigrators){await migrator.MigrateAsync();}await _dataSeeder.SeedAsync();}private static bool AddInitialMigrationIfNotExist(){try{if (!DbMigrationsProjectExists()){return false;}}catch (Exception){return false;}try{if (!MigrationsFolderExists()){AddInitialMigration();return true;}else{return false;}}catch (Exception){return false;}}private static bool DbMigrationsProjectExists(){var dbMigrationsProjectFolder = GetEntityFrameworkCoreProjectFolderPath();return dbMigrationsProjectFolder != null;}private static bool MigrationsFolderExists(){var dbMigrationsProjectFolder = GetEntityFrameworkCoreProjectFolderPath();return dbMigrationsProjectFolder != null && Directory.Exists(Path.Combine(dbMigrationsProjectFolder, "Migrations"));}private static void AddInitialMigration(){string argumentPrefix;string fileName;if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) || RuntimeInformation.IsOSPlatform(OSPlatform.Linux)){argumentPrefix = "-c";fileName = "/bin/bash";}else{argumentPrefix = "/C";fileName = "cmd.exe";}var procStartInfo = new ProcessStartInfo(fileName,$"{argumentPrefix} \"abp create-migration-and-run-migrator \"{GetEntityFrameworkCoreProjectFolderPath()}\"\"");try{Process.Start(procStartInfo);}catch (Exception){throw new Exception("无法运行ABP命令行");}}private static string? GetEntityFrameworkCoreProjectFolderPath(){var slnDirectoryPath = GetSolutionDirectoryPath();return slnDirectoryPath == null? throw new Exception("未找到解决方案文件夹"): Directory.GetDirectories(slnDirectoryPath).FirstOrDefault(d => d.EndsWith(".EntityFrameworkCore"));}private static string? GetSolutionDirectoryPath(){var currentDirectory = new DirectoryInfo(AppContext.BaseDirectory);while (currentDirectory != null && Directory.GetParent(currentDirectory.FullName) != null){currentDirectory = Directory.GetParent(currentDirectory.FullName);if (currentDirectory != null && Directory.GetFiles(currentDirectory.FullName).FirstOrDefault(f => f.EndsWith(".sln")) != null){return currentDirectory.FullName;}}return null;}
}

在程序包管理控制台选中General.Backend.DbMigrator,执行以下命令安装ABP依赖注入和托管主机相关的Nuget包

Install-Package Volo.Abp.Autofac -v 8.3.0
Install-Package Microsoft.Extensions.Hosting -v 8.0.0

在名称为General.Backend.DbMigrator的类库中,新建名称为GeneralDbMigratorModule 的迁移模块类

[DependsOn(typeof(AbpAutofacModule),typeof(GeneralDomainModule),typeof(GeneralEntityFrameworkCoreModule))]
public class GeneralDbMigratorModule : AbpModule
{}

接下来新建名称为DbMigratorHostedService的托管服务类,迁移程序启动时,调用迁移服务类中的MigrateAsync方法,进行数据迁移和数据播种

public class DbMigratorHostedService : IHostedService
{private readonly IHostApplicationLifetime _hostApplicationLifetime;private readonly IConfiguration _configuration;public DbMigratorHostedService(IHostApplicationLifetime hostApplicationLifetime,IConfiguration configuration){_hostApplicationLifetime = hostApplicationLifetime;_configuration = configuration;}public async Task StartAsync(CancellationToken cancellationToken){using (var application = await AbpApplicationFactory.CreateAsync<GeneralDbMigratorModule>(options =>{options.Services.ReplaceConfiguration(_configuration);options.UseAutofac();})){await application.InitializeAsync();await application.ServiceProvider.GetRequiredService<GeneralDbMigrationService>().MigrateAsync();await application.ShutdownAsync();_hostApplicationLifetime.StopApplication();}}public Task StopAsync(CancellationToken cancellationToken){return Task.CompletedTask;}
}

修改General.Backend.DbMigrator项目中的启动类Program,添加托管服务类到主机服务中

internal class Program
{static async Task Main(string[] args){await CreateHostBuilder(args).RunConsoleAsync();}public static IHostBuilder CreateHostBuilder(string[] args){var hostBuilder = Host.CreateDefaultBuilder(args).ConfigureServices((hostContext, services) =>{services.AddHostedService<DbMigratorHostedService>();});return hostBuilder;}
}

最后验证数据迁移和数据播种功能,将启动项目设置为General.Backend.DbMigrator,点击运行
输出如下信息,表明数据迁移成功
查询数据库的user和role表,可以看到已经存在了一个管理员用户和管理员角色,表明数据播种成功

解决方案的目录结构现如下

在下一章节中,实现表现层

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/909720.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一个测试工程师的实战笔记:我是如何在Postman和Apipost之间做出选择的?

作为一家金融科技公司的测试负责人,我每天要处理数十个需要加密验签的接口。从最开始的Postman,到后来的Apipost,让我重新思考:我们需要的究竟是一个代码编辑器,还是一个真正懂测试者的智能工具? 一、当加密需求被Postman的脚本支配 1、密码字段MD5加密 去年接手支付系统…

【ABP】项目示例(7)——数据播种

数据种子 在上一章节中,已经对仓储层和应用层进行了单元测试,在这一章节中,进行数据播种 大多数程序正常运行都需要依赖于初始数据,依赖于数据库的程序基本都是如此 例如需要有一个初始的管理员用户或者一个管理员角色,用来进行登录系统,像这种主要用于生产环境中的数据播…

kettle从入门到精通 第九十四课 ETL之kettle MySQL Bulk Loader大批量高性能数据写入

1、在使用kettle时如果对表输出性能要求,可以考虑用mysql 批量加载步骤,该步骤可以实现每秒5万+的数据同步(该数据仅是基于我本人的笔记本,若是服务器则效率更高),如下图所示: 2、原理 知其然知其所以然,之所以MySQL Bulk Loader速度如此之快是因为MySQL 批量加载器使用…

Web开发SpringBoot流程性的学习----回顾补充2(YApi)

Vue简述 Vue 是一套前端框架,免除原生JavaScript中的DOM操作,简化书写。 基于MVVM(Model-View-ViewModel)思想,实现数据的双向绑定,将编程的关注点放在数据上。 Vue2官网:https://v2.cn.vuejs.org/生命周期(Vue3已不同)AJAX 概念: Asynchronous JavaScript And XML,异步的…

4.1日报

今天完善了那个多条件查询 虽然没有做成知网那么高级的 但是查询功能已经很完善// 1. 政策标题精确查询(可根据需求改为like模糊查询)if (StringUtils.hasText(policyTitle)) {queryWrapper.like(Policy::getName, policyTitle);}// 2. 政策内容全文检索if (StringUtils.hasT…

MCP (Model Context Protocol)初体验:企业数据与大模型融合初探

简介 模型上下文协议(Model Context Protocol,简称MCP)是一种创新的开放标准协议,旨在解决大语言模型(LLM)与外部数据和工具之间的连接问题。它为AI应用提供了一种统一、标准化的方式来访问和处理实时数据,使模型不再局限于训练时获得的静态知识。 MCP由Anthropic首次提…

Redis高级篇-多级缓存

Redis高级篇-多级缓存Redis高级篇-多级缓存 1.什么是多级缓存 传统的缓存策略一般是请求到达Tomcat后,先查询Redis,如果未命中则查询数据库,如图:存在下面的问题: •请求要经过Tomcat处理,Tomcat的性能成为整个系统的瓶颈 •Redis缓存失效时,会对数据库产生冲击 多级缓存…

Redis安装说明

Redis安装说明Redis安装说明 大多数企业都是基于Linux服务器来部署项目,而且Redis官方也没有提供Windows版本的安装包。因此课程中我们会基于Linux系统来安装Redis. 此处选择的Linux版本为CentOS 7. Redis的官方网站地址:https://redis.io/ 1.单机安装Redis 1.1.安装Redis依赖…

Redis入门篇

Redis入门篇基础篇Redis 开篇导读 理想课程 小伙伴们理想的课程一定是能够通过讲解的方式,得到如下这些启发,我们的课程会从基础到精通,从redis小白,到redis大牛,还在等什么,这套课程一定就是你最适合你的课程~1.Redis简单介绍 Redis是一种键值型的NoSql数据库,这里有两…

安装Canal

安装Canal安装和配置Canal 下面我们就开启mysql的主从同步机制,让Canal来模拟salve 1.开启MySQL主从 Canal是基于MySQL的主从同步功能,因此必须先开启MySQL的主从功能才可以。 这里以之前用Docker运行的mysql为例: 1.1.开启binlog 打开mysql容器挂载的日志文件,我的在/tmp/…

团队项目

项目 内容这个作业属于哪个课程 软件工程这个作业要求在哪里 作业要求这个作业的目标 学习团队项目协作开发过程🚀团队展示 一、团队组成 队名:404 Not Found 成员组成:姓名 学号周戈 3123004164张荣辉 3123004162杨超民 3123004161饶博勋 3123004152李永胜 3123004148陈曦…