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、遷移成功,查看數據庫

 

 多個數據庫上下文就使得項目支持多數據庫鏈接,使用場景如連接備份數據庫、配置數據庫等

@天才臥龍的博科人

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章