如何使用NHibernate ICriteria API表达此LINQ查询?

时间:2010-10-29 16:48:44

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

我目前的项目是使用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<T>()方法会导致NotSupportedException被抛出。我可以使用ICriteria代替吗?

2 个答案:

答案 0 :(得分:2)

尝试这样的事情。它是未编译的,但只要IEnumerable<TicketAction> 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过滤以获得我需要的东西。

答案 1 :(得分:0)

支持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();