通过上下文解决依赖关系 - 深入解析树

Resolve dependencies by context - deep down the resolve tree

我们有两个应用程序共享一些具有依赖性的公共 classes。 这些依赖关系对于两者都是相同的,或者是特定于应用程序的。

现在为两个应用程序配置 IoC 很容易 - 使用 ImplementationA 作为一个应用程序的 IDependency,使用 ImplementationB 作为另一个应用程序的 IDependency。

但是 - 有第三个应用程序,有时在解析界面时需要使用应用程序 A 的依赖项,有时需要使用应用程序 B 的依赖项。换句话说,我需要这样的东西:

Resolve<ISomething>( when you come accross IDependecy (anywhere in the 'resolve tree') use ImplementationA)

Resolve<ISomething>( when you come accross IDependecy (anywhere in the 'resolve tree') use ImplementationB)

所以核心问题是:如何将上下文传递给从 Resolve 调用中选择实现的任何逻辑?

具体例子: .NET Core MVC App - 从请求中解析枚举值。现在我需要调用一些 IManagerFactory,将此枚举作为参数传递,并从应用程序 A 或 B 中获取具有所有依赖项的管理器的实现。(再次强调,不仅仅是管理器本身的依赖项)
从请求中获取上下文非常耗时,所以我只想做一次。这已经在方法开始时完成了。像这样

public async Task<Response> ProcessRequest([FromBody] Request request)
{
 var context = _someService.GetContext(request);
 var appType = ParseAppTypeFromContext(context);
 ...
 var manager=  _managerFactory.Resolve(appType);
 manager.DoSomething();
 manager.DoSomethingElse();
}

可能的解决方案:

  1. 我可以注册 ISomethingA,使用注册委托并让它通过 ResolvedParameter(Autofac 功能)解析正确的依赖关系 - 然后只解析 ISomethingA。

但是我必须为依赖于 IDependecy 的每个 class 和依赖于 class 的每个 class 等等做这件事 - 继续努力。

  1. 使用工厂。
    但是您仍然必须以某种方式告诉它您想要哪种实现。所以我必须从上到下传递该信息 - 这似乎有点.. 错误,因为这些是常见的 classes 不应该知道有一些应用程序 A 或 B。

所以..我迷路了。我不确定这是否适用于 IoC 或更好的设计。请指教
(我真的不在乎我使用哪个 IoC 容器——只要它是好的和维护的)

在我看来,使用工厂确实是错误的方法。工厂使 IDependency 的使用者变得复杂,并且引入此工厂抽象可能会导致整个应用程序发生彻底的变化。

相反,我认为最合适的解决方案是应用代理模式。此代理将是 IDependency 的一个实现,它将包装两个 IDependency 实现,并将根据您指定的条件将任何传入调用分派到正确的实现。

例如:

public class DependencyDispatcher : IDependency
{
    private ImplA a;
    private ImplB b;

    public DependencyDispatcher(ImplA a, ImplB b) {
        this.a = a;
        this.b = b;
    }

    private IDependency Dependency => someCondition ? this.a : this.b;

    // Implement IDependency methods to forward the call to Dependency
    void IDependency.DoSomething() => this.Dependency.DoSomething();
}

您可以将此代理配置为您第三个应用程序 Composition RootIDependency 的默认实现。

你的更新让事情变得更清楚了。您在请求中设置了一些运行时值,您需要根据该值做出决定。

这里有一些解决方案。首先,尝试将此决定 移出请求的 body 并移至 请求 header 中。这样,您的调度员可以执行以下操作:

private IDependency Dependency => 
    HttpContext.Current.Headers["MyHeader"] == "something" ? this.a : this.b;

如果这不是一个选项,并且请求 body 中的信息 属于 ,您可以让调度员根据其输入做出决定。例如:

public class DependencyDispatcher : IDependency
{
    ...

    private IDependency GetDependency(string appType) =>
        appType == "a" ? this.a : this.b;

    void IDependency.DoSomething(DoSomethingData data) =>
        this.GetDependency(data.AppType).DoSomething(data);
}

这显然只有在将 AppType 值(或可以转换为它的值)提供给 IDependency 的方法时才有可能。只有在那种情况下,有足够的可用信息才能做出此决定。

如果这不是一个选项,另一个选项是定义一个抽象,允许在 object 图中设置运行时值,它为调度程序提供该请求的信息。例如:

public interface IApplicationContext
{
    AppType ApplicationType { get; set; }
}

你的控制器可以得到这个 IApplicationContext 注入并设置 AppType 属性:

public async Task<Response> ProcessRequest([FromBody] Request request)
{
    var context = _someService.GetContext(request);
    this.applicationContext.ApplicationType = ParseAppTypeFromContext(context);
    this.dependency.DoSomethingElse();
}

或者,您可以添加一些中间件,在调用控制器的 Action 方法之前设置 AppType

您也可以让 Proxy 实现 IApplicationContext:

public class DependencyDispatcher : IDependency, IApplicationContext
{
    ...
    public AppType ApplicationType { get; set; }

    private IDependency Dependency => ApplicationType == AppType.A ? this.a : this.b;

    // Implement IDependency methods to forward the call to Dependency
    void IDependency.DoSomething() => this.Dependency.DoSomething();
}