Castle Windsor明确分享依赖关系

时间:2018-03-12 20:50:49

标签: c# .net design-patterns inversion-of-control castle-windsor

如何在不同时间跨组件图共享临时对象?

我有一些来自旧遗留代码的状态引擎。每个州都由IState代表,负责在流程中创建下一个州。

public interface IState
{
    Guid Session { get; }
    IState GetNextState();
}

起始状态由模型初始化:

public class Model
{
    private readonly IStateFactory _stateFactory;

    public Model(IStateFactory stateFactory)
    {
        _stateFactory = stateFactory;
    }

    public IState GetFirstState()
    {
        return _stateFactory.GetStateA();
    }
}

每个州都包含一个会话上下文(简化为仅包含GUID)。

public class Context : IDisposable
{
    public static int CreatedCount = 0;
    public static int DisposedCount = 0;
    //Has other DI injected dependencies.

    public Context()
    {
        CreatedCount++;
    }

    public Guid SessionGuid { get; } = Guid.NewGuid();

    public void Dispose()
    {
        DisposedCount++;
    }
}

已添加“CreatedCount”和“DisposedCount”以帮助演示此问题。请注意,它们是静态整数。

国家的实施可能是这样的:

public class MyState : IState
{
    private readonly Context _context;
    private readonly IStateFactory _stateFactory;

    public MyState(IStateFactory stateFactory, Context context)
    {
        _context = context;
        _stateFactory = stateFactory;
    }

    public Guid Session => _context.SessionGuid;

    public IState GetNextState()
    {
        var nextState = _stateFactory.GetStateB(_context);
        _stateFactory.DestroyState(this);
        return nextState;
    }
}

州工厂是一个简单的Castle Windsor实施的TypedFactory界面。

public interface IStateFactory
{
    IState GetFirstState(); 
    IState GetStateB(Context context);

    void DestroyState(IState state);
}

这个想法是每个“状态”可以基于某个动作启动下一个状态,并且当前状态“上下文”应该在下一个状态中使用。

容器以预期的方式构建:

var container = new WindsorContainer();
container.AddFacility<TypedFactoryFacility>();

container.Register(
    Component.For<Context>().LifestyleTransient(),
    Component.For<IState>().ImplementedBy<MyState>().Named("stateA").LifestyleTransient(),
    Component.For<IState>().ImplementedBy<MyState>().Named("stateB").LifestyleTransient(),
    Component.For<IStateFactory>().AsFactory()
);

基本上,我希望“stateB”获得Context的所有权。但是当我发布“stateA”(通过调用MyState.GetNextState)时,Context被释放并处理掉!我如何告诉Castle.Windsor将所有权转移到下一个州?

var model = container.Resolve<Model>();
var initialState = model.GetFirstState();
var nextState = initialState.GetNextState(); //releases the initial State.

Assert.That(initialState.Session, Is.EqualTo(nextState.Session)); //The context was 'shared' by stateA by passing it into the factory method for stateB.

Assert.That(Context.CreatedCount, Is.EqualTo(1));
Assert.That(Context.DisposedCount, Is.EqualTo(0)); //FAIL! Castle Windsor should see that the shared "Context" was passed into the constructor of modelB, and added a reference to it.

container.Release(model);
container.Release(nextState); //usually done by the model.
Assert.That(Context.CreatedCount, Is.EqualTo(1));
Assert.That(Context.DisposedCount, Is.EqualTo(1)); 

应该注意,状态转换可以从另一个线程启动,但在创建线程上调用。这会扰乱默认的Castle Windsor范围生活方式所使用的CallContext。这适用于桌面应用程序,因此默认WCF和Web请求范围生活方式不适用。

2 个答案:

答案 0 :(得分:1)

使用public partial class MainWindow : Window { public static StatusInfo status; public MainWindow() { InitializeComponent(); SourceInitialized += MainWindow_SourceInitialized; } private void MainWindow_SourceInitialized(object sender, EventArgs e) { SetUpDisplay(); } private void SetUpDisplay() { status = new StatusInfo(); } }

LifestyleScoped

答案 1 :(得分:0)

我已经提出了另一个解决这个问题的方法,这个问题适用于Castle Windsors范围没有的地方。我创造了一种生活方式,只要通过使用引用计数来使用它就能保持单身。一旦引用计数变为0,就释放该对象。

npm i @types/fullcalendar -s

它的用法如下:

public class ReferenceCountedSingleton : AbstractLifestyleManager, IContextLifestyleManager
{
    private readonly object _lock = new object();
    private Burden _cachedBurden;
    private int _referenceCount;

    public override void Dispose()
    {
        var localInstance = _cachedBurden;
        if (localInstance != null)
        {
            localInstance.Release();
            _cachedBurden = null;
        }
    }

    public override object Resolve(CreationContext context, IReleasePolicy releasePolicy)
    {
        lock(_lock)
        {
            _referenceCount++;

            if (_cachedBurden != null)
            {
                Debug.Assert(_referenceCount > 0);
                return _cachedBurden.Instance;
            }

            if (_cachedBurden != null)
            {
                return _cachedBurden.Instance;
            }
            var burden = CreateInstance(context, false);
            _cachedBurden = burden;
            Track(burden, releasePolicy);
            return burden.Instance;
        }
    }

    public override bool Release(object instance)
    {
        lock (_lock)
        {
            if (_referenceCount > 0) _referenceCount--;
            if (_referenceCount > 0) return false;
            _referenceCount = 0;
            _cachedBurden = null;
            return base.Release(instance);
        }
    }

    protected override void Track(Burden burden, IReleasePolicy releasePolicy)
    {
        burden.RequiresDecommission = true;
        base.Track(burden, releasePolicy);
    }

    public object GetContextInstance(CreationContext context)
    {
        return context.GetContextualProperty(ComponentActivator);
    }
}