【问题标题】:Dependency Injection Container依赖注入容器
【发布时间】:2011-01-25 16:23:49
【问题描述】:

我有一个数据访问层库,我想使其“可移植”。我喜欢它可移植的原因是因为我想在具体服务器上使用 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 在每个方法中查找连接字符串。

这可能吗?抽象工厂或某种依赖注入容器模式可以解决问题吗?如果是这样,我将不胜感激代码示例或代码示例的链接。

【问题讨论】:

  • 什么样的项目会使用你的数据访问代码?

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


【解决方案1】:

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

其次,这个DataProvider应该被注入到DataAccessConsumer中。

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

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

【讨论】:

  • +1 您可能还想查看此问题,并在此答案的基础上扩展答案:stackoverflow.com/questions/4570750/…
  • 谢谢马克。这是迄今为止我遇到的最有帮助的帖子。我很欣赏 Arafangion 的评论,但他只是在重申显而易见的事实。乔·R也是如此。
【解决方案2】:

嗯,我推出了自己的 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 容器/工厂(单例或静态类)

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 的代码。这在软件架构方面实现了真正的关注点分离。

【讨论】:

  • 您应该将提供者工厂(并且还使用 ConnectionFactory 而不是自己创建连接)作为依赖项传递,以实现真正的 DI。
  • 你是对的。我没想到!将相应地修改我的设计!
猜你喜欢
  • 2013-04-30
  • 2014-09-15
  • 1970-01-01
  • 2016-11-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-13
  • 2018-12-18
相关资源
最近更新 更多