c# - 如何在 .NET 中使用 Dapper 处理数据库连接?

标签 c# .net dapper

我一直在玩 Dapper,但我不确定处理数据库连接的最佳方式。

大多数示例显示了在示例类中甚至在每个方法中创建的连接对象。但是我觉得在每个 clss 中引用一个连接字符串是不对的,即使它是从 web.config 中提取的。

我的经验是将 DbDataContextDbContext 与 Linq to SQL 或 Entity Framework 一起使用,所以这对我来说是新的。

当使用 Dapper 作为我的数据访问策略时,我该如何构建我的网络应用程序?

最佳答案

更新:MarredCheese 评论的澄清:

"No need to use a using statement. Dapper will automatically open, close, and dispose of the connection for you." That's not correct. Dapper will automatically open closed connections, and it will automatically close connections that it auto-opened, but it will not automatically dispose of connections. Marc Gravell and Eric Lippert both advocate using using with Dapper here.

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

我不确定我是否正确使用了最佳实践,但我这样做是为了处理多个连接字符串。

如果你只有 1 个连接字符串就很容易

启动.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<IDbConnection>((sp) => new SqlConnection(dbConnectionString));

            // Register your regular repositories
            services.AddScoped<IDiameterRepository, 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<Diameter> 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<Diameter>(sql);
        }

        // ......
    }
}

如果你有超过 1 个连接字符串会出现问题

由于 Dapper 使用了 IDbConnection,您需要想办法区分不同的数据库连接。

我尝试创建多个接口(interface),“继承”自 IDbConnection,对应不同的数据库连接,并在 Startup< 上用不同的数据库连接字符串注入(inject) SqlConnection/.

失败是因为 SqlConnection 继承自 DbConnection,而 DbConnection 不仅实现了 IDbConnection,还实现了 组件类。因此,您的自定义接口(interface)将无法仅使用 SqlConnection 实现。

我还尝试创建自己的 DbConnection 类,它采用不同的连接字符串。这太复杂了,因为您必须实现 DbConnection 类中的所有方法。您失去了来自 SqlConnection 的帮助。

我最终做了什么

  1. 启动 期间,我将所有连接字符串值加载到字典中。我还为所有数据库连接名称创建了一个 enum 以避免使用魔法字符串。
  2. 我将字典作为单例注入(inject)。
  3. 我没有注入(inject) IDbConnection,而是创建了 IDbConnectionFactory 并将其作为 Transient 注入(inject)所有存储库。现在所有存储库都采用 IDbConnectionFactory 而不是 IDbConnection
  4. 何时选择正确的连接?在所有存储库的构造函数中!为了让事情变得干净,我创建了存储库基类并让存储库继承自基类。可以在基类中选择正确的连接字符串。

数据库连接名称.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 - 我自己的工厂实现

namespace DL.SO.Project.Persistence.Dapper
{
    public class DapperDbConnectionFactory : IDbConnectionFactory
    {
        private readonly IDictionary<DatabaseConnectionName, string> _connectionDict;

        public DapperDbConnectionFactory(IDictionary<DatabaseConnectionName, string> connectionDict)
        {
            _connectionDict = connectionDict;
        }

        public IDbConnection CreateDbConnection(DatabaseConnectionName connectionName)
        {
            string connectionString = null;
            if (_connectDict.TryGetValue(connectionName, out connectionString))
            {
                return new SqlConnection(connectionString);
            }

            throw new ArgumentNullException();
        }
    }
}

启动.cs

namespace DL.SO.Project.Web.UI
{
    public class Startup
    {
        // ......
         
        public void ConfigureServices(IServiceCollection services)
        {
            var connectionDict = new Dictionary<DatabaseConnectionName, string>
            {
                { DatabaseConnectionName.Connection1, this.Configuration.GetConnectionString("dbConnection1") },
                { DatabaseConnectionName.Connection2, this.Configuration.GetConnectionString("dbConnection2") }
            };

            // Inject this dict
            services.AddSingleton<IDictionary<DatabaseConnectionName, string>>(connectionDict);

            // Inject the factory
            services.AddTransient<IDbConnectionFactory, DapperDbConnectionFactory>();

            // Register your regular repositories
            services.AddScoped<IDiameterRepository, 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<Diameter> 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<Diameter>(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);
        }
    }
}

然后对于需要与其他连接通信的其他存储库,您可以为它们创建不同的存储库基类。

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<Parameter> GetAll()
        {
            const string sql = @"SELECT * FROM TABLE";
            return base.DbConnection.Query<Parameter>(sql);
        }

        // ......
    }
}

希望所有这些帮助。

关于c# - 如何在 .NET 中使用 Dapper 处理数据库连接?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9218847/

相关文章:

c# - 来自 C# - 有没有办法确定 swf 是 AVM1 还是 AVM2?

c# - Windows 8.1 应用程序中的 FlipView 渲染问题

c# - 在 Moq 中为返回 void 的方法分配参数

c# - 如何压缩一个 IEnumerable 自身

.net - 命名列 "Operation could destabilize the runtime"时使用Dapper和 'Id'

c# - 从表中查询数据时管理实体的最佳实践?

c# - 代码隐藏是否比内联代码更快?

c# - 几个小时后并行发送 HTTP 请求后 ServicePoint 对象的大小很大

c# - 使用 Async 和 Await 中断数据库调用(使用 Dapper)

c# - FormMethod.Post 在 MVC4 中返回查询字符串