Как использовать TransactionScope в .NET Core

#c# #asp.net-core #asynchronous

Вопрос:

В моем контроллере API у меня есть такой код:

 [HttpPost]
public async Task<ActionResult<TransactionDto>> CreateNewTrasaction(TransactionDto accountCreateDto)
{
    var commandModel = _mapper.Map<TransactionHistory>(accountCreateDto);
    _transactionRepository.CreateTransaction(commandModel);

    var accountReadDto = _mapper.Map<AccountReadDto>(commandModel);

    return Ok();
}
 

И внутри transactionRepository класса я сделал следующее:

 private readonly TransactionContext _transContext;

public TransactionRp(TransactionContext tContext)
{
    _transContext = tContext;
}

public async Task CreateTransaction(TransactionHistory transaction)
{
    using(IDbContextTransaction transactionContext = _transContext.Database.BeginTransaction())
    {
        try
        {
            transaction.CreatedDate = DateTime.UtcNow;
            _transContext.TransactionHistories.Add(transaction);
            await _transContext.SaveChangesAsync();

            var senderAccount = _transContext.Accounts.FirstOrDefault(x => x.AccountNo == transaction.AccountNo);
            var receiverAccount = _transContext.Accounts.FirstOrDefault(x => x.AccountNo == transaction.ReceiverAccount);

            senderAccount.Amount = senderAccount.Amount - transaction.Amount;
            receiverAccount.Amount = senderAccount.Amount   transaction.Amount;

            _transContext.Accounts.Update(receiverAccount);
            _transContext.Accounts.Update(receiverAccount);
        }
        catch (Exception ex)
        {
            throw;
        }
    }
}
 

Но когда я запускаю код, я получаю ошибку в этой строке

 await _transContext.SaveChangesAsync();
 

И ошибка в том, что:

Не удается получить доступ к удаленному экземпляру контекста.
Распространенной причиной этой ошибки является удаление экземпляра контекста, который был устранен в результате внедрения зависимостей, а затем последующая попытка использовать тот же экземпляр контекста в другом месте приложения.
Это может произойти, если вы вызываете «Dispose» для экземпляра контекста или заключаете его в инструкцию using. Если вы используете внедрение зависимостей, вы должны позволить контейнеру внедрения зависимостей позаботиться об удалении экземпляров контекста.

Имя объекта: ‘Транзакционный контекст’

Обновленный

Вот как я меняю контекст внутри ConfigureServices

 public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<TransactionContext>(opt => opt.UseSqlServer
        (Configuration.GetConnectionString("BankConnection"))
    );
} 
 

Комментарии:

1. Вы не показываете, как управляется _transContext. Это ДИ?

2. @DavidBrowne-Обновленный код Microsoft, я новичок в .net core. Я думаю, что это то, о чем ты спрашивал

3. Почему вы хотите использовать TransactionScope в первую очередь? Если вы используете EF Core или аналогичную ORM, изменения кэшируются и сохраняются в базе данных только при SaveChanges вызове в самом конце запроса. Вам не нужны явные транзакции, сам DbContext предоставляет вам семантику транзакций. Если вы позвоните SaveChanges несколько раз … не звоните. По крайней мере, не без очень серьезной причины

4. И если вы хотите распределенные транзакции, они не поддерживаются . Чистое ядро. И, кстати, вы не ждете выполнения задачи, а это значит, что нет никакой гарантии, когда операция будет фактически обработана. (вероятно, это происходит после завершения первоначального вызова, поэтому вы получаете ошибку удаления.)

Ответ №1:

Похоже, проблема в контроллере. используйте await с async помощью метода. async ключевое слово используется для того, чтобы сделать функцию асинхронной. await Ключевое слово попросит выполнение подождать, пока не будет выполнена определенная задача. Подробнее

 [HttpPost]
public async Task<ActionResult<TransactionCreateDto>> CreateNewTrasaction(TransactionCreateDto accountCreateDto)
{
    var commandModel = _mapper.Map<TransactionHistory>(accountCreateDto);
    await _trasactionRepository.CreateTransaction(commandModel);
    var accountReadDto = _mapper.Map<AccountReadDto>(commandModel);
    return Ok();
}