iT邦幫忙

0

EF Core Nested Transaction

  • 分享至 

  • xImage
  •  

紀錄一下目前解法( EF Core 3.1 適用,其他版本尚未試過 )

  1. 實作 IDbContextTransactionManager:Begin/Commit 時計算層數,最外層才真的 Commit
   
public class NestedTransactionManager : IDbContextTransactionManager
{
    readonly ISqlServerConnection _sqlServerConnection;

    public NestedTransactionManager(ISqlServerConnection sqlServerConnection)
    {
        // Dependency inject ISqlServerConnection, ISqlServerConnection is original IDbContextTransactionManager in EF Core 3.1 .
        _sqlServerConnection = sqlServerConnection;
    }

    int Layer = 0;

    public IDbContextTransaction CurrentTransaction
        => _sqlServerConnection.CurrentTransaction;

    public IDbContextTransaction BeginTransaction()
    {
        if (Layer++ == 0)
            _sqlServerConnection.BeginTransaction();
        return new NestedTransation(this, Layer);
    }

    public async Task<IDbContextTransaction> BeginTransactionAsync(CancellationToken cancellationToken = default)
    {
        if (Layer++ == 0)
            await _sqlServerConnection.BeginTransactionAsync(cancellationToken);
        return new NestedTransation(this, Layer);
    }

    public void CommitTransaction()
    {
        if (Layer-- <= 1)
            _sqlServerConnection.CommitTransaction();
    }

    public Task CommitTransactionAsync(CancellationToken cancellationToken = default)
        => Layer-- <= 1
        ? _sqlServerConnection.CurrentTransaction.CommitAsync(cancellationToken)
        : Task.CompletedTask;
        
    public void ResetState()
        => _sqlServerConnection.ResetState();

    public Task ResetStateAsync(CancellationToken cancellationToken = default)
        => _sqlServerConnection.ResetStateAsync(cancellationToken);

    public void RollbackTransaction()
        => _sqlServerConnection.RollbackTransaction();
}
class NestedTransation : IDbContextTransaction
{
    readonly NestedTransactionManager _manager;

    readonly int _layer;

    public NestedTransation(NestedTransactionManager manager, int layer)
    {
        _manager = manager;
        _layer = layer;
    }

    bool Commited => _layer > _manager.Layer;

    public Guid TransactionId
        => Transaction.TransactionId;

    IDbContextTransaction Transaction
        => _manager.CurrentTransaction;

    public void Commit()
        => _manager.CommitTransaction();

    public Task CommitAsync(CancellationToken cancellationToken = default)
        => _manager.CommitTransactionAsync(cancellationToken);

    public void Dispose()
    {
        if (!Commited && Transaction != null)
            Transaction.Dispose();
    }

    public ValueTask DisposeAsync()
        => !Commited && Transaction != null
        ? Transaction.DisposeAsync()
        : default;

    public void Rollback()
        => Transaction.Rollback();

    public Task RollbackAsync(CancellationToken cancellationToken = default)
        => Transaction.RollbackAsync(cancellationToken);
}	
  1. Startup 取代 Service
services.AddDbContext<AppDbContext>(options =>
{
    ...
    options.ReplaceService<IDbContextTransactionManager, NestedTransactionManager>();
});

圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
archer
iT邦新手 2 級 ‧ 2023-04-24 21:00:46

請問這個有使用範例嗎?因為對這方面很感興趣。

manu56 iT邦新手 5 級 ‧ 2023-04-25 11:40:08 檢舉

用法跟原本的 transaction 一樣,這裡只是抽換了 IDbContextTransactionManager,所以可以重複呼叫 begin/commit transaction。

archer iT邦新手 2 級 ‧ 2023-04-27 23:59:43 檢舉

謝謝您,我了解了

我要留言

立即登入留言