4

我正在使用 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 查询的人来说都是一个有用的练习。

4

1 回答 1

6

好的,我想我已经解决了升级到 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,并为更下游的孩子生成更多。如果您正在处理小数字,那很好,但如果您可能返回数千行(正如我在本例中将要的那样 - 它是电子邮件引擎的 Web 服务)。

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

于 2011-02-14T14:40:28.580 回答