通过上下文解析依赖性-深入解析树

时间:2018-08-15 08:29:27

标签: c# dependency-injection ioc-container

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

现在为两个应用程序配置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();
}

可能的解决方案:

  1. 我可以注册ISomethingA,使用注册委托,并通过ResolvedParameter(Autofac功能)解析正确的依赖关系,然后再解决ISomethingA。

但是我必须对依赖于IDependecy的每个类以及依赖于该类的每个类进行操作,依此类推-继续努力。

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

所以..我迷路了。我不确定这是IoC还是更好的设计。请指教。
 (我并不在乎我使用哪个IoC容器-只要它很好并且可以维护)

1 个答案:

答案 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 RootIDependency的默认实现。

您的更新使情况更加清晰。您正在为请求添加一些运行时值,并且需要根据该值做出决定。

这里有一些解决方案。首先,尝试将该决策从请求的正文中移出并移到 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();
}
相关问题