依赖注入容器

时间:2011-01-25 16:23:50

标签: c# design-patterns dependency-injection theory factory-pattern

我有一个数据访问层库,我想让它“可移植”。我喜欢它是可移植的原因是因为我想使用SQL Azure& Azure文件存储(例如,数据+ pdf报告)以及具体服务器上的Sql Server 2008R2和文件系统存储。

根据规范,系统应该与后来的实现(sql +文件系统存储)一起使用,而在会议上有一定的可伸缩性阈值,我们计划迁移到Azure。

我使用的数据访问类实现了IDataProvider接口(我构建了它),它定义了具体实现应该具有的任何数据访问方法。通过传递IDataProvider接口并调用其上的方法来完成数据访问层的使用,例如:

public Interface IDataProvider
{
    public bool DoSomething();
}

public class AzureDataProvider : IDataProvider
{
    private string ConnectionString;

    public AzureDataProvider(string connectionString)
    {
        this.ConnectionString = connectionString;
    }

    public AzureDataProvider():this(
        ConfigurationManager.ConnectionString["conn"].ConnectionString)
    {
    }

    public bool DoSomething()
    {
        return false;
    }
}

所以现在的问题是,在IDataProvider接口上调用方法的使用者类必须执行以下操作:

public class DataAccessConsumer
{
    public void SomeOperation()
    {
        AzureDataProvider azureProvider = new AzureDataProvider();
        IDataProvider dataProvider = (IDataProvider)azureProvider;

        bool result = dataProvider.DoSomething();
    }
}

因此上述代码的问题是客户端仍然必须了解具体的AzureDataProvider类。我希望有一种方法可以为客户端提供IDataProvider接口,而无需将连接字符串传递给每个接口方法,或者通过ConfigurationManager在每个方法中查找连接字符串。

这可能吗?抽象工厂或某种依赖注入容器模式会起作用吗?如果是这样,我会很感激代码示例或代码示例的链接。

2 个答案:

答案 0 :(得分:1)

首先,AzureDataProvider依赖于ConfigurationManager。这应该注入。

其次,应将此DataProvider注入DataAccessConsumer。

这意味着一个真实的应用程序将始终具有良好的依赖注入,不依赖于容器,但是您将需要“连接” - 将所有依赖项连接在一起。这很痛苦 - 仅在主入口点使用DependencyInjectionContainer来帮助解决这种连线问题。 (这允许您使用更方便的声明方法而不是命令式方法,因为您可以询问容器“让我获取DataAccessConsumer”,依赖注入框架将为您找出依赖关系。

我最喜欢的C#依赖注入框架是NInject2。

答案 1 :(得分:1)

好吧,我推出了自己的DI。如果目标是实现可移植性,那么我认为我已经达到了半可接受的解决方案。缺点是您无法真正实现完整的DI,但在我的应用环境中它已经足够了。

连接界面:

public interface IConnection
{
    public string ConnectionString;
}

具体连接实施

public class Connection: IConnection
{
    public string ConnectionString{ get; set; }

    public Connection(string connectionString)
    {
        this.ConnectionString = connectionString;
    }

    public Connection():this(ConfigurtionManager.ConnectionStrings["connection"].ConnectionString)
    {
        //Broke DI in the interest of usability.
    }
}

数据访问层接口

public interface IDataProvider
{
    IConnection Connection;

    public void Foo();
}

具体数据访问层实施

public class AzureProvider : IDataProvider
{
    IConnection Connection { get; set; }

    public AzureProvider(IConnection connection)
    {
        this.Connection = connection;
    }

    public void Foo()
    {

    }
}

DI Conainer / Factory(Singleton或Static Class)

public static class ProviderFactory
{
    public static IDataProvider GetProvider()  //I'd pass parameters if I had more than 1.
    {
        Connection connection = new Connection(); //this is why I broke DI.
        IConnection iConnection = (IConnection)connection;

        AzureProvider azureProvider = new AzureProvider(iConnection);
        IDataProvider iDataProvider = (IDataProvider)azureProvider;

        return iDataProvider;
    }
}

数据访问层使用者(在此示例中为页面):

public class SomePage : Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        IDataProvider provider = ProviderFactory.GetProvider();
        provider.Foo();
    }
}

如您所见,该页面不需要知道数据访问层的任何实现细节。只要ProviderFactory可以吐出IDataProvider,页面就会很开心。因此,如果我们决定更改提供程序,比如SqlStorageProvider,只要它实现了IDataProvider接口,就不必更改Page的代码。这实现了软件架构方面真正的关注点分离。