web-dev-qa-db-ja.com

Dapper使用時に接続を閉じる

クエリが明示的に実行されたときに接続を閉じる必要がありますか?接続を開いたままにしておくと、接続が再利用され、将来のクエリのSQLパフォーマンスが向上しますか?

14
Megrez7

Dapperの最新バージョンを使用していることを前提としています。

Dapperでは、接続を管理する2つの方法があります。

  • 自分を完全に管理します:ここでは、接続の開閉を完全に担当します。これは、ADO.NETでの作業中に接続を処理する方法と同じです。

  • Dapperによる管理の許可:Dapperは自動的に接続を開き(開かれていない場合)、閉じます(Dapperによって開かれた場合)。これはDataAdapter.Fill()メソッドに似ています。 私は個人的にはこの方法はお勧めしません。 これは毎回適用できるとは限りません。以下は、Marc Gravellが comment のいずれかでこの回答について言っていることです: https://stackoverflow.com/a/12629170/5779732

まあ、技術的に開いたり閉じたりすることは処分することとは異なります。個々の呼び出しの前後でのみ開閉する場合は、dapperにそれを実行させることもできます。 より広い粒度で(たとえば、リクエストごとに)開閉する場合は、コードでそれを実行し、開いている接続をdapperに渡すことをお勧めします

以下は here からの引用です:

Dapperは、必要に応じて接続を閉じます。したがって、1つの簡単なクエリを実行している場合は、Dapperで処理してください。多くのことをしている場合は、効率性の観点から、すべてのクエリを途中で開いて(一度)閉じ、最後に閉じる必要があります。

もちろん、単一の接続で複数のクエリを呼び出すことができます。ただし、リソースリークを回避するには、接続を閉じる必要があります(Close()Dispose()メソッドを呼び出すか、usingブロックで囲む)。接続を閉じると、接続プールに戻ります。接続プールの関与により、新しい接続コストよりもパフォーマンスが向上します。


接続を処理するだけでなく、トランザクションを管理するためにUnitOfWorkを実装することをお勧めします。 GitHubの this 優れたサンプルを参照してください。

以下のソースコードが役立つかもしれません。これは私のニーズのために書かれていることに注意してください。そのため、そのままでは機能しない可能性があります。

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);
}

この方法では、呼び出しコードで接続を直接公開するのではなく、1つの場所で接続を制御します。

上記のコードのリポジトリに関する詳細は、 here で見つけることができます。

UnitOfWorkは、単なるトランザクションではなく more であることに注意してください。ただし、このコードはトランザクションのみを処理します。このコードを拡張して、追加の役割をカバーすることができます。

26
Amit Joshi