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

现在,为两个应用程序配置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 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的中间件。

您也可以让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/

10-16 16:11