我可以使用NHibernate Criteria将实体及其子集合投影到类中吗?

时间:2011-02-12 07:59:10

标签: collections nhibernate-criteria nhibernate-projections

我正在使用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 个答案:

答案 0 :(得分:5)

好的,我想我已经解决了升级到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,并为链中的下一个孩子生成更多。如果您处理的是小数字,那就没问题,但如果您可能会返回数千行,那就不行了(正如我将在这个例子中那样 - 它是电子邮件引擎的网络服务)

  4. 我希望这对任何希望进一步深入NHibernate查询的人都有用。就个人而言,我很高兴我现在可以继续我的生活!