Могу ли я использовать NHibernate Criteria для проецирования сущности и ее дочерней коллекции на класс? - PullRequest
3 голосов
/ 12 февраля 2011

Я использую NH Criteria для извлечения сущности и проецирования выборочных полей в пользовательский класс (немного похоже на проецирование данных на ViewModel для отображения в виде MVC).

Это достаточно просто с помощью ProjectionList:

var emailCriteria = mSession.CreateCriteria<Email>();
emailCriteria.SetProjection(
    Projections.ProjectionList()
        .Add(Projections.Property("Subject"), "Subject")
);
emailCriteria.SetResultTransformer(Transformers.AliasToBean<EmailDataModel>());
var result = emailCriteria.List<EmailDataModel>();

Однако моя сущность содержит коллекцию, и я хочу вернуть ее обратно и спроецировать как коллекцию на мой пользовательский класс.

Моя модель предметной области выглядит (в упрощенном виде) следующим образом:

public class Email {
    public string Subject
    public List<EmailAttachment> Attachments
    etc...
}

public class EmailAttachment {
    public UploadedFile File
}

public class UploadedFile {
    public string Filename
    public UploadedFileData Data
}

public class UploadedFileData {
    public byte[] Data
}

Вот классы "модели данных", на которые я хочу проецировать:

public class EmailDataModel {
    public string Subject
    public List<EmailAttachmentDataModel> Attachments
}

public class EmailAttachmentDataModel {
    public string Filename
    public byte[] Data
}

СейчасЯ знаю, что эти модели очень похожи, и вы бы простили, что подумали: «Какой смысл?», Но это потому, что я упростил их.Приятно иметь возможность сгруппировать мои доменные объекты в удобные модели данных.

Моя большая проблема - выяснить, как получить доступ к необходимым полям изнутри моих дочерних объектов (в данном случае, UploadedFile.Filename и UploadedFileData.Data) и проецируйте их как коллекцию EmailAttachmentDataModel на мой EmailDataModel.

Я прочитал много статей в Интернете, в которых обсуждается доступ к дочерним коллекциям - с помощью EmailCriteria.CreateAlias ​​или EmailCriteria.CreateQuery - но у меня нет 'не нашел ничего, что объясняет, как проектировать дочернюю коллекцию как коллекцию.

Я надеюсь, что это будет полезным упражнением для всех, кто заинтересован в том, чтобы возиться с запросами NH Criteria.

1 Ответ

5 голосов
/ 14 февраля 2011

Хорошо, я думаю, что я решил это обновление до NHibernate 3 и использование QueryOver. Вот как теперь выглядит мой код:

//Declare entities
Email email = null;
EmailAttachment attachment = null;
UploadedFile file = null;
Byte[] fileData = null;

//Select data from parent and child objects
var results = mSession.QueryOver<QueuedEmail>(() => email)
   .JoinAlias(() => email.Attachments, () => attachment, JoinType.LeftOuterJoin)
   .JoinAlias(() => attachment.File, () => file, JoinType.LeftOuterJoin)
   .JoinAlias(() => file.Data, () => fileData, JoinType.LeftOuterJoin)
   .TransformUsing(Transformers.DistinctRootEntity)
   .List<Email>()

   //Loop through results projecting fields onto POCO
   .Select(x => new EmailDataModel()
   {
       Id = x.Id,
       Body = x.Body,
       AttachmentCount = x.Attachments.Count(),
       FromAddress = x.FromAddress,
       //Loop through child collection projecting fields onto POCO
       Attachments = x.Attachments.Select(attach => new EmailAttachmentDataModel()
       {
           Data = attach.File.Data.Data,
           Filename = attach.File.Filename,
           Id = attach.Id
       }).ToArray() //NB Now projecting this collection as an array, not a list
   }).ToArray();

Итак, вот оно. Наш результат - плоский класс, который содержит данные, которые нам нужны, плюс набор вложений (каждое из которых содержит только два поля из нашей структуры данных - приятно сплющенный).

Зачем вам это делать?

  1. Это упрощает результат, сглаживая только те поля, которые мне действительно нужны.

  2. Мои данные теперь безопасно инкапсулированы в класс, который можно передавать, не опасаясь случайного обновления моих данных (что может произойти, если вы просто передадите обратно объекты данных NH).

  3. Наконец (и это самое главное), потому что приведенный выше код генерирует только один оператор SELECT. Если бы я придерживался своего исходного запроса Criteria, он бы сгенерировал один SELECT для каждой строки, плюс еще больше для дочерних элементов, расположенных дальше по цепочке. Это хорошо, если вы имеете дело с небольшими числами, но не если вы потенциально можете вернуть тысячи строк (как я в этом случае - это веб-сервис для механизма электронной почты).

Надеюсь, это было полезно для всех, кто хочет продвинуться немного дальше в запросах NHibernate. Лично я просто счастлив, что теперь могу жить своей жизнью!

...