• 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();
            }
    View Code

    上述方法内只有一个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();
                }
                
            }
    View Code

    注意,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();
                }
                
            }
    View Code

    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);
            }
        }
    }
    View Code

    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、迁移成功,查看数据库

     多个数据库上下文就使得项目支持多数据库链接,使用场景如连接备份数据库、配置数据库等

    @天才卧龙的博科人

  • 相关阅读:
    new Date()的数据类型的问题
    GraphicsMagick / ImageMagick缺少lib报错no decode delegate for this image format
    python安装numpy和matplotlib
    蚂蚁金服CTO程立:金融级分布式交易的技术路径
    《Python基础教程读书笔记》
    《storm实战-构建大数据实时计算读书笔记》
    Ubuntu 16.04 几个国内更新源
    vim
    我的MySQL整理
    MySql unique的实现原理简析
  • 原文地址:https://www.cnblogs.com/chenwolong/p/TransactionScope.html
Copyright © 2020-2023  润新知