猿问

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

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


现在为这两个应用程序配置 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。

但是我必须为每个依赖于 IDependacy 的类以及依赖于该类的每个类等等都这样做 - 继续我的工作。

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

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


RISEBY
浏览 245回答 1
1回答

绝地无双

IMO 使用工厂确实是错误的方法。Factory 使消费者复杂化,IDependency并且引入这个 Factory 抽象可能会导致整个应用程序发生彻底的变化。相反,我认为最合适的解决方案是应用代理模式。此代理将是 的一个实现,IDependency它将包装这两个IDependency实现,并将根据您指定的条件将任何传入调用分派到正确的实现。例如:public class DependencyDispatcher : IDependency{&nbsp; &nbsp; private ImplA a;&nbsp; &nbsp; private ImplB b;&nbsp; &nbsp; public DependencyDispatcher(ImplA a, ImplB b) {&nbsp; &nbsp; &nbsp; &nbsp; this.a = a;&nbsp; &nbsp; &nbsp; &nbsp; this.b = b;&nbsp; &nbsp; }&nbsp; &nbsp; private IDependency Dependency => someCondition ? this.a : this.b;&nbsp; &nbsp; // Implement IDependency methods to forward the call to Dependency&nbsp; &nbsp; void IDependency.DoSomething() => this.Dependency.DoSomething();}您可以将此代理配置为第三个应用程序IDependency的组合根中的默认实现。您的更新使事情变得更加清晰。您正在为请求提供一些运行时值,您需要根据此值做出决定。这里有几个解决方案。首先,尝试将此决定从请求正文中移到请求标头中。这样,您的调度员可以执行以下操作:private IDependency Dependency =>&nbsp;&nbsp; &nbsp; HttpContext.Current.Headers["MyHeader"] == "something" ? this.a : this.b;如果这不是一个选项,并且信息属于请求正文,您也许可以让您的调度员根据其输入做出决定。例如:public class DependencyDispatcher : IDependency{&nbsp; &nbsp; ...&nbsp; &nbsp; private IDependency GetDependency(string appType) =>&nbsp; &nbsp; &nbsp; &nbsp; appType == "a" ? this.a : this.b;&nbsp; &nbsp; void IDependency.DoSomething(DoSomethingData data) =>&nbsp; &nbsp; &nbsp; &nbsp; this.GetDependency(data.AppType).DoSomething(data);}这显然只有在将该AppType值(或可以转换为它的值)提供给IDependency的方法时才有可能。只有在这种情况下,有足够的可用信息才能做出此决定。如果这不是一个选项,另一种选择是定义一个抽象,允许在对象图中设置运行时值,它为调度程序提供该请求的信息。例如:public interface IApplicationContext{&nbsp; &nbsp; AppType ApplicationType { get; set; }}您的控制器可以IApplicationContext注入它并设置AppType属性:public async Task<Response> ProcessRequest([FromBody] Request request){&nbsp; &nbsp; var context = _someService.GetContext(request);&nbsp; &nbsp; this.applicationContext.ApplicationType = ParseAppTypeFromContext(context);&nbsp; &nbsp; this.dependency.DoSomethingElse();}或者,您可以添加一些中间件,AppType在调用控制器的 Action 方法之前设置。您也可以让代理实现IApplicationContext:public class DependencyDispatcher : IDependency, IApplicationContext{&nbsp; &nbsp; ...&nbsp; &nbsp; public AppType ApplicationType { get; set; }&nbsp; &nbsp; private IDependency Dependency => ApplicationType == AppType.A ? this.a : this.b;&nbsp; &nbsp; // Implement IDependency methods to forward the call to Dependency&nbsp; &nbsp; void IDependency.DoSomething() => this.Dependency.DoSomething();}
随时随地看视频慕课网APP
我要回答