如何避免与IoC容器耦合

时间:2011-04-26 11:24:02

标签: c# dependency-injection ioc-container ninject dependency-management

我正在使用DI和IoC开发可扩展框架。用户必须能够通过将自己的实现放入容器中来覆盖框架内的现有功能。

如何让用户在不要求他们知道我正在使用哪个IoC容器的情况下执行此操作?

我目前的中途解决方案是按如下方式构建我的程序集:

1)定义仅包含接口的抽象程序集。

2)定义实现这些接口的具体组件。用户可以定义自己的内容以覆盖现有功能。

3)在单独的程序集中定义容器绑定;即每个混凝土组件一个装订组件。

这意味着混凝土组件不与特定的IoC容器耦合,如果我使用不同的容器,它们将被关闭以防止更改。但是,用户仍然需要知道我的框架正在使用哪个容器来编写绑定程序集,如果我更改了IoC容器(即从Ninject到Spring),他们需要释放新的绑定程序集。

我错过了什么吗?

5 个答案:

答案 0 :(得分:9)

Write loosely coupled code。应用程序应该依赖于容器。 Frameworks不应该。{/ p>

答案 1 :(得分:2)

常用方法是使用common service locator

抽象容器

MvcExtensions的作者已经非常成功地提取了IoC。

答案 2 :(得分:1)

Common Service Locator是一种方法,但它只包含解析的方法,而不是用于注册的方法。

您可能想了解agatha-rrsl project中如何实现这一点。有一个更完整的解释here,但简而言之:

  • 定义用于注册和解析类型的容器无关接口
  • 提供不同容器的实现(或让用户提交实现)

警告:您可能无法在库中直接使用您选择的容器。

答案 3 :(得分:0)

两个简单的选项是为用户提供一些可以注册类型映射的字典,或者只是为他们提供一个容器接口,提供您认为可能需要的所有服务,并允许用户使用供应自己的包装容器。抱歉如果这些不适合您的场景,我主要使用Unity,所以我不知道Ninject等是否做了Unity不喜欢的事情。

答案 4 :(得分:0)

我通过使用属性解决了这个问题,并提供了一个扫描器类,用于定位所有实现及其提供的接口。

public class ComponentAttribute : Attribute
{}

// class that should be registered in the container.
[Component]
public class MyService : IMyService
{}

// Contains information for each class that should be 
// registered in the container.
public interface IContainerMapping
{
    public Type ImplementationType {get;}
    public IEnumerable<T> ImplementedServices {get; }
}

public class ComponentProvider
{
    public static IEnumerable<IContainerMapping> Find() 
    {
        var componentType = typeof(ComponentAttribute);
        foreach (var type in Assembly.GetExecutingAssembly().GetTypes())
        {
           if (type.GetCustomAttributes(componentType, false).Count == 0)
              continue;

           var mapping = new ContainerMapping(type);
           List<Type> interfaces new List<Type>();
           foreach (var interfac in type.GetInterfaces())
           {
             //only get our own interfaces.
             if (interface.Assembly != Assembly.GetExecutingAssembly())
               continue;

             interfaces.Add(interfac);
           }

           mapping.ImplementedServices = interfaces;
           yield return mapping;
        }
    }
}

此解决方案为用户提供了很大的灵活性。他可以直接使用[Component]属性或使用您的解决方案来提供自己的解决方案。

用户应该做的事情是:

foreach (var mapping in ComponentProvider.Find())
    myContainer.Register(mapping.ImplementationType).As(mapping.ImplementedServices);

我通常通过提供一个MyProject.autofac项目来创建一个全面的解决方案,该项目将所有内容都记录在我最喜欢的容器中。