2010-10-29 1 views
5

В моем текущем проекте используется NHibernate 3.0b1 и API NHibernate.Linq.Query<T>(). Я довольно свободно владею LINQ, но у меня нет абсолютно никакого опыта работы с HQL или API ICriteria. Один из моих запросов не поддерживается API IQueryable, поэтому я предполагаю, что мне нужно использовать один из предыдущих API-интерфейсов, но я не знаю, с чего начать.Как выразить этот запрос LINQ с помощью API NHibernate ICriteria?

Я попытался найти в Интернете хорошее руководство по началу работы с 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 подтипы включают CreateAction, ReassignAction, CloseAction и т. Д. Все билеты имеют 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

попробуйте что-то вроде этого. Он не скомпилирован, но он должен работать до тех пор, пока IEnumerable<TicketAction> Actions и Person TakenBy никогда не имеют значения null. Если вы установите его в пустой список в конструкторе билета, это решит проблему с нулями.

Если добавить ссылку на объект Билета в TicketAction, вы могли бы сделать что-то вроде этого:

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, чтобы получить то, что мне нужно.

+0

Я предпочел бы не добавить обратную ссылку к 'TicketAction'' Ticket', если я могу помочь, так как это приведет к другим проблемам, но спасибо за подсказку. :) –

+0

Другим способом, с которым мы справлялись, было то, что это фактически создавало HQL в построителе строк. Мы попытались использовать выражения Linq для Nhibernate, но они тоже не поддерживают коллекции, когда коллекция находится на стороне объекта. К сожалению, метод «Содержит» не переводит в Linq в nHibernate. – Josh

+0

Я пошел с вашим предложением в конце (добавив ссылку на Ticket on TicketAction), но в итоге у меня возникла ошибка: «NHibernate.QueryException: не удалось разрешить свойство: TakenBy.Имя: CreateAction " –

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