抽象工厂适合Unity

时间:2015-03-13 05:30:19

标签: c# dependency-injection unity-container abstract-factory

我们正在构建一个与其他系统具有多个集成接触点的应用程序。我们正在有效地使用Unity来满足所有依赖注入需求。整个业务层使用接口驱动方法构建,实际实现在应用程序引导期间注入外部组合根。

我们希望以优雅的方式处理集成层。业务类和存储库依赖于IIntegrationController<A, B>接口。几个IIntegrationController<A, B>实现一起表示在后台与一个目标系统的集成 - 形成一个集成层。目前,我们在开始时将组合根中的所有内容连接起来。此界面的消费者也预先在适当的InjectionConstrutorResolvedParameter注册。大多数类型都使用PerResolveLifetime运行,而消耗IIntegrationController的业务类也分别针对每个请求上下文进行解析。

参考下面的代码。

        // IIntegrationController Family 1
        // Currently the default registration for IIntegrationController types injected into the business classes
        container.RegisterType<IIntegrationController<A, B>, Family1-IntegrationController<A, B>>();
        container.RegisterType<IIntegrationController<C, D>, Family1-IntegrationController<C, D>>();

        // IIntegrationController Family 2 (currently not registered)
        // We want to be able to register this, or manage this set of mapping registrations separately from Family 1,
        // and be able to hook these up dynamically instead of Family-1 on a per-resolve basis
        container.RegisterType<IIntegrationController<A, B>, Family2-IntegrationController<A, B>>();
        container.RegisterType<IIntegrationController<C, D>, Family2-IntegrationController<C, D>>();

        // Repository/Business Class that consume IIntegrationControllers.
        // There is a whole family of IIntegrationController classes being hooked in, 
        // and there are multiple implementations for the family (as shown above). A typical AbstractFactory scenario.
        container.RegisterType(typeof(Repository<Z>), new PerResolveLifetimeManager(),
            new InjectionConstructor(
                new ResolvedParameter<IIntegrationController<A, B>>(), 
                new ResolvedParameter<IIntegrationController<C, D>>())
        );

问题陈述:

我们希望能够在运行时切换整个IIntegrationController<A, B>族。在解决业务类时,我们希望根据上下文中可用的请求参数为其注入正确版本的IIntegrationController<A, B>

  • 基于“命名”注册的解决方案不具有可扩展性,原因有两个:必须切换整个系列的集成类,并且在代码中需要笨重的名称注册和条件解析才能使其难以维护
  • 即使存在分支链/层次结构,解决方案也应该有效,即IIntegrationController的直接消费者也通过Unity解决,因为它会动态地注入到另一个类中。
  • 我们在解决期间尝试了DependencyOverrideResolveOverride课程,但这需要覆盖整套家庭2 IIntegrationController分辨率,而不仅仅是能够切换整个层。
  • 我们理解,不是将IIntegrationController直接注入到业务类中,而是可能必须注入AbstractFactory,但我们无法使其工作,并且不确定注册和解决方案将在何处发生。如果业务类与AbstractFactory挂钩,首先我必须按照解决方案连接正确的工厂,
  • 这是否需要覆盖InjectionFactoryThis link提出了一种方法,但我们无法顺利开展工作。

1 个答案:

答案 0 :(得分:2)

您的设计的好处在于您已经拥有正确的抽象。您使用通用抽象,因此只需在已经SOLID设计之上应用正确的模式即可解决问题。

换句话说,使用代理:

// This class should be considered part of your composition root.
internal class IntegrationControllerDispatcher<TRequest, TResult> 
    : IIntegrationController<TRequest, TResult>
{
    private readonly IUserContext userContext;
    private readonly Family1_IntegrationController<A, B> family1Controller;
    private readonly Family2_IntegrationController<A, B> family2Controller;

    public IntegrationControllerDispatcher(
        IUserContext userContext,
        Family1_IntegrationController<A, B> family1Controller,
        Family2_IntegrationController<A, B> family2Controller) {
        this.userContext = userContext;
        this.family1Controller = family1Controller;
        this.family2Controller = family2Controller;
    }

    public TResult Handle(TRequest request) {
        return this.GetController().Handle(request);
    }

    private IIntegrationController<TRequest, TResult> GetController() {
        return this.userContext.IsInFamily("family1"))
            ? this.family1Controller
            : this.family2Controller;
    }
}

使用此类,您可以将整个配置简化为:

container.RegisterType<IUserContext, AspNetUserContext>();

container.RegisterType( 
    typeof(IIntegrationController<,>), 
    typeof(IntegrationControllerDispatcher<,>));

container.RegisterType(typeof(Repository<>), typeof(Repository<>));

请注意以下事项:

  • 请注意使用执行开放式通用映射的注册。您不必逐个注册所有已关闭的版本。你可以用一行代码来完成它。
  • 另请注意,不同系列的类型不是 注册。 Unity可以自动解决它们,因为我们的 IntegrationControllerDispatcher直接取决于他们。这个 class是一个基础结构逻辑,应该放在里面 你的作文根。
  • 请注意,在构建对象图时,不会决定使用特定的族实现;它是在运行时创建的,因为确定它的值是运行时值。在构建对象图时尝试确定这一点只会使事情复杂化,并使验证对象图更加困难。
  • 此外,这个运行时值在函数调用后面被抽象出来并放在抽象(IUserContext.IsInFamily之后,在这种情况下,这只是一个例子)。