web-dev-qa-db-fra.com

Fermer la connexion lors de l'utilisation de Dapper

Est-il nécessaire de fermer la connexion une fois que la requête est exécutée en appelant explicitement la méthode Close ou en plaçant la connexion dans l'instruction Using? Le fait de laisser la connexion ouverte entraînerait-il une réutilisation de la connexion et améliorerait-il les performances SQL pour les requêtes futures?

14
Megrez7

Je suppose que vous utilisez la dernière version de Dapper.

Avec Dapper, il existe deux façons de gérer la connexion:

  • Gérez-vous entièrement: Ici, vous êtes entièrement responsable de l'ouverture et de la fermeture de la connexion. C'est comme la façon dont vous traitez la connexion tout en travaillant avec ADO.NET.

  • Autorisez Dapper à le gérer: Dapper ouvre automatiquement la connexion (si elle n'a pas été ouverte) et la ferme (si elle a été ouverte par Dapper) pour vous. Ceci est similaire à la méthode DataAdapter.Fill()Personnellement, je ne recommande pas cette méthode. Cela peut ne pas être applicable à chaque fois. Voici ce que Marc Gravell dit dans l'un des commentaire pour cette réponse: https://stackoverflow.com/a/12629170/5779732

eh bien, techniquement ouvert/fermé est différent de disposé. Si vous voulez seulement ouvrir/fermer autour des appels individuels, vous pourriez aussi bien laisser Dapper le faire. Si vous ouvrez/fermez à une granularité plus large (par demande, par exemple), il serait préférable que votre code le fasse et passe une connexion ouverte à dapper.

Ci-dessous est la citation de ici :

Dapper fermera la connexion s'il devait l'ouvrir. Donc, si vous ne faites qu'une seule requête rapide - laissez Dapper s'en occuper. Si vous en faites plusieurs, vous devriez ouvrir (une fois) et fermer à la fin, avec toutes les requêtes au milieu ... juste du point de vue de l'efficacité.

Bien sûr, vous pouvez appeler plusieurs requêtes sur une seule connexion. Mais, la connexion doit être fermée (en appelant la méthode Close(), Dispose() ou en l'enfermant dans le bloc using) pour éviter une fuite de ressources. La fermeture de la connexion la renvoie dans le pool de connexions. L'implication du pool de connexions améliore les performances par rapport au nouveau coût de connexion.


En plus de gérer uniquement la connexion, je vous suggère d'implémenter UnitOfWork pour gérer également les transactions. Reportez-vous à this excellent échantillon sur GitHub.

Le code source suivant peut vous aider. Notez que ceci est écrit pour mes besoins; donc cela peut ne pas fonctionner pour vous tel quel.

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

Maintenant, vos référentiels devraient accepter cette UnitOfWork d'une manière ou d'une autre. Je choisis l'injection de dépendances avec le constructeur.

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

Et puis vous l'appelez comme ceci:

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

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

De cette façon, au lieu d'exposer directement la connexion dans votre code d'appel, vous la contrôlez à un seul endroit.

Plus de détails sur le référentiel dans le code ci-dessus peuvent être trouvés ici .

Veuillez noter que UnitOfWork est plus qu'une simple transaction. Ce code ne gère cependant que les transactions. Vous pouvez étendre ce code pour couvrir des rôles supplémentaires.

26
Amit Joshi