我们有两个应用程序,它们共享一些具有依赖关系的通用类。 这些依赖关系对于两者或特定于应用程序都是相同的。
现在为两个应用程序配置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();
}
可能的解决方案:
但是我必须对依赖于IDependecy的每个类以及依赖于该类的每个类进行操作,依此类推-继续努力。
所以..我迷路了。我不确定这是IoC还是更好的设计。请指教。
(我并不在乎我使用哪个IoC容器-只要它很好并且可以维护)
答案 0 :(得分:2)
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
的默认实现。
您的更新使情况更加清晰。您正在为请求添加一些运行时值,并且需要根据该值做出决定。
这里有一些解决方案。首先,尝试将该决策从请求的正文中移出并移到 request标头中。这样,您的调度员可以执行以下操作:
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();
}