c# - 依赖注入(inject)和策略模式

标签 c# .net dependency-injection ioc-container strategy-pattern

关于此主题的讨论很多,但是每个人似乎都错过了一个显而易见的答案。我需要帮助来审核这个“显而易见的” IOC容器解决方案。各种对话均假定在运行时选择策略并使用IOC容器。我将继续这些假设。

我还想添加一个假设,即不是必须选择的单个策略。相反,我可能需要检索一个对象图,该对象图在整个图的节点中都有几种策略。

我将首先快速概述这两个通常提出的解决方案,然后介绍我想看到IOC容器支持的“明显”替代方案。尽管我的问题并非特定于Unity,但我将使用Unity作为示例语法。

命名绑定(bind)

这种方法要求每个新策略都必须手动添加一个绑定(bind):

Container.RegisterType<IDataAccess, DefaultAccessor>();
Container.RegisterType<IDataAccess, AlphaAccessor>("Alpha");
Container.RegisterType<IDataAccess, BetaAccessor>("Beta");

...然后明确要求正确的策略:
var strategy = Container.Resolve<IDataAccess>("Alpha");
  • 优点:简单,并且受所有IOC容器的支持
  • 缺点:
  • 通常将调用方绑定(bind)到IOC容器,并且肯定要求调用方了解有关该策略的一些信息(例如名称“Alpha”)。
  • 每个新策略都必须手动添加到绑定(bind)列表中。
  • 此方法不适用于处理对象图中的多种策略。简而言之,它不符合要求。

  • 抽象工厂

    为了说明这种方法,请假定以下类:
    public class DataAccessFactory{
        public IDataAccess Create(string strategy){
            return //insert appropriate creation logic here.
        }
        public IDataAccess Create(){
            return //Choose strategy through ambient context, such as thread-local-storage.
        }
    }
    public class Consumer
    {
        public Consumer(DataAccessFactory datafactory)
        {
            //variation #1. Not sufficient to meet requirements.
            var myDataStrategy = datafactory.Create("Alpha");
            //variation #2.  This is sufficient for requirements.
            var myDataStrategy = datafactory.Create();
        }
    }
    

    然后,IOC容器具有以下绑定(bind):
    Container.RegisterType<DataAccessFactory>();
    
  • 优点:
  • 消费者未隐藏IOC容器
  • “环境上下文”更接近期望的结果,但是...
  • 缺点:
  • 每个策略的构造函数可能有不同的需求。但是现在,构造函数注入(inject)的责任已经从容器转移到抽象工厂。换句话说,每次添加新策略时,可能有必要修改相应的抽象工厂。
  • 大量使用策略意味着大量创建抽象工厂。如果IOC容器仅提供更多帮助,那就太好了。
  • 如果这是一个多线程应用程序,并且“环境上下文”确实是由线程本地存储提供的,那么当对象使用注入(inject)的抽象工厂创建所需的类型时,它可能会在一个不再有权访问必需的线程本地存储值的线程。

  • 类型切换/动态绑定(bind)

    这是我要使用的方法,而不是上面的两种方法。它涉及在IOC容器绑定(bind)中提供一个委托(delegate)。几乎所有的IOC容器都已经具有此功能,但是此特定方法具有重要的细微差别。

    语法如下所示:
    Container.RegisterType(typeof(IDataAccess),
        new InjectionStrategy((c) =>
        {
            //Access ambient context (perhaps thread-local-storage) to determine
            //the type of the strategy...
            Type selectedStrategy = ...;
            return selectedStrategy;
        })
    );
    

    请注意,InjectionStrategy没有返回IDataAccess的实例。相反,它返回实现IDataAccess的类型描述。然后,IOC容器将执行该类型的常规创建和“构建”,其中可能包括选择其他策略。

    这与标准的类型到委托(delegate)绑定(bind)形成对比,在Unity的情况下,绑定(bind)是这样编码的:
    Container.RegisterType(typeof(IDataAccess),
        new InjectionFactory((c) =>
        {
            //Access ambient context (perhaps thread-local-storage) to determine
            //the type of the strategy...
            IDataAccess instanceOfSelectedStrategy = ...;
            return instanceOfSelectedStrategy;
        })
    );
    

    上面的代码实际上很接近满足整体需求,但是绝对没有达到假设的Unity InjectionStrategy

    关注第一个样本(使用了假设的Unity InjectionStrategy):
  • 优点:
  • 隐藏容器
  • 无需创建无尽的抽象工厂,也不需要让消费者摆弄它们。
  • 只要有新策略可用,就无需手动调整IOC容器绑定(bind)。
  • 允许容器保留生命周期管理控件。
  • 支持纯DI故事,这意味着多线程应用可以使用适当的线程本地存储设置在线程上创建整个对象图。
  • 缺点:
  • 因为创建初始IOC容器绑定(bind)时,该策略返回的Type不可用,所以这意味着首次返回该类型时性能可能会受到影响。换句话说,容器必须当场反射(reflect)类型以发现其具有的构造函数,以便知道如何注入(inject)它。该类型的所有后续出现都应该很快,因为容器可以缓存从第一次发现的结果。这几乎不是一个“骗局”,值得一提,但我正在尝试进行全面披露。
  • ???

  • 是否存在可以以这种方式运行的现有IOC容器?任何人都有实现此效果的Unity自定义注入(inject)类?

    最佳答案

    据我所知,这个问题与几种候选策略之一的运行时选择或映射有关。

    没有理由依赖DI容器来执行此操作,因为至少有三种方法可以通过与容器无关的方式来执行此操作:

  • 使用Metadata Role Hint
  • 使用Role Interface Role Hint
  • 使用Partial Type Name Role Hint

  • 我的个人偏好是“部分类型名称角色提示”。

    关于c# - 依赖注入(inject)和策略模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22701412/

    相关文章:

    .net - 将SAP连接到.NET的选项

    java - Remember Me with Spring Security 3.1.3 - 弃用的默认构造函数

    Spring依赖注入(inject)带注释的Aspect

    c# - Owin:多次调用 OnApplyRedirect 并创建不正确的 RedirectUri

    c# - 如何在C#中将31/03/2011 00:00:00日期格式转换为2011年3月31日

    c# - 寻找如何创建通用 CreateDataAdapter

    c# - C# 中的 WMI 查询在非英语机器上不起作用

    java - 有关 @Value 错误行为的文档

    c# - 尝试调用 Twitter 流 API 时出现 401 错误

    c# - 开始了解 Web 套接字和 http 请求(和 STOMP)