如何避免与 IoC 容器耦合

发布于 2024-11-03 19:50:16 字数 410 浏览 5 评论 0原文

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

如何允许用户执行此操作而不要求他们知道我正在使用哪个 IoC 容器?

我当前的半途解决方案是按如下方式构造我的程序集:

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

2) 定义实现这些接口的具体程序集。用户可以定义自己的功能来覆盖现有功能。

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

这意味着具体组件不与特定的 IoC 容器耦合,如果我使用不同的容器,它们将针对更改而关闭。但是,用户仍然需要知道我的框架正在使用哪个容器才能编写绑定程序集,并且如果我更改了 IoC 容器(即从 Ninject 到 Spring),他们将需要发布新的绑定程序集。

我错过了什么吗?

I'm in the process of developing an extensible framework using DI and IoC. Users must be able override existing functionality within the framework by dropping their own implementations into the container.

How can I allow users to do this without requiring them to know which IoC container I am using?

My current half-way solution is to structure my assemblies as follows:

1) Define abstract assemblies containing only interfaces.

2) Define concrete assemblies which implement these interfaces. Users may define their own to override existing functionality.

3) Define the container bindings in separate assemblies; i.e. one binding assembly per concrete assembly.

This means the concrete assemblies are not coupled with a particular IoC container, and they would be closed against change if I used a different container. However, users are still required to know which container my framework is using in order to write the binding assemblies, and they would need to release new binding assemblies if I changed the IoC container (i.e. from Ninject to Spring).

Am I missing something?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

心的位置 2024-11-10 19:50:16

编写松散耦合的代码。应用程序应该依赖于容器。 框架不应该。

Write loosely coupled code. Applications should depend on containers. Frameworks should not.

月隐月明月朦胧 2024-11-10 19:50:16

常见的方法是使用 通用服务定位器 抽象容器

MvcExtensions 有非常成功地抽象了 IoC。

Common approach is to abstract container with common service locator

Author of MvcExtensions have abstracted IoC away quite successfully.

2024-11-10 19:50:16

公共服务定位器是一种方法,但它只包含解析方法,而不包含注册方法。

您可能想看看这是如何在 agatha-rrsl 项目 中实现的。 这里有更完整的解释,但是简而言之:

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

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

Common Service Locator is one approach, but it only contains methods for resolving, not for registering.

You may want to have a look at how this is implemented in the agatha-rrsl project. There's a more complete explanation here, but in short:

  • define a container-agnostic interface for registering and resolving types
  • provide implementations for the different containers (or let users submit implementations)

Caveat: you probably won't be able to directly use your container of choice in your library.

鹊巢 2024-11-10 19:50:16

两个简单的选择是要么为您的用户提供一些字典,他们可以在其中注册类型映射,要么只是为他们提供一个容器接口,该接口提供您认为可能需要的所有服务并允许用户提供自己的服务包装好的容器。如果这些不太适合您的场景,我深表歉意,我主要使用 Unity,所以我不知道 Ninject 等是否可以做 Unity 做不到的奇特事情。

Two simple options would be to either provide your user with some dictionary where they can register type mappings, or alternatively just provide them with a container interface that provides all of the services you think you're likely to require and allow users to supply their own wrapped containers. Apologies if these don't quite fit your scenario, I primarily use Unity, so I don't know if Ninject, etc. do fancy things Unity doesn't.

来世叙缘 2024-11-10 19:50:16

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

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 项目来创建一个准备就绪的解决方案,该项目将所有内容注册在我最喜欢的容器中。

I've solved this by using an attribute and provide a scanner class which is used to locate all implementations and the interfaces that they provide.

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;
        }
    }
}

This solution gives the user much flexibility. He can provide his own solution by using the [Component] attribute directly or by using your solution.

What the user should do is something like:

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

I usually create an all-ready solution by providing a MyProject.autofac project which registers everything in my favorite container.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文