111 votes

Comment gérer les connexions aux bases de données avec Dapper en .NET ?

J'ai joué avec Dapper, mais je ne suis pas sûr de la meilleure façon de gérer la connexion à la base de données.

La plupart des exemples montrent que l'objet de connexion est créé dans la classe d'exemple, voire dans chaque méthode. Mais il ne me semble pas correct de référencer une chaîne de connexion dans chaque classe, même si elle provient du web.config.

Mon expérience m'a permis d'utiliser un DbDataContext ou DbContext avec Linq to SQL ou Entity Framework, c'est donc nouveau pour moi.

Comment structurer mes applications web lorsque j'utilise Dapper comme stratégie d'accès aux données ?

4voto

Shuaib Points 540

Essayez ceci :

public class ConnectionProvider
    {
        DbConnection conn;
        string connectionString;
        DbProviderFactory factory;

        // Constructor that retrieves the connectionString from the config file
        public ConnectionProvider()
        {
            this.connectionString = ConfigurationManager.ConnectionStrings[0].ConnectionString.ToString();
            factory = DbProviderFactories.GetFactory(ConfigurationManager.ConnectionStrings[0].ProviderName.ToString());
        }

        // Constructor that accepts the connectionString and Database ProviderName i.e SQL or Oracle
        public ConnectionProvider(string connectionString, string connectionProviderName)
        {
            this.connectionString = connectionString;
            factory = DbProviderFactories.GetFactory(connectionProviderName);
        }

        // Only inherited classes can call this.
        public DbConnection GetOpenConnection()
        {
            conn = factory.CreateConnection();
            conn.ConnectionString = this.connectionString;
            conn.Open();

            return conn;
        }

    }

4voto

Zabbu Points 1067

Tout le monde semble ouvrir ses connexions beaucoup trop tôt ? Je me suis posé la même question, et après avoir consulté la source ici - https://github.com/StackExchange/dapper-dot-net/blob/master/Dapper/SqlMapper.cs

Vous constaterez que chaque interaction avec la base de données vérifie si la connexion est fermée et l'ouvre si nécessaire. Pour cette raison, nous utilisons simplement les instructions using comme ci-dessus sans conn.open(). De cette manière, la connexion est ouverte le plus près possible de l'interaction. Si vous le remarquez, la connexion est également fermée immédiatement. Cela sera également plus rapide que la fermeture automatique lors de l'élimination.

L'un des nombreux exemples tirés de la base de données ci-dessus :

    private static int ExecuteCommand(IDbConnection cnn, ref CommandDefinition command, Action<IDbCommand, object> paramReader)
    {
        IDbCommand cmd = null;
        bool wasClosed = cnn.State == ConnectionState.Closed;
        try
        {
            cmd = command.SetupCommand(cnn, paramReader);
            if (wasClosed) cnn.Open();
            int result = cmd.ExecuteNonQuery();
            command.OnCompleted();
            return result;
        }
        finally
        {
            if (wasClosed) cnn.Close();
            cmd?.Dispose();
        }
    }

Voici un petit exemple de l'utilisation d'un wrapper pour Dapper appelé DapperWrapper. Cela nous permet d'envelopper toutes les méthodes de Dapper et de Simple Crud pour gérer les connexions, assurer la sécurité, la journalisation, etc.

  public class DapperWrapper : IDapperWrapper
  {
    public IEnumerable<T> Query<T>(string query, object param = null, IDbTransaction transaction = null, bool buffered = true, int? commandTimeout = null, CommandType? commandType = null)
    {
      using (var conn = Db.NewConnection())
      {
          var results = conn.Query<T>(query, param, transaction, buffered, commandTimeout, commandType);
          // Do whatever you want with the results here
          // Such as Security, Logging, Etc.
          return results;
      }
    }
  }

3voto

Sergey Points 968

J'enveloppe la connexion avec la classe d'aide :

public class ConnectionFactory
{
    private readonly string _connectionName;

    public ConnectionFactory(string connectionName)
    {
        _connectionName = connectionName;
    }

    public IDbConnection NewConnection() => new SqlConnection(_connectionName);

    #region Connection Scopes

    public TResult Scope<TResult>(Func<IDbConnection, TResult> func)
    {
        using (var connection = NewConnection())
        {
            connection.Open();
            return func(connection);
        }
    }

    public async Task<TResult> ScopeAsync<TResult>(Func<IDbConnection, Task<TResult>> funcAsync)
    {
        using (var connection = NewConnection())
        {
            connection.Open();
            return await funcAsync(connection);
        }
    }

    public void Scope(Action<IDbConnection> func)
    {
        using (var connection = NewConnection())
        {
            connection.Open();
            func(connection);
        }
    }

    public async Task ScopeAsync<TResult>(Func<IDbConnection, Task> funcAsync)
    {
        using (var connection = NewConnection())
        {
            connection.Open();
            await funcAsync(connection);
        }
    }

    #endregion Connection Scopes
}

Exemples d'utilisation :

public class PostsService
{
    protected IConnectionFactory Connection;

    // Initialization here ..

    public async Task TestPosts_Async()
    {
        // Normal way..
        var posts = Connection.Scope(cnn =>
        {
            var state = PostState.Active;
            return cnn.Query<Post>("SELECT * FROM [Posts] WHERE [State] = @state;", new { state });
        });

        // Async way..
        posts = await Connection.ScopeAsync(cnn =>
        {
            var state = PostState.Active;
            return cnn.QueryAsync<Post>("SELECT * FROM [Posts] WHERE [State] = @state;", new { state });
        });
    }
}

Je n'ai donc pas besoin d'ouvrir explicitement la connexion à chaque fois. De plus, vous pouvez l'utiliser de cette manière pour des raisons de commodité lors de la future refonte :

var posts = Connection.Scope(cnn =>
{
    var state = PostState.Active;
    return cnn.Query<Post>($"SELECT * FROM [{TableName<Post>()}] WHERE [{nameof(Post.State)}] = @{nameof(state)};", new { state });
});

Qu'est-ce que TableName<T>() peut être trouvée dans cette réponse .

0voto

Gabriel Scavassa Points 434

Bonjour @donaldhughes Je suis également novice en la matière, et j'avais l'habitude de faire cela : 1 - Créer une classe pour obtenir ma chaîne de connexion 2 - Appeler la classe de chaîne de connexion dans un Using

Regardez :

DapperConnection.cs

public class DapperConnection
{

    public IDbConnection DapperCon {
        get
        {
            return new SqlConnection(ConfigurationManager.ConnectionStrings["Default"].ToString());

        }
    }
}

DapperRepository.cs

  public class DapperRepository : DapperConnection
  {
       public IEnumerable<TBMobileDetails> ListAllMobile()
        {
            using (IDbConnection con = DapperCon )
            {
                con.Open();
                string query = "select * from Table";
                return con.Query<TableEntity>(query);
            }
        }
     }

Et cela fonctionne bien.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X