Подтвердить что ты не робот

Правильный способ использования BeginTransaction с Dapper.IDbConnection

Каков правильный способ использования BeginTransaction() с IDbConnection в Dapper?

Я создал метод, в котором я должен использовать BeginTransaction(). Вот код.

using (IDbConnection cn = DBConnection)
{
    var oTransaction = cn.BeginTransaction();

    try
    {
        // SAVE BASIC CONSULT DETAIL
        var oPara = new DynamicParameters();
        oPara.Add("@PatientID", iPatientID, dbType: DbType.Int32);
        ..........blah......blah............
    }
    catch (Exception ex)
    {
        oTransaction.Rollback();
        return new SaveResponse { Success = false, ResponseString = ex.Message };
    }
}

Когда я выполнил выше метод - я получил исключение -

Недействительная операция. Соединение закрыто.

Это связано с тем, что вы не можете начать транзакцию до открытия соединения. Поэтому, когда я добавляю эту строку: cn.Open();, ошибка будет решена. Но я где-то читал, что ручное открытие соединения - это плохая практика! Dapper открывает соединение только тогда, когда ему нужно.

В структуре Entity вы можете обрабатывать транзакцию с помощью TransactionScope.

Итак, мой вопрос - хорошая практика для обработки транзакции без добавления строки cn.Open()... в Dapper? Я предполагаю, что для этого должен быть правильный путь.

4b9b3361

Ответ 1

Вручное открытие соединения - это не "плохая практика"; dapper работает с открытыми или закрытыми соединениями в качестве удобства, не более того. Общим доходом являются люди, у которых есть связи, которые остаются открытыми, неиспользуемыми, слишком долго, не выпуская их в пул - однако это не проблема в большинстве случаев, и вы, безусловно, можете сделать:

using(var cn = CreateConnection()) {
    cn.Open();
    using(var tran = cn.BeginTransaction()) {
        try {
            // multiple operations involving cn and tran here

            tran.Commit();
        } catch {
            tran.Rollback();
            throw;
        }
    }
}

Обратите внимание, что dapper имеет необязательный параметр для передачи транзакции, например:

cn.Execute(sql, args, transaction: tran);

На самом деле у меня возникает соблазн сделать методы расширения на IDbTransaction, которые работают аналогично, поскольку транзакция всегда предоставляет .Connection; это позволит:

tran.Execute(sql, args);

Но этого сегодня не существует.

TransactionScope - еще один вариант, но имеет другую семантику: это может быть связано с LTM или DTC, в зависимости от... ну, удачи, в основном. Также возникает соблазн создать обертку вокруг IDbTransaction, которая не нуждается в try/catch - больше похожа на то, как работает TransactionScope; что-то вроде (этого также не существует):

using(var cn = CreateConnection())
using(var tran = cn.SimpleTransaction())
{
    tran.Execute(...);
    tran.Execute(...);

    tran.Complete();
}

Ответ 2

Вы не должны звонить

cn.Close();

потому что блок использования попытается закрыть тоже. Для части транзакции да, вы также можете использовать TransactionScope, так как это не связанная с Entity Framework технология. Взгляните на этот ответ SO: fooobar.com/questions/146477/... В нем объясняется, как привлечь ваше соединение в области транзакций. Важным аспектом является: соединение автоматически заносится в транзакцию IIF, вы открываете соединение внутри области.

Ответ 3

Посмотрите решение Тима Шрайбера, которое просто, но мощно и реализовано с использованием шаблона репозитория и имеет Dapper Transactions. p >

В приведенном ниже коде Commit() показано это.

public class UnitOfWork : IUnitOfWork
{
    private IDbConnection _connection;
    private IDbTransaction _transaction;
    private IBreedRepository _breedRepository;
    private ICatRepository _catRepository;
    private bool _disposed;

    public UnitOfWork(string connectionString)
    {
        _connection = new SqlConnection(connectionString);
        _connection.Open();
        _transaction = _connection.BeginTransaction();
    }

    public IBreedRepository BreedRepository
    {
        get { return _breedRepository ?? (_breedRepository = new BreedRepository(_transaction)); }
    }

    public ICatRepository CatRepository
    {
        get { return _catRepository ?? (_catRepository = new CatRepository(_transaction)); }
    }

    public void Commit()
    {
        try
        {
            _transaction.Commit();
        }
        catch
        {
            _transaction.Rollback();
            throw;
        }
        finally
        {
            _transaction.Dispose();
            _transaction = _connection.BeginTransaction();
            resetRepositories();
        }
    }

    private void resetRepositories()
    {
        _breedRepository = null;
        _catRepository = null;
    }

    public void Dispose()
    {
        dispose(true);
        GC.SuppressFinalize(this);
    }

    private void dispose(bool disposing)
    {
        if (!_disposed)
        {
            if(disposing)
            {
                if (_transaction != null)
                {
                    _transaction.Dispose();
                    _transaction = null;
                }
                if(_connection != null)
                {
                    _connection.Dispose();
                    _connection = null;
                }
            }
            _disposed = true;
        }
    }

    ~UnitOfWork()
    {
        dispose(false);
    }
}

Ответ 4

Мы реализовали этот шаблон Uow, но у нас есть проблемы с асинхронными вызовами. Иногда в _transaction.Dispose() мы получаем Соединение не поддерживает MultipleActiveResultSets.