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

标签 c# dependency-injection ioc-container

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

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

但是 - 有第三个应用程序,有时在解析接口(interface)时需要使用应用程序 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,使用注册委托(delegate)并让它通过 ResolvedParameter(Autofac 功能)解析正确的依赖关系 - 然后只解析 ISomethingA。

但我必须为依赖于 IDependecy 的每个类和依赖于该类的每个类等等做这件事——按我的方式做。

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

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

最佳答案

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

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

例如:

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 的默认实现你的第三次申请。

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

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

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

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

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 的方法时才有可能。只有在那种情况下,有足够的可用信息才能做出此决定。

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

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

您的 Controller 可以注入(inject)此 IApplicationContext 并设置 AppType 属性:

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

或者,您可以添加一些中间件,在调用 Controller 的 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();
}

关于c# - 通过上下文解决依赖关系 - 深入解析树,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51855355/

相关文章:

c# - 什么实际处理 Windows 壁纸的绘制?

java - Spring 限定符和属性占位符

dependency-injection - .Net 6 Blazor 服务器项目中的配置注入(inject)为空

c# - 如何在不强制用户使用库的 IOC 容器的情况下编写库

java - MentaContainer 上的自动布线不起作用?

c# - 固定托管 List<> 以获取指向数据的指针

c# - 使用触摸滑动拖动打开 WPF 扩展器

c# - 有没有一种方法可以使用 JsonConverter 派生的自定义转换器进行依赖注入(inject)

c# - 实体类的依赖注入(inject)

WPF Prism 为什么要注册类型? (带容器)