【发布时间】:2018-02-19 12:01:13
【问题描述】:
我有一个控制器调用全局单例,在 ASP.NET WebAPI 的上下文中调用每个请求实例。基本上
每个请求 => 单例 => 每个请求
以下设置是否正确?我特别担心第二个 per-request 实例,因为它可能是基于用户的声明。
或者反模式和单例应该从控制器接收(每个请求)依赖项?
// UnityConfig.cs, the Resolver uses CreateChildContainer()
public static void RegisterTypes(IUnityContainer unity)
{
// global singleton
unity.RegisterType<MessageContext>(new ContainerControlledLifetimeManager());
// instance per request
unity.RegisterType<ClaimsContext>(new HierarchicalLifetimeManager());
}
// API controller
public class MyController : ApiController
{
private readonly MessageContext _message;
public FormController(MessageContext message)
{
_message = message;
}
public Task<IHttpActionResult> MyAction()
{
_message.FireAndForget();
return Ok();
}
}
// global singleton to keep connection pool
public class MessageContext
{
private readonly IUnityContainer _unity;
public MessageContext(IUnityContainer unity)
{
_unity = unity;
}
public Task FireAndForget()
{
// resolve per request
var claim = _unity.Resolve<ClaimsContext>().MyClaim);
// ...
}
}
【问题讨论】:
-
这个问题,如果对
_unity.Resolve的调用能正确解决,仍然存在,但我猜它是臭代码。重构可能是最好的! -
正如博客文章所解释的,消费者将在消费者的生命周期内保持其依赖关系。这意味着,如果您在单例中注入 per-request,per-request 依赖项将在请求中继续存在,这几乎总是会导致意想不到的后果(现在或将来)。防止出现这种情况,并找到一种方法来使用单元测试来检测这些类型的错误(或切换到开箱即用为您检测这些类型问题的容器)。
-
明白这一点,而且很明显上面的代码设计得不好,但是
ClaimsContext是手动解决的,而不是构造函数注入,而不是在MessageContext的私有属性中。引用仅在FireAndForget调用期间有效。那么你所说的在那种情况下仍然适用吗?博文 afaik 中没有提到这种情况。 -
您在 MessageContext 中应用 Service Locator anti-pattern,这也很糟糕。您应该同时阻止服务定位器和强制依赖项。
标签: c# asp.net asp.net-web-api dependency-injection unity-container