【问题标题】:Unity 2.0 and handling IDisposable types (especially with PerThreadLifetimeManager)Unity 2.0 和处理 IDisposable 类型(尤其是使用 PerThreadLifetimeManager)
【发布时间】:2011-07-05 00:38:52
【问题描述】:

我知道类似的问题被问了好几次(例如:herehereherehere)但它是针对以前版本的 Unity,其答案取决于使用的 LifetimeManager类。

文档说:

Unity 使用继承的特定类型 来自 LifetimeManager 基类 (统称为寿命 经理)来控制它如何存储 对对象实例的引用以及如何 容器处理这些 实例。

好的,听起来不错,所以我决定检查内置生命周期管理器的实现。我的结论:

  • TransientLifetimeManager - 不处理处置。容器只解析实例,它不跟踪它。调用代码负责处理实例。
  • ContainerControlledLifetimeManager - 在处置生命周期管理器时处置实例(= 当处置容器时)。提供在层次结构中的所有容器之间共享的单例实例。
  • HierarchicalLifetimeManager - 从 ContainerControlledLifetimeManager 派生行为。它为层次结构(子容器)中的每个容器提供“单例”实例。
  • ExternallyControlledLifetimeManager - 不处理处置。正确的行为,因为容器不是实例的所有者。
  • PerResolveLifetimeManager - 不处理处置。它通常与TransientLifetimeManager 相同,但它允许在解析整个对象图时重用实例进行依赖注入。
  • PerThreadLifetimeManager - 不处理处置,如 MSDN 中所述。 谁负责处置?

内置PerThreadLifetimeManager的实现是:

public class PerThreadLifetimeManager : LifetimeManager
{
    private readonly Guid key = Guid.NewGuid();
    [ThreadStatic]
    private static Dictionary<Guid, object> values;

    private static void EnsureValues()
    {
        if (values == null)
        {
            values = new Dictionary<Guid, object>();
        }
    }

    public override object GetValue()
    {
        object result;
        EnsureValues();
        values.TryGetValue(this.key, out result);
        return result;
    }

    public override void RemoveValue()
    { }

    public override void SetValue(object newValue)
    {
        EnsureValues();
        values[this.key] = newValue;
    }
}

因此,处置容器不会处置使用此生命周期管理器创建的一次性实例。线程完成也不会处理这些实例。那么谁负责发布实例呢?

我尝试在代码中手动处理已解析的实例,但发现了另一个问题。我无法拆除实例。生命周期管理器的 RemoveValue 是空的 - 一旦创建了实例,就不可能从线程静态字典中删除它(我也怀疑 TearDown 方法什么都不做)。因此,如果您在处置实例后调用Resolve,您将得到处置实例。我认为当将此生命周期管理器与线程池中的线程一起使用时,这可能是一个很大的问题。

如何正确使用这个生命周期管理器?

此外,此实现通常在自定义生命周期管理器中重用,例如 PerCallContext、PerHttpRequest、PerAspNetSession、PerWcfCall 等。只有线程静态字典被其他构造替换。

我是否正确理解处理一次性对象取决于生命周期管理器?所以应用程序代码依赖于使用的生命周期管理器。

我读到在其他处理临时一次性对象的 IoC 容器中由子容器处理,但我没有找到 Unity 的示例 - 它可能可以使用本地范围的子容器和 HiearchicalLifetimeManager 处理,但我不确定如何去做吧。

【问题讨论】:

  • 有可能通过实现自定义生命周期让 Unity 确定性地实现处置行为,但是这里解释起来太复杂了。在我的书中,我使用了超过六页来解释如何做到这一点:affiliate.manning.com/idevaffiliate.php?id=1150_236
  • @Mark:很遗憾你的书将在 8 月出版,我不喜欢 MEAP。
  • 另一个有趣的点(或设计疏忽)是在内部注册UnityContainer 会在你处理它时抛出 StackOverflowException。您需要使用ExternallyControlledLifetimeManager 来防止容器尝试调用它自己的 Dispose 方法。
  • @RichardDingwall 你不应该需要自己注册它,它已经可以默认解析IUnityContainer

标签: c# .net inversion-of-control unity-container idisposable


【解决方案1】:

只有少数情况下 Unity 会处理实例。它真的不受支持。我的解决方案是一个自定义扩展来实现这一点 - http://www.neovolve.com/2010/06/18/unity-extension-for-disposing-build-trees-on-teardown/

【讨论】:

    【解决方案2】:

    查看 Unity 2.0 源代码,它闻起来像是 LifetimeManagers 用于以不同方式将对象保持在范围内,因此垃圾收集器不会摆脱它们。例如,使用 PerThreadLifetimeManager,它将使用 ThreadStatic 来保存对具有该特定线程生命周期的每个对象的引用。但是,在容器被 Disposed 之前它不会调用 Dispose。

    有一个 LifetimeContainer 对象用于保存所有创建的实例,然后在 UnityContainer 被 Disposed 时被 Disposed(反过来,它会按时间倒序在其中 Dispose 所有 IDisposables)。

    编辑:仔细检查后,LifetimeContainer 仅包含 LifetimeManagers(因此名称为“Lifetime”Container)。因此,当它被处置时,它只处置生命周期管理器。 (我们面临着已经讨论过的问题)。

    【讨论】:

    • 根据我的调查,只有 ContainerControlled 和 Hierarchical 生命周期管理器 Dispose 解决了实例:ladislavmrnka.com/2011/03/unity-build-in-lifetime-managers
    • @Ladislav 你是对的......我进一步查看了代码并意识到我对 LifetimeManager 的实际内容有一个错误的假设。
    【解决方案3】:

    我最近在我的应用程序中使用 Unity 时遇到了这个问题。在我看来,我在 Stack Overflow 和其他网上找到的解决方案似乎并没有以令人满意的方式解决这个问题。

    在不使用 Unity 时,IDisposable 实例具有易于理解的使用模式:

    1. 在小于函数的范围内,将它们放入 using 块中以“免费”处理。

    2. 为类的实例成员创建时,在类中实现 IDisposable 并将清理放在 Dispose() 中。

    3. 当传递给类的构造函数时,什么都不做,因为 IDisposable 实例在其他地方拥有。

    Unity 使事情变得混乱,因为当依赖注入正确完成时,上面的案例 #2 就消失了。应该注入所有依赖项,这意味着基本上没有类将拥有正在创建的 IDisposable 实例的所有权。但是,它也没有提供“获取”在 Resolve() 调用期间创建的 IDisposables 的方法,因此似乎无法使用 using 块。还剩下什么选择?

    我的结论是Resolve() 接口本质上是错误的。仅返回请求的类型和泄漏需要特殊处理(如 IDisposable)的对象是不正确的。

    作为回应,我为 Unity 编写了 IDisposableTrackingExtension 扩展,它跟踪在类型解析期间创建的 IDisposable 实例,并返回一个一次性包装对象,其中包含所请求类型的实例和对象图中的所有 IDisposable 依赖项。

    使用此扩展,类型解析看起来像这样(此处使用工厂显示,因为您的业务类不应该将 IUnityContainer 作为 依赖):

    public class SomeTypeFactory
    {
      // ... take IUnityContainer as a dependency and save it
    
      IDependencyDisposer< SomeType > Create()
      {
        return this.unity.ResolveForDisposal< SomeType >();
      }
    }
    
    public class BusinessClass
    {
      // ... take SomeTypeFactory as a dependency and save it
    
      public void AfunctionThatCreatesSomeTypeDynamically()
      {
        using ( var wrapper = this.someTypeFactory.Create() )
        {
          SomeType subject = wrapper.Subject;
          // ... do stuff
        }
      }
    }
    

    这协调了上面的 IDisposable 使用模式 #1 和 #3。普通类使用依赖注入;他们不拥有注入的 IDisposables,因此他们不会处理它们。执行类型解析(通过工厂)的类,因为它们需要动态创建的对象,这些类是所有者,并且此扩展提供了管理处置范围的工具。

    【讨论】:

      【解决方案4】:

      使用 HttpContext.Current.ApplicationInstance.EndRequest 事件挂钩到请求的末尾,然后处理存储在此生命周期管理器中的对象是否是一个可行的解决方案?像这样:

      public HttpContextLifetimeManager()
      {
          HttpContext.Current.ApplicationInstance.EndRequest += (sender, e) => {
              Dispose();
          };
      }
      
      public override void RemoveValue()
      {
          var value = GetValue();
          IDisposable disposableValue = value as IDisposable;
      
          if (disposableValue != null) {
              disposableValue.Dispose();
          }
          HttpContext.Current.Items.Remove(ItemName);
      }
      
      public void Dispose()
      {
          RemoveValue();
      }
      

      您不必像其他解决方案那样使用子容器,并且用于处置对象的代码仍然在生命周期管理器中。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-11-22
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多