【问题标题】:Create an instance of ISession per ViewModel为每个 ViewModel 创建一个 ISession 实例
【发布时间】:2010-06-25 10:48:53
【问题描述】:

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

  • 卡利本
  • 忍者
  • NHibernate

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

我想关注 ayende's advice 关于 ViewModel:每个 ViewModel 都会打开一个新会话。

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

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

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

提前致谢

迈克

【问题讨论】:

    标签: nhibernate viewmodel ninject caliburn isession


    【解决方案1】:

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

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

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

    【讨论】:

      【解决方案2】:

      我有一个非常相似的项目(除了我没有使用 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。

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

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

      【讨论】:

      • 如果我不使用构造函数注入实例化存储库,但在构造函数中执行类似的操作,这是否有效? this.repository = IoC.ResolveInContext(form.Guid);
      • 如果你的意思是在你的表单上有一个静态的 Guid 属性,那么它应该可以工作。上下文仅在 Ninject 构造对象时适用,因此即使您将相同的 Guid 作为表单上的静态属性传递,每个表单也会获得自己的 ISession。现在我想了想,你不需要传递任何东西,因为上下文只适用于构建期间。 ResolveInContext 可以只设置上下文。然而,这显然不是线程安全的。
      【解决方案3】:

      我们在 unhaddins 中通过 AOP 实现了这一点。 称为“每个业务事务的对话”。

      谷歌搜索

      【讨论】:

        【解决方案4】:
        【解决方案5】:

        嗯,感谢 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();
            });
        

        并且viewmodel的构造函数必须包含所有依赖于ISession的注入依赖:

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

        希望有帮助

        【讨论】:

        • IScreen 是 Caliburn 中视图模型的基本界面
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-03-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-12-27
        • 2013-01-03
        相关资源
        最近更新 更多