How do I handle Database Connections with Dapper in .NET?

前端 未结 9 2055
闹比i
闹比i 2020-12-07 07:52

I\'ve been playing with Dapper, but I\'m not sure of the best way to handle the database connection.

Most examples show the connection object being created in the

9条回答
  •  伪装坚强ぢ
    2020-12-07 08:35

    Microsoft.AspNetCore.All: v2.0.3 | Dapper: v1.50.2

    I am not sure if I am using the best practices correctly or not, but I am doing it this way, in order to handle multiple connection strings.

    It's easy if you have only 1 connection string

    Startup.cs

    using System.Data;
    using System.Data.SqlClient;
    
    namespace DL.SO.Project.Web.UI
    {
        public class Startup
        {
            public IConfiguration Configuration { get; private set; }
    
            // ......
    
            public void ConfigureServices(IServiceCollection services)
            {
                // Read the connection string from appsettings.
                string dbConnectionString = this.Configuration.GetConnectionString("dbConnection1");
    
                // Inject IDbConnection, with implementation from SqlConnection class.
                services.AddTransient((sp) => new SqlConnection(dbConnectionString));
    
                // Register your regular repositories
                services.AddScoped();
    
                // ......
            }
        }
    }
    

    DiameterRepository.cs

    using Dapper;
    using System.Data;
    
    namespace DL.SO.Project.Persistence.Dapper.Repositories
    {
        public class DiameterRepository : IDiameterRepository
        {
            private readonly IDbConnection _dbConnection;
    
            public DiameterRepository(IDbConnection dbConnection)
            {
                _dbConnection = dbConnection;
            }
    
            public IEnumerable GetAll()
            {
                const string sql = @"SELECT * FROM TABLE";
    
                // No need to use using statement. Dapper will automatically
                // open, close and dispose the connection for you.
                return _dbConnection.Query(sql);
            }
    
            // ......
        }
    }
    

    Problems if you have more than 1 connection string

    Since Dapper utilizes IDbConnection, you need to think of a way to differentiate different database connections.

    I tried to create multiple interfaces, 'inherited' from IDbConnection, corresponding to different database connections, and inject SqlConnection with different database connection strings on Startup.

    That failed because SqlConnection inherits from DbConnection, and DbConnection inplements not only IDbConnection but also Component class. So your custom interfaces won't be able to use just the SqlConnection implenentation.

    I also tried to create my own DbConnection class that takes different connection string. That's too complicated because you have to implement all the methods from DbConnection class. You lost the help from SqlConnection.

    What I end up doing

    1. During Startup, I loaded all connection string values into a dictionary. I also created an enum for all the database connection names to avoid magic strings.
    2. I injected the dictionary as Singleton.
    3. Instead of injecting IDbConnection, I created IDbConnectionFactory and injected that as Transient for all repositories. Now all repositories take IDbConnectionFactory instead of IDbConnection.
    4. When to pick the right connection? In the constructor of all repositories! To make things clean, I created repository base classes and have the repositories inherit from the base classes. The right connection string selection can happen in the base classes.

    DatabaseConnectionName.cs

    namespace DL.SO.Project.Domain.Repositories
    {
        public enum DatabaseConnectionName
        {
            Connection1,
            Connection2
        }
    }
    

    IDbConnectionFactory.cs

    using System.Data;
    
    namespace DL.SO.Project.Domain.Repositories
    {
        public interface IDbConnectionFactory
        {
            IDbConnection CreateDbConnection(DatabaseConnectionName connectionName);
        }
    }
    

    DapperDbConenctionFactory - my own factory implementation

    namespace DL.SO.Project.Persistence.Dapper
    {
        public class DapperDbConnectionFactory : IDbConnectionFactory
        {
            private readonly IDictionary _connectionDict;
    
            public DapperDbConnectionFactory(IDictionary connectionDict)
            {
                _connectionDict = connectionDict;
            }
    
            public IDbConnection CreateDbConnection(DatabaseConnectionName connectionName)
            {
                string connectionString = null;
                if (_connectDict.TryGetValue(connectionName, out connectionString))
                {
                    return new SqlConnection(connectionString);
                }
    
                throw new ArgumentNullException();
            }
        }
    }
    

    Startup.cs

    namespace DL.SO.Project.Web.UI
    {
        public class Startup
        {
            // ......
    
            public void ConfigureServices(IServiceCollection services)
            {
                var connectionDict = new Dictionary
                {
                    { DatabaseConnectionName.Connection1, this.Configuration.GetConnectionString("dbConnection1") },
                    { DatabaseConnectionName.Connection2, this.Configuration.GetConnectionString("dbConnection2") }
                };
    
                // Inject this dict
                services.AddSingleton>(connectionDict);
    
                // Inject the factory
                services.AddTransient();
    
                // Register your regular repositories
                services.AddScoped();
    
                // ......
            }
        }
    }
    

    DiameterRepository.cs

    using Dapper;
    using System.Data;
    
    namespace DL.SO.Project.Persistence.Dapper.Repositories
    {
        // Move the responsibility of picking the right connection string
        //   into an abstract base class so that I don't have to duplicate
        //   the right connection selection code in each repository.
        public class DiameterRepository : DbConnection1RepositoryBase, IDiameterRepository
        {
            public DiameterRepository(IDbConnectionFactory dbConnectionFactory)
                : base(dbConnectionFactory) { }
    
            public IEnumerable GetAll()
            {
                const string sql = @"SELECT * FROM TABLE";
    
                // No need to use using statement. Dapper will automatically
                // open, close and dispose the connection for you.
                return base.DbConnection.Query(sql);
            }
    
            // ......
        }
    }
    

    DbConnection1RepositoryBase.cs

    using System.Data;
    using DL.SO.Project.Domain.Repositories;
    
    namespace DL.SO.Project.Persistence.Dapper
    {
        public abstract class DbConnection1RepositoryBase
        {
            public IDbConnection DbConnection { get; private set; }
    
            public DbConnection1RepositoryBase(IDbConnectionFactory dbConnectionFactory)
            {
                // Now it's the time to pick the right connection string!
                // Enum is used. No magic string!
                this.DbConnection = dbConnectionFactory.CreateDbConnection(DatabaseConnectionName.Connection1);
            }
        }
    }
    

    Then for other repositories that need to talk to the other connections, you can create a different repository base class for them.

    using System.Data;
    using DL.SO.Project.Domain.Repositories;
    
    namespace DL.SO.Project.Persistence.Dapper
    {
        public abstract class DbConnection2RepositoryBase
        {
            public IDbConnection DbConnection { get; private set; }
    
            public DbConnection2RepositoryBase(IDbConnectionFactory dbConnectionFactory)
            {
                this.DbConnection = dbConnectionFactory.CreateDbConnection(DatabaseConnectionName.Connection2);
            }
        }
    }
    
    using Dapper;
    using System.Data;
    
    namespace DL.SO.Project.Persistence.Dapper.Repositories
    {
        public class ParameterRepository : DbConnection2RepositoryBase, IParameterRepository
        {
            public ParameterRepository (IDbConnectionFactory dbConnectionFactory)
                : base(dbConnectionFactory) { }
    
            public IEnumerable GetAll()
            {
                const string sql = @"SELECT * FROM TABLE";
                return base.DbConnection.Query(sql);
            }
    
            // ......
        }
    }
    

    Hope all these help.

提交回复
热议问题