【问题标题】:Can I use NHibernate Criteria to project an entity and its child collection onto a class?我可以使用 NHibernate Criteria 将实体及其子集合投影到类上吗?
【发布时间】:2011-02-12 07:59:09
【问题描述】:

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

【问题讨论】:

    标签: collections nhibernate-criteria nhibernate-projections


    【解决方案1】:

    好的,我想我已经解决了升级到 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 查询的人有用。就我个人而言,我很高兴我现在可以继续我的生活!

    【讨论】:

    • 我刚刚遇到了类似的情况,虽然这在技术上是可行的,但我想补充一点,您的 .List() 调用之后的所有内容实际上都是使用 LINQ 在本地完成的。这意味着您已经将所有连接的数据从数据库中提取到内存中。这在您的示例中可能不是问题,但在某些情况下很容易成为问题。不幸的是,我认为这实际上不可能直接在 NHibernate 中完成,因为您最终要么像在此处所做的那样在本地过滤重复的连接数据,要么运行另一个查询来获取子数据。
    • 对我非常有用。但是我可以使用 Future 进行这个查询吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-26
    • 2012-06-09
    • 1970-01-01
    • 1970-01-01
    • 2015-03-29
    相关资源
    最近更新 更多