Castle Windsor瞬态可释放对象

12

我知道这个问题已经被反复讨论了...但是我对Windsor跟踪短暂的IDisposable对象的方式有问题。

我理解让Windsor管理我的IDisposable对象的好处...但我不喜欢它。

如果我想在using块中包装我的组件会发生什么?程序员会假设资源会在using块结束时得到清理,对吧?错了-Dispose会被调用,但Windsor会保留实例直到显式释放。对于我来说,这很好,因为我知道我在做什么...但是另一个开发人员要编写一个类并想要像通常使用其他IDisposable一样在using块中使用IDisposable呢?

using(factory.CreateInstance()) 
{
   ....  
}

对我来说,看起来比:

MyDisposable instance;
try
{
    instance = factory.GetInstance();
}
finally 
{
    factory.Release(instance);
}
为了真正地处理我的实例并使它们有资格进行垃圾回收,我需要引用WindsorContainer或使用一个公开释放方法的类型化工厂。这意味着使用IDisposable组件的唯一可接受方式是使用类型化工厂。在我看来,这并不好...如果有人将IDisposable接口添加到现有组件中,那该怎么办呢?每个希望注入该组件的地方都需要更改。在我看来,这真的很糟糕。(当然,在非DI场景下,也需要更改以调用Dispose...但是使用Windsor时,每个地方都需要更改以使用类型化工厂,这是一次更大的变化)。
好的,可以使用自定义ReleasePolicy,怎么样?
public class CustomComponentsReleasePolicy : AllComponentsReleasePolicy
{
    public override void Track(object instance, Burden burden)
    {
        if (burden.Model.LifestyleType == LifestyleType.Pooled) 
            base.Track(instance, burden);
    }
}

好的,太棒了,我的IDisposable瞬态组件现在将被垃圾回收。

如果我想使用TypedFactory让我的类生产一个类型的多个实例怎么办?

public interface IMyFactory 
{
    MyDisposable GetInstance();
    void Release(MyDisposable instance);
}

[Singleton]
public class TestClass
{
    public TestClass(IMyFactory factory) { }
}

首先,调用工厂的Release方法不会对MyDisposable调用Dispose()方法,因为MyDisposable没有被跟踪......

我该如何克服这些困难呢?

谢谢。


1
我想我需要写一篇博客来记录这件事。 - Krzysztof Kozmic
2个回答

12
首先,您如何知道与未创建的对象相关的去除关注点? 您无法控制对象的创建,因为您没有自己创建它(工厂为您完成了这项工作)。 当您将ioc解析与使用者处理(调用.Dispose而不是factory.Release)相结合时,您正在引入要求您的对象知道它是如何创建的,但它并没有自己创建。 请考虑以下示例:“Component”是您通过容器解析的内容,但您希望自己处理其处置。
public class Component : IDisposable
{
    private readonly IAmSomething _something;

    public Component(IAmSomething something)
    {
        _something = something;
    }

    public void Dispose()
    {
        // Problem 1: the component doesnt know that an implementation of IAmSomething might be disposable
        // Problem 2: the component did not create "something" so it does not have the right to dispose it
        // Problem 3: What if an implementation of "something" has a depenency on a disposable instance deep down in its object graph?

        // this is just bad..
        IDisposable disposable = _something as IDisposable;

        if (disposable != null)
            disposable.Dispose();

    }
}

public interface IAmSomething
{

}

public class SomethingA : IAmSomething
{

}

public class SomethingB : IAmSomething, IDisposable 
{
    public void Dispose()
    {
    }
}

如上所示,退役可能很复杂,我简单地看不到我如何优雅地处理这个问题,特别是当温莎为我做这件事时。如果你的代码库中充斥着服务定位器反模式(http://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/),我可以看出这成为一个问题(我并不是说你的代码是),但那时你真的有更大的问题。
using(factory.CreateInstance()) 
{
   ....  
}

looks much clearer to me than: …

使用using语句是一种约定,如果省略它,编译时不会出现错误,所以从我的角度来看,使用try/finally与释放资源只是另一种约定,尽管稍微冗长一些。你可以通过创建帮助程序来缩短try/finally,例如:

[TestFixture]
public class SomeCastleTests
{
    [Test]
    public void Example()
    {
        var container = new WindsorContainer();

        // you would of course use a typed factory instead in real word

        using (var usage = new ComponentUsage<IAmSomething>(container.Resolve<IAmSomething>, container.Release))
        {
            // use..
            usage.Component
        }
    }
}

public class ComponentUsage<T> : IDisposable where T : class
{
    private Func<T> _create;
    private Action<T> _release;

    private T _instance;

    public ComponentUsage(Func<T> create, Action<T> release)
    {
        _create = create;
        _release = release;
    }

    public T Component
    {
        get
        {
            if (_instance == null)
                _instance = _create();

            return _instance;
        }
    }

    public void Dispose()
    {
        if (_instance != null)
        {
            _release(_instance);
            _instance = null;
        }
    }
}

在我看来,这并不好...如果有人给现有组件添加IDisposable接口,那么每个需要注入该组件的地方都需要更改。

我不理解这个陈述。一个组件何时释放和何时需要它是两个不同的问题,如果一个组件作为构造函数的依赖项提供,添加IDisposable并不会改变任何东西。通过构造函数获取依赖项的类没有创建它,因此不负责释放它。


0
作为一个附加功能,您可以通过创建一个拦截器来解决您提到的一些其他问题,该拦截器会为您释放对象。我在处理工作单元时使用这种方法。拦截器的代码如下:
public class UnitOfWorkReleaseOnDispose : IInterceptor
{
    private readonly IUnitOfWorkFactory unitOfWorkFactory;

    public UnitOfWorkReleaseOnDispose(IUnitOfWorkFactory unitOfWorkFactory)
    {
        this.unitOfWorkFactory = unitOfWorkFactory;
    }

    public void Intercept(IInvocation invocation)
    {
        invocation.Proceed();
        this.unitOfWorkFactory.DestroyUnitOfWork((IUnitOfWork)invocation.Proxy);
    }
}

我的注册码看起来像这样:

            Component.For<IUnitOfWork>()
                .ImplementedBy<TransactionalUnitOfWork>()
                .LifestyleTransient()
                .Interceptors<UnitOfWorkReleaseOnDispose>()
                    .Proxy.Hook(h => h.Service<InterfaceMethodsOnlyProxyGenerationHook<IDisposable>>())

代理钩子只是为了表明我只想代理IDisposable接口方法。该类看起来像这样:
public class InterfaceMethodsOnlyProxyGenerationHook<TInterface> : IProxyGenerationHook
{
    public void MethodsInspected()
    {

    }

    public void NonProxyableMemberNotification(Type type, System.Reflection.MemberInfo memberInfo)
    {

    }

    public bool ShouldInterceptMethod(Type type, System.Reflection.MethodInfo methodInfo)
    {
        return typeof(TInterface) == type;
    }
}

这也需要注册,因为我正在使用重载的挂钩函数:

            Component.For<IProxyGenerationHook>()
                .ImplementedBy<InterfaceMethodsOnlyProxyGenerationHook<IDisposable>>()
                .LifestyleSingleton()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接