【问题标题】:How do I express this LINQ query using the NHibernate ICriteria API?如何使用 NHibernate ICriteria API 表达这个 LINQ 查询?
【发布时间】:2010-10-29 16:48:44
【问题描述】:

我当前的项目正在使用 NHibernate 3.0b1 和 NHibernate.Linq.Query<T>() API。我非常精通 LINQ,但我完全没有使用 HQL 或 ICriteria API 的经验。 IQueryable API 不支持我的一个查询,所以我认为我需要使用以前的 API 之一——但我不知道从哪里开始。

我尝试在网上搜索 ICriteria 的良好“入门”指南,但我发现的唯一示例要么过于简单,无法在此处应用,要么过于高级以至于我无法理解。如果有人有一些好的学习材料可以传递,将不胜感激。

无论如何,我查询的对象模型看起来像这样(非常简化,省略了不相关的属性):

class Ticket {
    IEnumerable<TicketAction> Actions { get; set; }
}
abstract class TicketAction {
    Person TakenBy { get; set; }
    DateTime Timestamp { get; set; }
}
class CreateAction : TicketAction {}
class Person {
    string Name { get; set; }
}

Ticket 有一组 TicketAction 描述其历史。 TicketAction 子类型包括 CreateActionReassignActionCloseAction 等。所有票证在创建时都会在此集合中添加一个 CreateAction

此 LINQ 查询正在搜索由具有给定名称的人创建的票证。

var createdByName = "john".ToUpper();
var tickets = _session.Query<Ticket>()
    .Where(t => t.Actions
        .OfType<CreateAction>()
        .Any(a => a.TakenBy.Name.ToUpper().Contains(createdByName));

OfType&lt;T&gt;() 方法导致NotSupportedException 被抛出。我可以改用 ICriteria 吗?

【问题讨论】:

    标签: nhibernate linq-to-nhibernate nhibernate-criteria


    【解决方案1】:

    试试这样的。它是未编译的,但只要IEnumerable&lt;TicketAction&gt; ActionsPerson TakenBy 永远不会为空,它就应该可以工作。如果在票证构造函数中将其设置为空列表,这将解决空值问题。

    如果您在 TicketAction 中添加对 Ticket 对象的引用,您可以执行以下操作:

    ICriteria criteria = _session.CreateCriteria(typeof(CreateAction))
       .Add(Expression.Eq("TakenBy.Name", createdByName));
    
    var actions = criteria.List<CreateAction>();
    
    var results = from a in criteria.List<>()
       select a.Ticket;
    

    根据我的经验,当列表位于对象端时,nhibernate 在标准方面存在问题 - 例如您的情况。当它是输入端的值列表时,您可以使用 Expression.Eq。我总是不得不通过 linq 找到解决这个限制的方法,在那里我尽可能地过滤掉初始结果集,然后用 linq 再次过滤以获得我需要的结果。

    【讨论】:

    • 如果我能提供帮助,我宁愿不必添加对TicketActionTicket 的反向引用,因为这会引入其他问题,但感谢您的提示。 :)
    • 我们处理此类情况的另一种方法是在字符串生成器中实际创建 HQL。我们尝试使用 Linq to Nhibernate 表达式,但是当集合位于对象端时,这些表达式也不支持集合。不幸的是,“包含”方法不能在 Linq 中转换为 nHibernate。
    • 我毕竟接受了你的建议(在 TicketAction 上添加对 Ticket 的引用),但我最终得到一个错误:“NHibernate.QueryException:无法解析属性:TakenBy.Name of:CreateAction "
    • 我相信这是因为 TakenBy 默认为空。在 TicketAction 的构造函数中,将其设置为空 Person。此外,使用 nhibernate,您的属性/方法需要是虚拟的。
    【解决方案2】:

    支持OfType。我不确定 ToUpper 是不是,但由于 SQL 忽略大小写,这并不重要(只要您不在内存中运行查询......)。这是来自 nHibernate.LINQ 项目的工作单元测试:

    var animals = (from animal in session.Linq<Animal>()
                   where animal.Children.OfType<Mammal>().Any(m => m.Pregnant)
                   select animal).ToArray();
    Assert.AreEqual("789", animals.Single().SerialNumber);
    

    也许您的查询应该类似于以下内容:

    var animals = (from ticket in session.Linq<Ticket>()
                   where ticket.Actions.OfType<CreateAction>().Any(m => m.TakenBy.Name.Contains("john"))
                   select ticket).ToArray();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多