首先,我开始使用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框架执行此操作?
答案 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();
}
}