Закрытие соединения при использовании Dapper
Необходимо ли закрывать соединение, когда запрос выполняется явным образом, вызывая метод Close или помещая соединение в оператор Using? Приведет ли оставление открытого соединения к повторному использованию соединения и повысит производительность SQL для будущих запросов?
1 ответ
Я предполагаю, что вы используете последнюю версию Dapper.
В Dapper есть два способа управления соединением:
Полностью управляйте собой: здесь вы несете полную ответственность за открытие и закрытие соединения. Это так же, как вы относитесь к соединению при работе с ADO.NET.
Разрешить Dapper управлять им: Dapper автоматически открывает соединение (если оно не было открыто) и закрывает его (если оно было открыто Dapper) для вас. Это похоже на
DataAdapter.Fill()
метод.Я лично не рекомендую этот путь.Это не может быть применимо каждый раз. Вот что говорит Марк Гравелл в одном из комментариев к этому ответу: /questions/14807979/pochemu-dapper-dot-net-ne-otkryivaet-i-ne-zakryivaet-samo-soedinenie/14807990#14807990
ну, технически открытый / закрытый отличается от утилизируемого. Если вы собираетесь открывать / закрывать только отдельные вызовы, вы можете позволить dapper сделать это. Если вы открываете / закрываете с более высокой степенью детализации (например, для каждого запроса), для вашего кода было бы лучше сделать это и передать открытое соединение в dapper.
Конечно, вы можете вызывать несколько запросов по одному соединению. Но соединение должно быть закрыто Close()
, Dispose()
метод или приложив его в using
блок), чтобы избежать утечки ресурса. Закрытие соединения возвращает его в пул соединений. Вовлечение пула соединений повышает производительность по сравнению со стоимостью нового соединения.
В дополнение к простой обработке соединения, я предлагаю вам внедрить UnitOfWork для управления транзакциями. Пошлите этот превосходный образец на GitHub.
Следующий исходный код может помочь вам. Обратите внимание, что это написано для моих нужд; так что это может не сработать для вас как есть.
public sealed class DalSession : IDisposable
{
public DalSession()
{
_connection = new OleDbConnection(DalCommon.ConnectionString);
_connection.Open();
_unitOfWork = new UnitOfWork(_connection);
}
IDbConnection _connection = null;
UnitOfWork _unitOfWork = null;
public UnitOfWork UnitOfWork
{
get { return _unitOfWork; }
}
public void Dispose()
{
_unitOfWork.Dispose();
_connection.Dispose();
}
}
public sealed class UnitOfWork : IUnitOfWork
{
internal UnitOfWork(IDbConnection connection)
{
_id = Guid.NewGuid();
_connection = connection;
}
IDbConnection _connection = null;
IDbTransaction _transaction = null;
Guid _id = Guid.Empty;
IDbConnection IUnitOfWork.Connection
{
get { return _connection; }
}
IDbTransaction IUnitOfWork.Transaction
{
get { return _transaction; }
}
Guid IUnitOfWork.Id
{
get { return _id; }
}
public void Begin()
{
_transaction = _connection.BeginTransaction();
}
public void Commit()
{
_transaction.Commit();
Dispose();
}
public void Rollback()
{
_transaction.Rollback();
Dispose();
}
public void Dispose()
{
if(_transaction != null)
_transaction.Dispose();
_transaction = null;
}
}
interface IUnitOfWork : IDisposable
{
Guid Id { get; }
IDbConnection Connection { get; }
IDbTransaction Transaction { get; }
void Begin();
void Commit();
void Rollback();
}
Теперь ваши репозитории должны каким-то образом принимать этот UnitOfWork. Я выбираю Инъекцию зависимостей с помощью Конструктора.
public sealed class MyRepository
{
public MyRepository(IUnitOfWork unitOfWork)
{
this.unitOfWork = unitOfWork;
}
IUnitOfWork unitOfWork = null;
//You also need to handle other parameters like 'sql', 'param' ect. This is out of scope of this answer.
public MyPoco Get()
{
return unitOfWork.Connection.Query(sql, param, unitOfWork.Transaction, .......);
}
public void Insert(MyPoco poco)
{
return unitOfWork.Connection.Execute(sql, param, unitOfWork.Transaction, .........);
}
}
И тогда вы называете это так:
С транзакцией:
using(DalSession dalSession = new DalSession())
{
UnitOfWork unitOfWork = dalSession.UnitOfWork;
unitOfWork.Begin();
try
{
//Your database code here
MyRepository myRepository = new MyRepository(unitOfWork);
myRepository.Insert(myPoco);
//You may create other repositories in similar way in same scope of UoW.
unitOfWork.Commit();
}
catch
{
unitOfWork.Rollback();
throw;
}
}
Без транзакции:
using(DalSession dalSession = new DalSession())
{
//Your database code here
MyRepository myRepository = new MyRepository(dalSession.UnitOfWork);//UoW have no effect here as Begin() is not called.
myRepository.Insert(myPoco);
}
Таким образом, вместо прямого отображения соединения в вашем коде вызова, вы управляете им в одном месте.
Более подробную информацию о репозитории в приведенном выше коде можно найти здесь.
Обратите внимание, что UnitOfWork
это больше, чем просто транзакция. Этот код обрабатывает только транзакции. Вы можете расширить этот код, чтобы охватить дополнительные роли.