【问题标题】:Managing AutoFac lifetime scopes per session and request in asp.net mvc 3在 asp.net mvc 3 中管理每个会话和请求的 AutoFac 生命周期范围
【发布时间】:2013-10-12 05:13:55
【问题描述】:

我想在 Web 应用程序中使用 AutoFac。我有根容器,每个会话有一个子容器,每个请求有一个子容器。我试图弄清楚管理这些生命周期范围的最佳方法是什么。在 Global.asax.cs 我添加了以下内容:

protected void Application_Start(object sender, EventArgs e)
{
    var container = ...;
}

protected void Session_Start(object sender, EventArgs e)
{
    var sessionScope = container.BeginLifetimeScope("session");

    Session["Autofac_LifetimeScope"] = sessionScope;
}

protected void Application_BeginRequest(object sender, EventArgs e)
{
    var sessionScope = (ILifetimeScope) Session["Autofac_LifetimeScope"];
    var requestScope = sessionScope.BeginLifetimeScope("httpRequest");

    HttpContext.Current.Items["Autofac_LifetimeScope"] = requestScope;
}

protected void Application_EndRequest(object sender, EventArgs e)
{
    var requestScope = (ILifetimeScope)HttpContext.Current.Items["Autofac_LifetimeScope"];
    requestScope.Dispose();
}

protected void Session_End(object sender, EventArgs e)
{
    var sessionScope = (ILifetimeScope)Session["Autofac_LifetimeScope"];

    sessionScope.Dispose();
}

protected void Application_End(object sender, EventArgs e)
{
    container.Dispose();
}
  1. 如何告诉 AutoFac 使用我的 requestScope 作为获取依赖项的起点,以便我注册为 InstancePerLifetimeScope 的实现将使用我的 requestScope 解析?

  2. 如果这不可能,我可以让 AutoFac 在我的 sessionScope 之外创建其每个请求的生命周期范围吗?

  3. 还是我在这里走错了路?是否有其他方法可以让 AutoFac 了解这种层次结构?

感谢任何帮助或其他 cmets。


回应史蒂文。

我仍处于原型设计的早期阶段,但在 sessionScope 中可能会有一些东西:

  • 用户首选项
  • 身份验证和授权上下文(例如用户身份和角色)

与我要构建的应用程序无关,但在电子商务环境中,购物车可以是会话范围的。这可能是最好的具体例子。它是您期望的比请求长,但比应用短的东西。

可能不止这些,但如果我有一个用于 UserPreferences、身份验证和授权的策略,那么该策略也可以应用于稍后将创建的其他组件。

一种可能的替代方法是在请求开始时获取所有必要的信息,并将这些配置的组件放在请求范围内。它会给我我期望的结果,但它与我脑海中关于应用程序->会话->请求层次结构的模型不匹配。我希望创建一个有意义的系统,因为我绝对不是要维护它的人。

【问题讨论】:

  • 您希望在每个会话范围内注册哪些服务?我们需要这样才能对问题 3“我是不是走错了路?”给出了一个好的答案。

标签: c# asp.net-mvc-3 autofac


【解决方案1】:

您需要做的是实现您自己的Autofac.Integration.Mvc.ILifetimeScopeProvider。此接口控制如何/在何处生成请求生命周期范围。默认的 Autofac.Integration.Mvc.RequestLifetimeScopeProvider 会根据每个请求处理生命周期范围的创建、处置和维护。

You can browse the code for RequestLifetimeScopeProvider here,如果您打算这样做,我强烈建议您这样做。这是我能想到的最好的示例,其中包含显示其中一件事情的责任的工作代码。

ILifetimeScopeProvider 的实现将是您获取会话子容器的位置,从中生成请求容器,然后在请求结束时清理请求容器。如果会话容器不存在,您可能还想在其中创建会话容器。处理会话容器的清理/处置可能会很棘手,但从设计的角度来看,如果它们都在一个地方而不是提供者中的某个地方,或者您的应用程序类中的某个地方,那就太好了。

拥有ILifetimeScopeProvider 后,您将在设置依赖关系解析器时使用它。

var scopeProvider = new MyCustomLifetimeScopeProvider(container, configAction);
var resolver = new AutofacDependencyResolver(container, scopeProvider);
DependencyResolver.SetResolver(resolver);

关于会话级范围概念的几句警告:

  1. 您的内存占用可能很大。您最终会为系统上的每个用户提供一个生命周期范围。虽然请求生命周期会很快出现并很快消失,但这些会话级范围可能会存在很长时间。如果您有很多会话范围的项目,那么您将为每个用户使用相当大的内存使用量。如果人们在没有正确退出的情况下“放弃”他们的会话,那么这些东西的寿命就会更长。
  2. 生命周期范围及其内容不可序列化Looking at the code for LifetimeScope,它没有被标记为[Serializable]...即使是这样,居住在那里的已解析对象也不一定都被标记为可序列化的。这很重要,因为这意味着您的会话级生命周期范围可能在具有内存会话的单个机器上工作,但是如果您使用 SQL 会话或会话服务部署到场,事情将会崩溃,因为会话无法序列化您存储的范围。如果您选择不序列化范围,那么跨机器的每个用户都有不同的范围 - 这也是一个潜在的问题。
  3. 会话并不总是补水。如果正在访问的处理程序(例如,Web 表单)没有实现IRequiresSessionState,则会话将不会被重新水化(无论它是否在进程中)。 Web 表单和MvcHandler 默认实现了这一点,因此您不会看到任何问题,但如果您有需要注入的自定义处理程序,您会遇到一些障碍,因为这些请求不存在“会话”。
  4. Session_End 并不总是触发Per the docs on SessionStateModule.End,如果您使用进程外会话状态,您实际上不会获得 Session_End 事件,因此您将无法清理。

鉴于这些限制,尽量远离会话存储范围通常是件好事。但是...如果您要这样做,ILifetimeScopeProvider 就是这样做的方法。

【讨论】:

  • Nicholas Blumhardt 在 CodeProject describing this hierarchy 上发表了一篇文章。但是那篇文章只是给出了一些不好的建议?我可以想象有些组件的生命周期与会话相同,所以它看起来是一个不错的方法。该应用程序不会有很多用户,也无需将其部署在场上。但你提出了一些有效的观点。
  • 还有 2b,当您扩展到 Web 场时,您的应用程序会出现内存泄漏,因为在使用进程外会话状态时永远不会调用 Session_End
  • 在那篇文章中,我认为尼克并不是在“提供建议”,而是使用会话提供了一个更具体的示例来解释嵌套层次结构如何工作。
  • 好点,@Steven - 我将其添加到问题列表中,并附注指出如果您不实施 IRequiresSessionState 那么您将不会有会话......这意味着没有会话容器。
  • @Steven:它不会是下一个推特。无需扩展到网络场。
猜你喜欢
  • 2011-09-15
  • 1970-01-01
  • 1970-01-01
  • 2012-11-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-19
  • 1970-01-01
相关资源
最近更新 更多