Castle Windsor瞬态一次性用品

时间:2011-07-21 20:34:19

标签: .net castle-windsor castle

我知道这已被讨论过了令人作呕......但我对Windsor跟踪Transient IDisposable对象的方式存在疑问。

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

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

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

对我来说比看起来更清楚:

MyDisposable instance;
try
{
    instance = factory.GetInstance();
}
finally 
{
    factory.Release(instance);
}

为了真正处理我的实例并使它们符合GC条件,我需要引用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 Transient组件现在将是GC。

如果我想使用TypedFactory,那么我的类可以生成多个类型的实例怎么办?

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

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

好吧,首先,在工厂调用Release将无法在MyDisposable上调用Dispose(),因为MyDisposable未被跟踪....

我如何克服这些困难?

感谢。

2 个答案:

答案 0 :(得分:10)

首先,您如何知道与您未创建的对象相关的解体问题?你无法控制对象的创建,因为你没有自己创建它(工厂为你做了这个)。当您将ioc解析与使用者处理(调用.Dispose而不是factory.Release)结合使用时,您将引入对象知道如何创建它的要求,但它并没有创建自己。请考虑以下示例:

“组件”是您通过容器解决的问题,但您想要自行处理

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()) 
{
   ....  
}
     

对我来说比看起来更清楚:......

使用语句是一个约定,如果你省略它就没有编译时错误,所以从我的观点来看,try / finally with release只是另一个约定,虽然有点冗长。例如,您可以通过创建帮助程序来缩短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不会更改任何内容。通过构造函数获取依赖关系的类没有创建它,因此不负责释放它。

答案 1 :(得分: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>>())

代理钩子只是说我只想代理IDiposable接口方法。该课程如下:

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()