簡體   English   中英

C# NetCore 事務 null

[英]C# NetCore Transaction null

我有許多類都與特定表相關聯。 在更高級別上,我有調用各種方法的父事務,因此子方法必須能夠在瞬態事務中工作,並且通常事先不知道某個階段的方法是否會成為事務的一部分,因為我們嘗試保持通用。

然后在某些情況下,我們需要 Dapper 查詢,例如打開/關閉身份。 我知道 Dapper 需要傳遞一個事務作為參數,否則它將不會被納入事務(結果我錯了,見下文)。

DbContext(Pooling) 是按“組件/dll”設置的,因此由於連接僅在其在事務中打開時才被登記,因此 scope 用於上下文以確保為該事務打開它。 此外,當從例如 HealthChecks 調用這些相同的方法時會有所幫助,否則當它們中的許多調用由服務打開的相同連接時,它們會抱怨打開的連接太多。 在方法中使用此 scope 也有助於在並行工作中調用這些方法,以便它們更好地在並行線程中運行。

換句話說,以這種方式可以從這些父級調用這些方法,這些父級可以是並行作業父級或需要服務 scope 或需要瞬態層次結構的父事務的單例。

問題是:由於某種原因,以下事務中的事務始終是 null。

try {
using TransactionScope scope = new TransactionScope(TransactionScopeOption.Required,
            System.TimeSpan.FromMinutes(10), TransactionScopeAsyncFlowOption.Enabled);

using Context localcontext = new Context(new DbContextOptionsBuilder<Context>()
                .UseSqlServer(_options.ConnectionString).Options);
// just for safety:
localcontext.Database.GetDbConnection().Open(); 

 // the following line is only for dapper input:
IDbContextTransaction transaction = localcontext.Database.CurrentTransaction;

await localcontext.Database.GetDbConnection()
  .ExecuteAsync("SET IDENTITY_INSERT [dbo].[Whatever] ON",
                null, (System.Data.IDbTransaction)transaction);
}

(我從這里獲取: 將當前事務傳遞給 DbCommand和這里https://github.com/zzzprojects/Dapper.Transaction

更新/解決方案:

好的。 所以...當使用事務 scope 時,不必將事務參數傳遞給 Dapper 以確保它參與事務。 這就是線索。

通過顯示代碼應該是什么來更容易解釋什么是錯誤的:

using(var connection=new SqlConnection(_connectionString))
{
    await connection.ExecuteAsync("SET IDENTITY_INSERT [dbo].[Whatever] ON");
}

ExecuteAsync來自 Dapper。

沒有理由創建事務,更不用說事務 scope 來執行單個命令。

沒有理由僅僅為了打開與數據庫的連接或執行原始 SQL 命令而創建 DbContext。 DbContext 不是數據庫連接,它的工作是將 Map 對象轉換為關系數據。 這里不涉及任何對象。

要執行多個命令,沒有理由使用多個連接。 只需一個接一個地執行命令。 如果確實有必要,請圍繞這些命令使用顯式數據庫事務。 或者在單個事務 scope 中創建連接。

假設您有一個包含這些命令的數組,例如從腳本文件中讀取的內容:

string[] commands=new[]{...};
using(var connection=new SqlConnection(_connectionString))
{
    await connection.OpenAsync();

    using (var transaction = connection.BeginTransaction())
    {
        foreach(var sql in commands)
        {
            await connection.ExecuteAsync(sql,transaction:transaction);
        }
        transaction.Commit();
    }
}

使用 TransactionScope 做同樣的事情只需要打開事務 scope 內的連接。

string[] commands=new[]{...};

using( var scope = new TransactionScope(TransactionScopeOption.Required,
            System.TimeSpan.FromMinutes(10), TransactionScopeAsyncFlowOption.Enabled)
using(var connection=new SqlConnection(_connectionString))
{
    await connection.OpenAsync();

    foreach(var sql in commands)
    {
        await connection.ExecuteAsync(sql);
    }
    scope.Complete();
}

刪除此行

IDbContextTransaction transaction = localcontext.Database.CurrentTransaction;

如果有一個活動的 TrasnactionScope,您的 SqlConnection 將自動加入其中。 TransactionScope 的全部意義在於您的數據訪問方法可以完全沒有事務處理。 然后在一些外層業務層或者controller方法中,對事務進行編排。

CurrentTransaction是 null 的原因是有兩種不同的方式來處理事務。 如果您想要當前的 System.Transactions.Transaction,您可以使用System.Transactions.Transaction.Current獲得它。

退一步說,使用 SqlConnection 管理事務有 3 種不同的方法。

  1. TSQL 事務:您可以使用 TSQL API 直接發出BEGIN TRANCOMMIT TRAN等。

  2. ADO.NET Transactions : SqlConnection.BeginTrasaction, IDbTransaction, SqlTransaction, etc. This is a wrapper over the TSQL API, and is a PITA because it introduces a useless requirement to pass the SqlTransaction to each SqlCommand that you want to enlist in the Transaction. 但是在當前事務中加入 TSQL 命令不是可選的,而且從來都不是。 這很痛苦,因為用戶 SqlCommand 的方法可能不知道是否存在事務。 Dapper 和 EF 都在他們的事務處理方法中包裝了這個 API。

  3. System.Transactions Transactions :部分原因是在 .NET 2.0 中引入了System.Transactions作為一種新的統一方式來處理 .NET 中的事務,並且 SqlClient 增加了對它的支持。 System.Transactions 的主要創新是添加“環境”事務。 所以代碼可能不知道是否有交易,正確的事情就會發生。 如果當前存在 Transaction,則打開 SqlConnection 時,SqlConnection 將被列入其中,並且在提交 Transaction 之前,不會提交使用 SqlConnection 所做的更改。 您的 ADO.NET 代碼無需了解交易。 Dapper 和 EF 都建立在 ADO.NET 和 SqlClient 之上,所以這一切都可以正常工作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM