将运行时值注入Unity依赖性解析程序

时间:2014-04-29 10:50:28

标签: c# asp.net-web-api dependency-injection unity-container

我正在开发一个webapi项目并使用Unity作为我们的IOC容器。我有一组分层依赖项,如下所示:

unityContainer.RegisterType<BaseProvider, CaseProvider>(new HierarchicalLifetimeManager());
unityContainer.RegisterType<IRulesEngine, RulesEngine>();
unityContainer.RegisterType<IQuestionController, QuestionController>();
unityContainer.RegisterType<IAPIThing, WebAPIThing>();

现在,BaseProvider的构造函数接受一个int作为参数,它是Case标识符。 WebAPIThing在其构造函数中使用BaseProvider。通常在非Web场景中,我会使用类似的东西注入case id:

public static IAPIThing GetIAPIThing(int caseId)
{
  return CreateUnityContainer().Resolve<IAPIThing >(new ParameterOverride("caseId", caseId).OnType<CaseProvider>());
}

但这只有在我明确调用该方法时才有效。在Web API场景中,我使用的是 config.DependencyResolver = new UnityDependencyResolver(unityContainer);解析我的api控制器。

我猜我仍然需要影响DependencyResolver在运行时如何解析该BaseProvider对象。

任何人都必须做类似的事情吗?

编辑1
我尝试过使用以下似乎有效的方法:

unityContainer.RegisterType<BaseProvider>(
        new HierarchicalLifetimeManager()
        , new InjectionFactory(x => 
                    new CaseProvider(SessionManager.GetCaseID())));

1 个答案:

答案 0 :(得分:5)

您正在尝试将运行时值(case id)注入对象图中,这意味着您使对象图的配置,构建和验证变得复杂。

你应该做的是将原始价值提升到自己的抽象。这听起来可能很愚蠢,但这种抽象在描述其功能方面会做得更好。例如,在您的情况下,抽象应该命名为ICaseContext

public interface ICaseContext
{
    int CurrentCaseId { get; }
}

通过有效地隐藏int这个抽象背后:

  • 非常明确地阐述了这个int的角色。
  • 删除了应用程序可能需要的任何其他int类型值的冗余。
  • 延迟解析此int,直到构建对象图形为止。

您可以在应用程序的核心层中定义此ICaseContext,并且每个人都可以依赖它。在Web API项目中,您可以定义此ICaseContext抽象的特定于Web API的实现。例如:

public class WebApiCaseContext : ICaseContext
{
    public int CurrentCaseId
    {
        get { return (int)HttpContext.Current.Session["CaseId"];
    }
}

此实现可以注册如下:

unityContainer.RegisterType<ICaseContext, WebApiCaseContext>();

更新

请注意,您自己的new CaseProvider(SessionManager.GetCaseID())配置并不能解决所有问题,因为这意味着在验证对象图时必须有一个会话,在应用程序启动期间和单元内部都不会出现这种情况。整合测试。