依赖注入Sql连接?

时间:2009-11-15 15:00:54

标签: c# sql dependency-injection

首先,我开始使用StructureMap,但任何DI框架中都会有一个例子。

我有一个课程,

public class GeoData
{
   public List<Country> GetCountries()
   {
      IDbConnection con = new SqlConnection(ConfigurationManager.ConnectionString["GeoDataConnection"])    
      //Sql stuff to return countries from a database
   }
}

这是一个简单的视图,看看这个类实际上是什么样的,但基本上就是这样。

现在,我有了新的要求。我需要能够在类初始化或方法上更改连接字符串。 E.g。

public void Do()
{
   var geoData = new GeoData();

   if(x)
   {
      geoData.ConnectionString = ConfigurationManager.ConnectionString["LIVEGeoDataConnection"]);
   }
   else
   {
      geoData.ConnectionString = ConfigurationManager.ConnectionString["STAGINGGeoDataConnection"]);
   }

   geoData.GetCountries();
}

使用依赖注入是否有更好的解决方案?您如何使用您选择的DI框架执行此操作?

6 个答案:

答案 0 :(得分:9)

从技术上讲,Wim Hollebrandse已经回答了你的问题,但我只是想指出我会亲自做另外一种方式,因为我不喜欢在每次实例化类时都必须传递连接字符串。我意识到你有一个默认的构造函数,但我认为我们可以让它更清洁一点。

首先,我将创建一个静态类来获取连接,如下所示:

public static class ConnectionFactory
{
    public static IDbConnection GetConnection()
    {
        return GetConnection(ConfigurationManager.ConnectionString["GeoDataConnection"]);
    }

    public static IDbConnection GetConnection(string connectionString)
    {
        return new SqlConnection(connectionString);
    }
}

然后,我会这样使用它:

public class GeoData
{
   public List GetCountries()
   {
      using (IDbConnection con = ConnectionFactory.GetConnection())
      {
        //Sql stuff to return countries from a database
      }
   }
}

使用这种方法,如果默认连接字符串发生更改,则只需更改一行代码,而不是从配置文件中转到引用连接字符串的每一行。但是,它确实能够在必要时覆盖连接字符串。

希望有帮助...

答案 1 :(得分:5)

简单,您不需要框架。只需要为GeoData类重载一个构造函数。

GeoData geo = new GeoData(yourConnString);

字符串是您的依赖项。由于它不是一个复杂的类型,你可以在那里获得依赖注入。

DI不是火箭科学,虽然有些人可能会相信你。

答案 2 :(得分:5)

问自己的第一个问题是什么是GeoData?换句话说,班级的职责是什么?

它似乎是Domain Layer的一部分,因此可能包含业务逻辑。它可以由(coupled)用于其他类。

如果是这样的话,依赖是什么?确定这一点的一种方法是尝试编写单独测试GeoData的unit tests。如果测试需要大量设置,则测试类要么与其他类紧密耦合,要么具有多个职责(低cohesion)。

假设我们更改了类,以便构造函数获取连接字符串参数。我们如何测试公共GetCountries方法?好吧,首先我们建立一个包含已知测试数据的数据库......

这是耗时且脆弱的(如果有人更新数据会怎么样?),测试运行相对缓慢(它必须连接到数据库)。

好吧,我们可以将实现IDbConnection的对象传递给构造函数(构造函数注入)。请注意,依赖注入通常涉及传入接口或抽象类。要测试它,我们必须创建一个假的IDbConnection。我们可以使用isolation (mocking) framework。但是当我们调用CreateCommand时,我们需要它来创建一个假的IDbCommand ......

引用Jeremy Miller(StructureMap的作者)“这太赚不了太多的努力。”请参阅他的文章Best and Worst Practices for Mock Objects

一种可能性是使用Repository Pattern。您可以将特定存储库的接口传递给GeoData构造函数。这很容易伪造(手动或使用模拟库)进行测试。具体的存储库将处理所有数据访问。它可以与ORM框架结合使用,以进一步抽象数据访问。连接字符串管理将通过ORM或通过存储库(最好是在另一个依赖项或基类中)完成。

如果这听起来很复杂,那是因为它是。您选择了一个最难处理的依赖注入案例(不幸的是,它也是最常见的案例之一)。

依赖注入本身就是一个相当简单的概念。如果您的类正在调用Web服务,则可以将Web Service代码放在单独的类中,该类不执行任何其他操作,实现接口,并将该接口传递给原始类。如果您有很多类和/或依赖项,DI / IoC容器框架可以使这更容易,但它们不是必需的。

编辑:为了清楚起见,依赖注入不是复杂的部分。分离数据访问是。

答案 3 :(得分:1)

我创建了一个工厂,用于创建GeoData类的实例,而该实例又实现了Do方法的接口(例如,IDoCommand)。

工厂有责任使用全局上下文来确定要向GeoData实例注入哪个连接字符串(构造函数是我的首选技术),或者将其作为参数放在Create方法中

答案 4 :(得分:0)

Martin Fowler有一篇关于这个主题的文章here,它解释了各种方法。就个人而言,我更喜欢界面注入,但这是一个品味问题。

答案 5 :(得分:0)

我要做的是创建一个新类来包含连接字符串选择逻辑,然后使用它来获取GeoData实例的连接字符串:

public class ConnectionStringManager
{
    public string GeoDataConnectionString
    {
        get
        {
            return x 
                ? ConfigurationManager.ConnectionString["LIVEGeoDataConnection"])
                : ConfigurationManager.ConnectionString["STAGINGGeoDataConnection"]);
        }
    }
}

然后,您可以将其注入包含Do方法的类中,以设置GeoData实例,如下所示:

public class Blah(ConnectionStringManager connManager)
{
    public void Do()
    {
        var geoData = new GeoData { ConnectionString = connManager.GeoDataConnectionString };
        geoData.GetCountries();
    }
}