【问题标题】:Resolve dependencies by context - deep down the resolve tree通过上下文解决依赖关系 - 深入解析树
【发布时间】:2018-08-15 08:29:27
【问题描述】:

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

现在为两个应用配置 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 的每个类和依赖于该类的每个类都这样做,依此类推 - 继续努力。

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

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

【问题讨论】:

    标签: c# dependency-injection ioc-container


    【解决方案1】:

    IMO,使用工厂确实是错误的方法。工厂使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 的默认实现。

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

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

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

    您的控制器可以得到这个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

    您也可以让代理实现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();
    }
    

    【讨论】:

    • 但是你如何传递关于使用哪个实现的信息,从解析调用到 DependencyDispatcher?或者更准确地说 - 来自将调用解决的工厂。这是主要问题 - 选择实现的逻辑取决于上下文。这不是可以全局设置的。
    • 嗯,这是一个我无法为您回答的问题,因为您尚未描述进行转换所需的信息和条件。您能否更新您的问题并展示一些具体示例来说明您正在努力实现的目标?
    • 更新了我的答案
    • 我想到了与 IApplicationContext 类似的东西,但有一个问题 - 只有在每个范围有一个 appType 时才有效(或者只要它是同步的 - 否则可能会发生一个实例更改另一个的 appType)。实际上就是这种情况,但我希望有更通用的解决方案,因为我们将来可能需要在另一个应用程序中使用它。 (它很可能是异步的)但这仍然非常好,我想我们必须在异步出现时处理它。所以谢谢:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-05-19
    • 1970-01-01
    • 2023-02-08
    • 2016-10-15
    • 2016-11-11
    • 2015-10-27
    • 2014-12-18
    相关资源
    最近更新 更多