为每个 ViewModel 创建一个 ISession 实例

发布于 2024-09-06 20:17:19 字数 539 浏览 7 评论 0原文

这是我的问题:我正在使用以下工具构建一个桌面应用程序:

  • Caliburn
  • Ninject
  • NHibernate

我所有的视图模型和存储库都是用 Ninject 实例化的。我的存储库都需要在其构造函数中使用 ISession。

我想遵循ayende关于ViewModel的建议:每个ViewModel打开一个新会话。

是否可以配置 Ninject 在创建 ViewModel 时打开一个新会话,并在该视图模型使用的存储库中使用此会话?

我查看了 Ninject 的 InScope 函数,以及 NHibernate 中的 ICurrentSessionContext 接口,但我不知道如何对所有这些进行建模以获得我想要的东西......

以前有人做过类似的东西吗?

预先感谢

迈克

here is my problem: I'm building a desktop application, with the following tools:

  • Caliburn
  • Ninject
  • NHibernate

All my view models and repositories are instanciated with Ninject. My repositories all need an ISession in their constructor.

I'd like to follow ayende's advice concerning the ViewModels: each ViewModel opens a new session.

Is it possible to configure Ninject to open a new session when a ViewModel is created, and use this session inside the repositories used by this view model?

I had a look to the InScope function of Ninject, as well as the ICurrentSessionContext interface in NHibernate, but I don't know how to model all of that to get what I want...

Did someone make something like that before?

Thanks in advance

Mike

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

ヤ经典坏疍 2024-09-13 20:17:19

我利用 ViewModel 生命周期解决了类似的场景:我创建了一个由存储库实现的 ISessionAware 接口(使用 SetSession 方法),然后通过 ViewModel 的 OnInitialize 方法中的 ISessionAware 初始化存储库(当虚拟机启动时,该方法由 Caliburn 强制执行)由 ScreenConductor 管理)。

使用反射来检查保存存储库的属性,我可以将所有基础设施放在 BaseDataVM 类上。

我认为在容器中使用范围会更优雅,但我不知道 Ninject。

I solved a similar scenario leveraging the ViewModel lifecycle: I created an ISessionAware interface (with a SetSession method) to be implemented by repositories, then I initialized the repositories through ISessionAware in the OnInitialize method of the ViewModel (which is enforced by Caliburn when the VM is managed by a ScreenConductor).

Using reflection to inspect the properties holding the repositories, I could put all the infrastructure on a BaseDataVM class.

Using a scope in the container would be more elegant, I think, but I don't know Ninject.

故事还在继续 2024-09-13 20:17:19

我有一个非常相似的项目(除了我没有使用 Caliburn)并且也一直在尝试找出如何做到这一点。我确实想出了一种非常适合使用 Ninject 的 InScope() 方法进行构造函数注入的方法。

我有一个名为 IoC 的静态类,它包装了对 Ninject 内核的访问。由于依赖项全部注入到构造函数中,因此上下文仅在创建对象时才相关。因此,为上下文提供的内容并不重要,但 Guid 感觉是安全的选择。 Program.OpenSession() 是打开新 ISession 的静态方法。

public static class Ioc
{
    private static readonly IKernel _kernel;

    static IoC()
    {
        _kernel = new StandardKernel();
        _kernel.Load(new ContextModule());
    }

    private static object _context;

    public static T ResolveInContext<T>(object context)
    {
        _context = context;
        var result = _kernel.Get<T>();
        _context = null;
        return result;
    }

    private class ContextModule : NinjectModule
    {
        public override void Load()
        {
            Bind<ISession>().ToMethod(x => Program.OpenSession()).InScope(x => _context);
            Bind<frmCompanyViewer>().ToSelf().InScope(x => _context);
        }
    }
}

用法是:

var frm = IoC.ResolveInContext<frmCompanyViewer>(Guid.NewGuid());

表单的构造函数签名是:

public frmCompanyViewer(ISession session, ICompanyRepository companyRepository)

我通过绑定上的 InScope 验证,用于构造 frmCompanyViewer 的同一个 ISession 也用于构造 companyRepository。如果我删除 InScope,则会使用两个 ISession。

编辑添加:这也可以,请参阅评论。对于实际应用程序来说,这应该是线程安全的。我将方法名称更改为 ConstructInContext 以澄清上下文仅在对象构造期间适用。

    public static T ConstructInContext<T>()
    {
        _context = Guid.NewGuid();
        var result = _kernel.Get<T>();
        _context = null;
        return result;
    }

I have a very similar project (except I'm not using Caliburn) and have been trying to figure out how to do this as well. I did come up with one method that works well for constructor injection using Ninject's InScope() method.

I have a static class called IoC that wraps access to Ninject's kernel. Since the dependencies are all injected into the constructor, the context is only relevant when the object is being created. So it doesn't matter what is supplied for context, but a Guid feels like the safe choice. Program.OpenSession() is a static method to open a new ISession.

public static class Ioc
{
    private static readonly IKernel _kernel;

    static IoC()
    {
        _kernel = new StandardKernel();
        _kernel.Load(new ContextModule());
    }

    private static object _context;

    public static T ResolveInContext<T>(object context)
    {
        _context = context;
        var result = _kernel.Get<T>();
        _context = null;
        return result;
    }

    private class ContextModule : NinjectModule
    {
        public override void Load()
        {
            Bind<ISession>().ToMethod(x => Program.OpenSession()).InScope(x => _context);
            Bind<frmCompanyViewer>().ToSelf().InScope(x => _context);
        }
    }
}

Usage is:

var frm = IoC.ResolveInContext<frmCompanyViewer>(Guid.NewGuid());

The form's constructor signature is:

public frmCompanyViewer(ISession session, ICompanyRepository companyRepository)

I verified that with InScope on the bindings, the same ISession that is used to construct frmCompanyViewer is also used to construct companyRepository. If I remove InScope then two ISessions are used.

Edited to add: This would also work, see comments. This should be made thread safe for a real application. I changed the method name to ConstructInContext to clarify that the context only applies during object construction.

    public static T ConstructInContext<T>()
    {
        _context = Guid.NewGuid();
        var result = _kernel.Get<T>();
        _context = null;
        return result;
    }
绝不放开 2024-09-13 20:17:19

我们在 Unhaddins 中通过 AOP 实现了这一点。
称为“每笔业务交易的对话”。

在谷歌中搜索

We have this with AOP, in unhaddins.
Is called "Conversation per Business Transaction".

search in google

心是晴朗的。 2024-09-13 20:17:19

好吧,感谢 ninject 小组,我找到了解决方案。

这里的解决方案是在绑定ISession时使用函数InScope,并在IContext变量中浏览以检查服务。如果请求层次结构中的一项服务可分配给我的视图模型的基类,我将使用上下文作为范围。

因此,第一次将 ISession 注入到 ViewModel 的构造函数中时,将使用一个新范围。并且 ViewModel 构造函数内对 ISession 的所有后续调用都将在相同范围内解析。然后只为我的 ViewModel 创建一个会话。

这是代码:

Bind<ISession>().ToMethod(ctx =>
    {
        var session = ctx.Kernel.Get<INHibernateSessionFactoryBuilder>()
            .GetSessionFactory()
            .OpenSession();

        session.FlushMode = FlushMode.Commit;

        return session;
    })
    .InScope(ctx =>
    {
        var request = ctx.Request;

        if (request.Service is IScreen)
            return request;

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

        return new object();
    });

并且视图模型的构造函数必须包含依赖于 ISession 的所有注入依赖项:

[Inject]
public PlayersManagementViewModel(ISession session, IPlayersRepository playersRepository)
{
}

希望有帮助

Well, I've found a solution thanks to the ninject group.

The solution here is to use the function InScope when I bind ISession, and browse in the IContext variable to inspect the services. If one service in the request hierarchy is assignable to the base class of my view models, I use the context as scope.

So the first time an ISession will be injected in the constructor of my ViewModel, a new scope is used. And all subsequent calls to ISession inside the constructor of the ViewModel will be resolved with the same scope. And then only one session is created for my ViewModel.

Here is the code:

Bind<ISession>().ToMethod(ctx =>
    {
        var session = ctx.Kernel.Get<INHibernateSessionFactoryBuilder>()
            .GetSessionFactory()
            .OpenSession();

        session.FlushMode = FlushMode.Commit;

        return session;
    })
    .InScope(ctx =>
    {
        var request = ctx.Request;

        if (request.Service is IScreen)
            return request;

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

        return new object();
    });

And the constructor of the viewmodel must contains all the injected dependencies which rely on the ISession:

[Inject]
public PlayersManagementViewModel(ISession session, IPlayersRepository playersRepository)
{
}

Hope that helps

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文