如何使用LifestyleScoped实现Castle Windsor IScopeAccessor以提供每个ViewModel的NHibernate会话

时间:2012-12-20 16:55:52

标签: nhibernate castle-windsor session-management

我正在寻找帮助创建IScopeAccessor的实现,或寻找新的解决方案,这将允许我为每个ViewModel提供NHibernate会话。

我知道Windsor现在支持scoped生活方式(here)。但是,该示例使用using块创建特殊范围,并在使用中调用container.resolve。

    _container.Register(Component.For<A>().LifestyleScoped());

    using (_container.BeginScope())
    {
        var a1 = _container.Resolve<A>();
        var a2 = _container.Resolve<A>();
        Assert.AreSame(a1, a2);
    }

我无法想到一种方法来使这项工作,因为我不想传递容器,我希望将范围绑定到创建的ViewModel,这将在需要时动态发生。

作为替代方案,看起来我可以创建一个IScopeAccessor的实现,根据KrzysztofKoźmic(here)允许我

  
    

“......提供你喜欢的任何范围。范围是一个抽象的术语,它可以是任何东西。”

  

不幸的是,我发现IScopeAccessor的实现不是特定于基于Web的场景,我很难理解我需要做什么才能将“任何东西”变成有效的范围。

我找到了一个使用Ninject确切想要做的事情的例子(http://www.emidee.net/index.php/2010/08/23/ninject-use-one-database-session-per-视图模型/):

Bind<ISession>().ToMethod(ctx =>
{
    var session = ctx.Kernel.Get<....>().BuildSessionFactory().OpenSession();
    return session;
})
.InScope(context =>
{ 
    var request = context.Request;

    if (typeof(IViewModel).IsAssignableFrom(request.Service))
        return request; 

    while ((request = request.ParentRequest) != null)
        if (typeof(IViewModel).IsAssignableFrom(request.Service))
            return request;

    return new object(); 
});

在Ninject中,InScope指示只要回调返回的对象保持活动状态,就应该重用绑定创建的任何实例。本质上,此回调返回根级别ViewModel(因为ViewModel可以嵌套)。

关于如何使用Windsor做同样的事情或获得相同结果的任何想法?

1 个答案:

答案 0 :(得分:1)

问题似乎是创造的地方。 如果所有关于正在构建的视图模型的依赖关系,你可以使用boud生活方式,如What's new...中所述 或者您也可以使用自己的范围访问器,它对视图模型很敏感。例如:

public class ViewModelScopeAccessor : IScopeAccessor
{
    private IDictionary<Guid, ILifetimeScope> scopes = new Dictionary<Guid, ILifetimeScope>();
    private ILifetimeScope defaultScope;

    public ViewModelScopeAccessor()
        : this(new DefaultLifetimeScope())
    { }

    public ViewModelScopeAccessor(ILifetimeScope defaultScope)
    {
        this.defaultScope = defaultScope;
    }

    public ILifetimeScope GetScope(CreationContext context)
    {
        var creator = context.Handler.ComponentModel.Implementation;
        var viewModel = creator as IViewModel;
        if (viewModel != null)
        {
            ILifetimeScope scope;
            if (!scopes.TryGetValue(viewModel.UID, out scope))
            {
                scope = new DefaultLifetimeScope();
                scopes[viewModel.UID] = scope;
            }

            return scope;
        }
        else
        {
            return defaultScope;
        }
    }

    public void Dispose()
    {
        foreach (var scope in scopes)
        {
            scope.Value.Dispose();
        }

        defaultScope.Dispose();
        scopes.Clear();
    }
}

用于以下viewmodel接口:

public interface IViewModel
{
    string DisplayName { get; }

    Guid UID { get; }
}

你当然可以用其他方式比较视图模型,这只是一个例子。

绑定生活方式和范围访问器这两者的缺点是,如果在viewmodel中使用类型化工厂,它将无法工作,懒惰地构造对象,因为范围访问者不知道,从中对象/方法调用其工厂方法。但我认为这是一个普遍的.NET问题,因为一个方法确实从来不知道它从哪里被调用。

因此,您可以使用自己的工厂,每个工厂实例只生成一个实例,并将它们作为视图模型的范围。

希望这有帮助。