我们有两个应用程序共享一些具有依赖关系的通用类。
这些依赖关系对于两者或特定于应用程序都是相同的。
现在,为两个应用程序配置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应用程序-值是从请求中解析的。现在,我需要调用一些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();
}
可能的解决方案:
我可以注册ISomethingA,使用注册委托并让它通过ResolvedParameter(Autofac功能)解析正确的依赖关系,然后解析ISomethingA。
但是我必须对依赖于IDependecy的每个类以及依赖于该类的每个类都这样做,依此类推-继续努力。
使用工厂。
但是您仍然必须以某种方式告诉它您想要哪种实现。因此,我将必须从上到下传递该信息-似乎有点..错误,因为这些是不知道存在某个应用程序A或B的通用类。
所以..我迷路了。我不确定这是IoC还是更好的设计。请指教。
(我并不在乎我使用哪个IoC容器-只要它很好并且可以维护)
最佳答案
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 Root中
IDependency
的默认实现。您的更新使事情变得更加清晰。您在请求中添加了一些运行时值,因此您需要根据该值做出决定。
这里有一些解决方案。首先,尝试将此决策移出请求的正文并移入请求标头。这样,您的调度员可以执行以下操作:
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
的中间件。您也可以让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/