星期日, 10月 30, 2022

[EF] Transaction

閱讀 EF6 官方文章 - 使用交易 的整理筆記,紀錄最常用的兩種方式,分別為
  • SaveChange()、Database.ExecuteSqlCommand()
  • BeginTransaction()

環境建置

在 AdventureWorks2019 內建立 EFTransaction Table 來存放資料,並開啟 SQL Profile 來觀察交易

EFTransacton Table Script
USE [AdventureWorks2019]
GO

CREATE TABLE [dbo].[EFTransaction]
(
	[ID] [int] IDENTITY(1,1) NOT NULL,
	[RecordTime] [datetime] NULL,
	CONSTRAINT [PK_EFTransaction] PRIMARY KEY CLUSTERED 
	(
		[ID] ASC
	)
)
Profile 內開啟交易事件

[EF] Transaction-1


SaveChange()、Database.ExecuteSqlCommand()

SaveChange() 和 Database.ExecuteSqlCommand() 預設都會開啟交易,隔離層級為資料庫預設層級,以 MS SQL 為例,預設隔離層級是 read committed
DbFirstDbContext dbFirstDbContext = new DbFirstDbContext();
dbFirstDbContext.EFTransaction.Add(new EFTransaction() { RecordTime = DateTime.Now });
dbFirstDbContext.SaveChanges();

string TSQL = "DELETE FROM EFTransaction WHERE RecordTime < getdate()";
dbFirstDbContext.Database.ExecuteSqlCommand(TSQL);
從下圖 profile 截圖可以觀察到,SaveChange() 和 Database.ExecuteSqlCommand() 都包在各自的交易內,且從 Audit Login 內可以看到 read committed
 
[EF] Transaction-2

BeginTransaction()

BeginTransaction() 可以把多個資料庫操作包在同一個交易內,以上述的 SaveChange() 和 Database.ExecuteSqlCommand() 來觀察
using (DbFirstDbContext dbFirstDbContext = new DbFirstDbContext())
using (DbContextTransaction tran = dbFirstDbContext.Database.BeginTransaction(System.Data.IsolationLevel.ReadCommitted))
{
    dbFirstDbContext.EFTransaction.Add(new EFTransaction() { RecordTime = DateTime.Now });
    dbFirstDbContext.SaveChanges();

    string TSQL =
        "insert into EFTransaction (RecordTime)" +
        " values(getdate())";
    dbFirstDbContext.Database.ExecuteSqlCommand(TSQL);

    tran.Commit();
}

從下圖 profile 截圖可以觀察到,SaveChange() 和 Database.ExecuteSqlCommand() 的 insert 動作都包在同一個交易內,且從 Audit Login 內可以看到 read committed

[EF] Transaction-3

另外透過 BeginTranaction() 來開啟交易,是可以明確指定交易層級的,該範例雖然有明確指定 read committed,但 read committed 是預設值 

文章內關於 BeginTransaction() 的備註說明
Database.BeginTransaction() will open the connection if it is not already opened. If DbContextTransaction opened the connection then it will close it when Dispose() is called.

沒有留言:

張貼留言