C# 如何在n层体系结构中解决此NHibernate查询?

C# 如何在n层体系结构中解决此NHibernate查询?,c#,nhibernate,onion-architecture,C#,Nhibernate,Onion Architecture,我试图将NHibernate与我的服务层分离,这让我遇到了麻烦。我的架构如下所示: web->services->repositories->nhibernate->db 我希望能够从我的服务层和可能的web层生成nhibernate查询,而这些层不知道它们正在处理什么orm。目前,我在我的所有存储库上都有一个find方法,该方法采用了IList标准。这允许我传入一个条件列表,例如newobject(){“Username”,usernameviable}在我的体系结构中的任意位置。NHiber

我试图将NHibernate与我的服务层分离,这让我遇到了麻烦。我的架构如下所示:

web->services->repositories->nhibernate->db

我希望能够从我的服务层和可能的web层生成nhibernate查询,而这些层不知道它们正在处理什么orm。目前,我在我的所有存储库上都有一个find方法,该方法采用了
IList标准
。这允许我传入一个条件列表,例如
newobject(){“Username”,usernameviable}在我的体系结构中的任意位置。NHibernate接受这一点,并创建一个新的Criteria对象,并添加传入的Criteria。这对于我的服务层的基本搜索很好,但我希望能够传入一个查询对象,我的存储库将其转换为NHibernate标准


真的,我很想实现这个问题中描述的东西:。我只是没有找到任何关于如何实现这样的东西的好资源。该问题中描述的方法是一种好方法吗?如果是这样的话,有谁能提供一些关于如何实现这种解决方案的建议吗?

抽象出ORM将:

  • 带来了大量重新定义API的工作
  • 使优化/批处理数据库访问变得不可能
  • 使理解执行什么查询变得更加困难
  • 将导致吨的选择N+1
所有这些都没有什么价值:交换ORM框架的模糊选项,很可能会有很多其他问题

  • 缺少的功能
  • 执行上的细微差别
  • 学习曲线
更新:经验

我曾经参与实现现有DAL抽象的新提供者。它的性能很差,引入了很多bug,错误处理非常混乱,有时使用陈旧的数据,因为应用程序采用默认实现。原因:

  • 缓存不知道上下文
  • CacheImplementation具有不同的语义
  • 批处理API差异太大,无法抽象
  • 错误特定于实现(例如,FileNotFound->FilesearchDialog对于基于tcp/ip的数据库是不可用的)
  • 错误恢复是不同的(每个实现都有自己的错误集,可以从中恢复)
  • 锁定机制不同
  • SQL数据库中没有一致的更改事件
  • 嵌套事务
  • 默认实现在模型类中溢出
  • 重新实现所有抽象查询需要大量工作,并引入了大量复制粘贴错误
  • 在不同的实现中,不明确说明顺序的查询将返回不同的排序结果
对应用程序进行了大量重构:

  • 剥离功能只有一个实现提供
  • 每个实现的缓存管理
  • 瞬态数据导致的包装器标识问题
  • 很难在两个数据存储上实现查询
其他要点:

  • 通过抽象DAL的数据迁移非常缓慢
  • 由于上述问题,实施另一个实施将永远不会发生,因为成本太高(在上述场景中,我们开始缓慢地重新实施整个项目)
  • 实现DALAPI的正确语义非常困难,因为纯API中没有使用上下文
在我看来,移植(业务任务)的痛苦要小得多,因为性能的原因,我们这样做了几年

更新2:体验2:尝试从NHibernate移植到EntityFramework时遇到的障碍(与NH合并,但无法在合理时间内与EF 4合并)

  • 嵌套事务
  • 枚举支持
  • 使用compositeId的引用(如何摆脱引用ID)
  • 组件中的引用
  • 阅读批处理(期货),这是方便的网页+计数在一次去
  • 映射CultureInfo(IUserType支持)

    • 感谢您的回复!我明白你的意思,但这些答案并不能解决我的问题。由于我正在编写的系统的状态,我无法更改我的体系结构。相反,我只是将所有sql/hql/criteria api查询保留在存储库层中,而不是试图向我的服务公开某种复杂的查询类。这种方法应该很管用。对于我的下一个架构方法,我会考虑其他答案中的要点。

      为什么你要抽象和隐藏NHiBiNATE?您应该阅读并重新考虑您的设计我将添加以使此答案完整您真的应该提供一些
      cons
      来避免抽象ORM,例如PITA测试
      testlist.ForEach(e=>session.Save(e));session.Flush();session.Clear()
      var mock=new Moq().Call(s=>s.GetSomeThing(…).Returns(testlist).Object第一个是否命中数据库?我并不想成为一个皮塔人,但我很好奇你是如何处理这件事的……这里没有皮塔人,我很高兴向别人学习。我使用的是
      SqliteConfiguration.Standard.InMemory()
      ,每个测试都有自己的会话。这会影响数据库,但由于所有测试都有自己的内存测试,所以我在速度和隔离方面没有问题。查询测试可以共享同一会话。默认/测试数据可以由您编写一次的方法注入,也可以为每个测试数据注入。谢谢,我见过这种方法,但似乎永远无法在测试设置中正确导出模式,最终不得不手动处理生成的sql。