没有 LINQ 扩展的 NHibernate 通用存储库?

Posted

技术标签:

【中文标题】没有 LINQ 扩展的 NHibernate 通用存储库?【英文标题】:NHibernate generic repository without LINQ extension? 【发布时间】:2013-11-04 12:55:12 【问题描述】:

我正在寻找几天前的答案,但没有结果。

我正在尝试使用 NHibernate 为通用存储库实现 IRepository 接口。界面如下:

public interface IRepository<TEntity, TKey> where TEntity : BaseEntity

    TEntity GetById(TKey id);
    void Insert(TEntity entidad);
    void Update(TEntity entidad);
    void Delete(TEntity entidad);
    IQueryable<TEntity > Table  get; ;

我知道 ISession 接口有一个扩展,它可以在我的查询中使用 LINQ,但我也知道该插件不能 100% 工作(我测试了它,但是当我尝试执行一些复杂的查询,它thwows未实现的异常,所以我不能使用那个插件)

我 100% 使用 ISession 中的 QueryOver 函数,所以.. 你知道将 IQueroOver 函数实现为 IQueryable 的任何方法吗(不启动“SELECT *”查询)?如果没有,是否还有其他想法可以在不使用 LINQ 扩展的情况下实现通用存储库模式?

非常感谢

【问题讨论】:

你好 James,Query 函数是 NHibernate.Linq 扩展的另一个函数 我不能使用它的原因是因为一些join函数还没有被Linq扩展支持,我需要它 LEFT OUTER JOINs 在不相关的实体上不受支持,这是真的。我不知道将QueryOver API 转换为 LINQ 提供程序 API 的方法(据我所知,您不能这样做),因此您要么必须使用 LINQ 提供程序并仔细解决不支持的问题功能或公开IQueryOver&lt;T,T&gt;。作为旁注,我认为大约 90% 的时间并不真正需要存储库模式...... 【参考方案1】:

没有这种事

每个人对 100% 实施的 LINQ 提供程序应具有的功能都有自己的定义。 人们可能期望 LINQ 提供程序能够将String.Equals(String, String, StringComparison) 转换为使用正确排序规则的 SQL 表达式,但除非创建 LINQ 提供程序的程序员对其进行编程,否则它将无法做到这一点。 我们可能希望 LINQ 提供程序能够做的事情没有尽头,因此每个 LINQ 提供程序都有限制。 对于 LINQ 提供程序,没有 100% 这样的事情。 这是一个高不可攀的概念。 它是无限的。

因此,重要的是......

    了解您的 LINQ 提供程序的限制,并且 知道如何解决这些限制。

限制

对于 NHibernate,了解限制比它应该做的要困难一些,因为目前没有 NHibernate LINQ 提供程序的官方文档。 (NH-2444,欢迎使用补丁!) 但是,只要知道这个 LINQ 提供程序是建立在 HQL 之上的,您就可以弄清楚其中的一部分,因此 HQL 的任何限制,LINQ 也会有。

没有 NHibernate 查询语法直接支持在不相关的列上执行任意左外连接(当然,除了 Native SQL)。 NHibernate 中的查询被设计成执行连接的所有信息都已经存储在映射中。 之后,您所要做的就是指定关系,例如order.Customer,NHibernate 就知道将哪些表和列连接在一起。您应该能够使用 HQL 或 LINQ 进行任意 cross 连接,然后您可以添加 where 子句使其表现得像内连接,但没有左外连接。

由于没有一个查询语法直接支持您想要执行的操作,因此构建一个 LINQ/QueryOver 包装器(与现有的 LINQ/HQL 包装器相对,并且实施起来可能同样复杂)将一无所获。 无论您使用的是 LINQ 还是 QueryOver,您都必须采取相同的步骤来处理此查询。

解决方法

由于 LINQ 提供程序不可能是完整的,因此设计良好的 LINQ 提供程序必须是可扩展的。 这样,如果 LINQ 提供程序本身不支持您需要的功能,它不会毁了您的一天。 NHibernate LINQ 提供程序可以扩展。 请参阅Giorgetti Alessandro's blog 了解更多信息。

当然,在我们开始编写 LINQ 提供程序扩展之前,我们应该检查一下是否有更简单的方法来解决问题。 我想到了几种可能性:

处理此问题的最简单方法是简单地将关系添加到您的 NHibernate 映射,使您能够在 NHibernate 查询(LINQ 或其他)中利用它。 如果两列的数据类型不兼容,或者需要进行某种操作才能执行连接,请考虑修复数据和架构以实现更平滑的映射。

另一种选择是通过子查询获得创意或使用多个查询来模拟您想要的连接会做什么。例如,查看我的other answers 之一:

...这个 LEFT OUTER JOIN 可以通过组合两个单独查询的结果来模拟 - 一个是获取订单...

var orders = session.Query<OrderHeader>()
    .Fetch(x => x.CreatedBy);

...还有一个是为了得到没有命令的人:

var peopleWithNoOrders = session.Query<Person>()
    .Where(p => !session.Query<OrderHeader>().Any(o => o.CreatedBy == p));

... 因为 left outer join 等同于 inner join 加上不匹配的附加结果。我不知道您尝试执行的加入的详细信息,但希望这会给您提供足够的想法来开始。

【讨论】:

如果有错误请纠正我。对于左外连接,NHibernate 不提供联合功能。因此,您必须获取数据(通过 ToList() 之类的方法),以便您可以使用 Linq-to-object 来操作数据。由于联合支持,您不能使用 Linq-to-NHibernate 提供的代理。 你是对的。 NHibernate 不支持 UNION - 但无论如何你都不会在这里使用 UNION。 UNION 要求从两个查询返回的列集相同,但由于我们试图连接两个不相关的实体,在此示例中为 OrderHeaderPerson,因此 UNION 将不起作用。您可以(并且应该)做的是使用 NHibernate 的 Future 方法将两个查询一起批处理到数据库的一次往返中。然后,是的,您将获取这两个结果并使用 Linq-to-Objects 将它们组合成一个结果集。 ...所以也许我对“您可以扩展 NHibernate 的 LINQ 提供程序,所以请停止抱怨”的咆哮并不完全适用于这个特定问题,但我认为它仍然是一个有益的观点这类问题。

以上是关于没有 LINQ 扩展的 NHibernate 通用存储库?的主要内容,如果未能解决你的问题,请参考以下文章

LINQ to NHibernate - 如何检测失败的翻译

NHibernate 与 LINQ(谓词?)

将 Linq 中的 NHibernate 应用程序重写为 SQL

使用 NHibernate 3.0 QueryOver 或 LINQ 提供程序的权衡

为 linq 和实体框架编写扩展方法

使用 String.IsNullOrEmpty(string) 和 Nhibernate 创建动态 Linq 表达式