【问题标题】:Simple but good example on how to use Dapper with Structuremap and dependency injection关于如何将 Dapper 与 Structuremap 和依赖注入一起使用的简单但很好的示例
【发布时间】:2016-10-27 19:09:45
【问题描述】:

我正在尝试了解如何将依赖注入与 Dapper (IDbConnection) 一起使用,并且仍然能够使用内置的 dispose。

我在网上找到了几篇文章,但不是我认为容易理解的。

我想弄清楚的是如何让这个简单的类可测试:

public class UserProfileRepository : IUserProfileRepository
{
    private readonly IConfigRepository _configRepository;

    public UserProfileRepository(IConfigRepository configRepository)
    {
        _configRepository = configRepository;
    }

    public UserProfile GetUserProfile(string userId)
    {
        const string query = @"Select UserId, UserName
                                From Users
                                Where UserId = @UserId";

        using (var conn = new SqlConnection(_configRepository.GetConnectionString("MyConnectionString")))
        {
            conn.Open();
            return conn.Query<UserProfile>(query, new { UserId = userId }).SingleOrDefault();
        }
    }
}

我有一个如下所示的配置存储库,因此我可以模拟对 web.config 的请求:

public class ConfigRepository : IConfigRepository
{
    public string GetConnectionString(string key)
    {
        var conString = ConfigurationManager.ConnectionStrings[key];
        if (conString != null)
        {
            return conString.ConnectionString;
        }

        return string.Empty;
    }
}

我已经读到您可以使用 ConnectionFactory,但还没有弄清楚如何实现它,但仍然知道我正在正确地处理它。

谁能指出我正确的方向?

【问题讨论】:

  • 您还没有考虑其他选项。一,通过集成测试来测试这个类。第二,模拟整个存储库(因为您毕竟使用依赖注入)并放弃数据库调用。目前还不清楚您实际上要完成什么,因为您仍然直接实例化 sql 连接,使其不可模拟。
  • 谢谢。是的,我刚刚开始尝试使这些存储库可测试,但要么我不擅长谷歌搜索,要么没有关于如何做到这一点的最佳实践。
  • @HenrikFransas 最佳实践基于您的要求,没有适合所有情况的标准规则。尽管如此,让我发布一个使用 Ninject 的 DI 示例,Structure Map 也应该存在类似的机制
  • 我下面的答案是对另一个问题的重复,但要求或多或少相同,只有 Mocking 是额外的

标签: c# dependency-injection structuremap dapper


【解决方案1】:

根据我的经验,最好的连接创建机制是 DependencyInjectionConnectionFactory 的组合。我正在摆脱IConfigRepository,因为这里所有的工作都是使用工厂完成的

优势是多方面的:

  • 在运行时在事务或线程范围内创建连接对象
  • 在运行时更改系统的数据提供者和数据库(使用连接工厂)

你应该做什么(在代码中):

在数据访问层声明IDBConnection对象:

[Inject] // Property Injection
public IDBConnection Connection {get; set;}

使用像 Ninject 这样的 DI 框架声明绑定:

Bind<IDBConnection>().ToMethod(ctx => 
ConnectionFactory.CreateDbConnection("DefaultConnection"));

如下创建 DBConnection 工厂:

连接工厂从配置文件中获取连接提供者和连接字符串,如下所示:

<connectionStrings>
    <add name="DefaultConnection" connectionString="Data Source=<Value>;Initial Catalog=<Value>;User Id=<Value>;Password=<Value>" providerName="System.Data.SqlClient" />
</connectionStrings>

标识符是DefaultConnection,它使用SqlClient提供程序,但在运行时可以更改为不同的客户端,如Oracle, MySql

 using System;
 using System.Data.Common;

 public static class ConnectionFactory
    {
        /// <summary>
        /// Create DBConnection type based on provider name and connection string
        /// </summary>
        /// <param name="connectionIdentifier"></param>
        /// <returns></returns>
        public static DbConnection CreateDbConnection(string connectionIdentifier)
        {
            // Provider name setting
            var providerNameValue = ConfigurationManager.ConnectionStrings[connectionIdentifier].ProviderName;

            // Connection string setting
            var connectionStringValue = ConfigurationManager.ConnectionStrings[connectionIdentifier].ConnectionString;

            // Assume failure.
            DbConnection connection;

            // Null connection string cannot be accepted
            if (connectionStringValue == null) return null;

            // Create the DbProviderFactory and DbConnection.
            try
            {
                // Fetch provider factory
                var factory = DbProviderFactories.GetFactory(providerNameValue);

                // Create Connection
                connection = factory.CreateConnection();

                // Assign connection string
                if (connection != null)
                    connection.ConnectionString = connectionStringValue;
            }
            catch (Exception ex)
            {
                connection = null;
            }
            // Return the connection.
            return connection;
        }
}

如何使用:

单次调用并处理

using(Connection)
{
 ...
}

对于事务上下文,按原样使用,不需要using

关于模拟:

您使用哪个 Mock 框架进行单元测试,您必须模拟 UserProfileRepository :: GetUserProfile(string userId) 的结果,这比使用依赖注入填充 MockConnection 更容易,这会使它变得复杂。 DI 适合实际用例,用于在运行时填充连接对象

【讨论】:

  • 谢谢,试试看。这是否会使其根据请求正确处理或使用什么范围?
  • 在 Pooling on 模式下,Close / Dispose 将连接对象发送回池而不是销毁它。异议的创建和处置始终是每个请求,但您可以使用依赖注入调用/绑定来基于范围事务。我的代码使用 Ninject,请确保找到 StructureMap 替代品。
  • 谢谢,会试用并回复您
  • 抱歉,还没来得及试用,这个周末就试试,
  • 如何处理和关闭事务上下文的连接对象?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-02-22
  • 1970-01-01
  • 1970-01-01
  • 2021-02-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多