Is it necessary to close connection once query is executed explicitly calling Close method or putting the connection within Using statement? Would leaving connection open lead to connection reuse and improve SQL performance for future queries?
问题:
回答1:
I am assuming that you are using latest version of Dapper.
With Dapper, there are two ways to manage connection:
Fully manage yourself: Here, you are fully responsible for opening and closing connection. This is just like how you treat connection while working with ADO.NET.
Allow Dapper to manage it: Dapper automatically opens the connection (if it was not opened) and closes it (if it was opened by Dapper) for you. This is similar to
DataAdapter.Fill()
method.I personally do not recommend this way.This may not be applicable every time. Following is what Marc Gravell says in one of the comment for this answer: https://stackoverflow.com/a/12629170/5779732
well, technically open/closed is different to disposed. If you are only going to be opening/closing around the individual calls, you might as well let dapper do it. If you are opening/closing at a wider granularity (per request, for example), it would be better for your code to do it and pass an open connection to dapper.
Ofcourse, you can call multiple queries on single connection. But, connection should be closed (by calling Close()
, Dispose()
method or by enclosing it in using
block) to avoid resource leak. Closing connection returns it to connection pool. Involvement of connection pool improves the performance over new connection cost.
In addition to just handling connection, I suggest you implement UnitOfWork to manage transactions as well. Refer this excellent sample on GitHub.
Following source code may help you. Note that this is written for my needs; so it may not work for you as is.
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();
}
Now, your repositories should accept this UnitOfWork in some way. I choose Dependency Injection with Constructor.
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, .........);
}
}
And then you call it like this:
With 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;
}
}
Without Transaction:
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);
}
This way, instead of directly exposing connection in your calling code, you control it at one location.
More details about Repository in above code could be found here.
Please note that UnitOfWork
is more than just a transaction. This code handles only transaction though. You may extend this code to cover additional roles.