Net6/NetCore3.1搭建codeFirst 【支持多dbcontext】并接合TransactionScope 完成事务操作
十年河东,十年河西,莫欺少年穷
学无止境,精益求精
1、打开VS2019或VS2022创建一个webApi项目
添加引用
1.Microsoft.EntityFrameworkCore.SqlServer 2.Microsoft.EntityFrameworkCore.Design
本篇采用VS2019做演示
1.1、配置文件增加数据库链接字符串
{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }, "AllowedHosts": "*", "ConnectionStrings": { "swapDbContext": "Data Source=LAPTOP-84R6S0FB;Initial Catalog=swap;Integrated Security=True;MultipleActiveResultSets=true" } }
1.2、Startup.cs中ConfigureServices引入Sqlserver使用
public void ConfigureServices(IServiceCollection services) { services.AddControllers(); services.AddDbContext<swapDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("swapDbContext")), ServiceLifetime.Scoped); }
2、为解决方案增加一个类库,命名为CoreDbContext
添加引用
1.Microsoft.EntityFrameworkCore.Relational 2.Microsoft.EntityFrameworkCore.Tools 3.Microsoft.EntityFrameworkCore.SqlServer
2.1、新建DbConfigs、DbDtos、文件夹及swapDbContext.cs类
2.2、在DbDtos文件夹创建一个简单的实体
namespace CoreDbContext.DbDtos { public class book { public string uid { get; set; } public string bookName { get; set; } public bool IsDeleted { get; set; } } }
2.3、在DbConfigs文件夹中创建实体配置【指定主键,全局筛选器,字段长度、字段备注等】
internal class bookConfig : IEntityTypeConfiguration<book> { public void Configure(EntityTypeBuilder<book> builder) { builder.ToTable("T_books"); builder.HasKey(A => A.uid); builder.HasQueryFilter(A => A.IsDeleted == false); builder.Property(A => A.bookName).HasMaxLength(50).HasComment("书名"); } }
2.4、创建数据库上下文
namespace CoreDbContext { /// <summary> /// add-Migration -Context 可以通过-Context 指定上下文,因此,项目中可以有多个DbCOntext,这样就支持多个数据库 /// </summary> public class swapDbContext : DbContext { public DbSet<book> books { get; set; } public swapDbContext(DbContextOptions<swapDbContext> options) : base(options) { } protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); //从当前程序集命名空间加载所有的IEntityTypeConfiguration modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly); } } /// <summary> /// 开发环境专用 用于add-Migration 时使用 /// </summary> public class swapDbContextFactory : IDesignTimeDbContextFactory<swapDbContext> { public swapDbContext CreateDbContext(string[] args) { var optionsBuilder = new DbContextOptionsBuilder<swapDbContext>(); optionsBuilder.UseSqlServer("Data Source=LAPTOP-84R6S0FB;Initial Catalog=swap;Integrated Security=True;MultipleActiveResultSets=true"); return new swapDbContext(optionsBuilder.Options); } } }
3、迁移数据库
Add-Migration initDb
Update-DataBase
4、查看生成的数据库及表
5、接合TransactionScope进行简单事务测试
关于TransactonScope可参考:https://www.cnblogs.com/csdbfans/p/TransactionScope.html
5.1、验证方法题内只有一个SaveChanges()时,执行的是事务

[HttpGet] public IActionResult TestBook() { book book = new book() { uid = Guid.NewGuid().ToString(), bookName = "平凡的世界", IsDeleted = false }; dbContext.books.Add(book); string name = ""; for (int i = 0; i < 20; i++) { name += "平凡的世界"; } book book2 = new book() { uid = Guid.NewGuid().ToString(), bookName = name, IsDeleted = false }; dbContext.books.Add(book2); dbContext.SaveChanges(); return Ok(); }
上述方法内只有一个SaveChanges,但书的名称超出了最大值50,是否会引起事务回滚?
调试如下:
数据库插入失败
验证结果:方法内只有一个SaveChanges时,EFCORE执行的是事务操作,因此当方法内只有一个SaveChanges时,就无须再使用TransactionScope了
5.2、验证方法题内有多个SaveChanges()时,执行的不是事务
将上述方法增加一个saveChagnes
调试后,数据库会增加一条记录
验证结果:方法内有多个SaveChanges时,EFCORE执行的是不事务操作,此时就需要接合使用TransactionScope了
5.3、接合TransactionScope来使用

[HttpGet] public IActionResult TestBook() { using (TransactionScope scope = new TransactionScope()) { book book = new book() { uid = Guid.NewGuid().ToString(), bookName = "平凡的世界", IsDeleted = false }; dbContext.books.Add(book); dbContext.SaveChanges(); book book1 = new book() { uid = Guid.NewGuid().ToString(), bookName = "钢铁是怎样炼成的", IsDeleted = false }; dbContext.books.Add(book1); dbContext.SaveChanges(); string name = ""; for (int i = 0; i < 20; i++) { name += "平凡的世界"; } FormattableString sql = $"insert into [T_books] values(newid(),{name},0)"; dbContext.Database.ExecuteSqlInterpolated(sql); scope.Complete(); return Ok(); } }
注意,dbContext.Database.ExecuteSqlInterpolated()中的参数是带有内插值的字符串,字符串中无需包含单引号
调试如下:
数据库如下【之前插入的一条,我已删除】:
验证结果:在多个saveChanges时或者和原生SQL语句接合使用时,使用TransactionScope可以保证执行的是事务
5.4、异步操作的TransactionScope声明【须使用参数:TransactionScopeAsyncFlowOption.Enabled】

[HttpGet] public async Task<IActionResult> TestBook() { using (TransactionScope scope = new TransactionScope( TransactionScopeAsyncFlowOption.Enabled)) { book book = new book() { uid = Guid.NewGuid().ToString(), bookName = "平凡的世界", IsDeleted = false }; dbContext.books.Add(book); await dbContext.SaveChangesAsync(); book book1 = new book() { uid = Guid.NewGuid().ToString(), bookName = "钢铁是怎样炼成的", IsDeleted = false }; dbContext.books.Add(book1); await dbContext.SaveChangesAsync(); scope.Complete(); return Ok(); } }
6、多个DbContext上下文时怎么做数据库迁移
6.1、项目中新增TestDbContext

namespace CoreDbContext { /// <summary> /// add-Migration -Context 可以通过-Context 指定上下文,因此,项目中可以有多个DbCOntext,这样就支持多个数据库 /// </summary> public class TestDbContext : DbContext { public DbSet<book> books { get; set; } public TestDbContext(DbContextOptions<TestDbContext> options) : base(options) { } protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); //从当前程序集命名空间加载所有的IEntityTypeConfiguration modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly); } } /// <summary> /// 开发环境专用 用于add-Migration 时使用 /// </summary> public class TestDbContextFactory : IDesignTimeDbContextFactory<TestDbContext> { public TestDbContext CreateDbContext(string[] args) { var optionsBuilder = new DbContextOptionsBuilder<TestDbContext>(); optionsBuilder.UseSqlServer("Data Source=LAPTOP-84R6S0FB;Initial Catalog=TestDb;Integrated Security=True;MultipleActiveResultSets=true"); return new TestDbContext(optionsBuilder.Options); } } }
6.2、配置文件中新增链接字符串并在Startup.cs中注册TestDbContext
public void ConfigureServices(IServiceCollection services) { services.AddControllers(); services.AddDbContext<swapDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("swapDbContext")), ServiceLifetime.Scoped); services.AddDbContext<TestDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("TestDbContext")), ServiceLifetime.Scoped); }
6.3、数据迁移
数据迁移时报错,要求带上 -Context 参数
因此,当项目中有多个DbContext时,我们在做数据库迁移时需要指定context
Add-Migration addTestDb -context TestDbContext
Update-DataBase -context TestDbContext
6.4、迁移成功,查看数据库
多个数据库上下文就使得项目支持多数据库链接,使用场景如连接备份数据库、配置数据库等
@天才卧龙的博科人
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 周边上新:园子的第一款马克杯温暖上架
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
· 使用C#创建一个MCP客户端