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

发布于 2024-10-17 05:45:35 字数 1658 浏览 9 评论 0原文

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

I'm using NH Criteria to retrieve an entity and project selective fields onto a custom class (a bit like projecting data onto a ViewModel for display on an MVC view).

This is easy enough using 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>();

However, my entity contains a collection, and I want to bring that back too, and project it as a collection onto my custom class.

My domain model looks (in simplified form) like this:

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
}

Here's the "data model" classes I want to project onto:

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

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

Now I know these models look very similar, and you'd be forgiven for thinking "what's the point?", but that's because I've simplified them. It's nice to be able to flatten my domain objects into handy data models.

My big problem is figuring out how to access the necessary fields from deep down in my child objects (in this case, UploadedFile.Filename and UploadedFileData.Data), and project them as an EmailAttachmentDataModel collection onto my EmailDataModel.

I've read a lot of articles online which discuss accessing child collections - using either EmailCriteria.CreateAlias or EmailCriteria.CreateQuery - but I haven't found anything which explains how to project a child collection AS a collection.

I hope this will be a useful exercise for anyone interested in tinkering with NH Criteria queries.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

落日海湾 2024-10-24 05:45:35

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

Ok, I think I've resolved this upgrading to NHibernate 3 and using QueryOver. Here's what my code looks like now:

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

So there it is. Our result is a flattened class which contains the data we need, plus a collection of attachments (which each contain just two fields from our data structure - nicely flattened).

Why should you do this?

  1. It simplifies the result by flattening into only the fields I really want.

  2. My data is now safely encapsulated in a class which can be passed around without fear of accidentally updating my data (which could happen if you just pass back NH data entities).

  3. Finally (and most importantly), because the code above only generates one SELECT statement. Had I stuck with my original Criteria query, it would have generated one SELECT for each row, plus more for the children further down the chain. That's fine if you're dealing with small numbers, but not if you're potentially returning thousands of rows (as I will in this instance - it's a web service for an email engine).

I hope this has been useful for anybody wishing to push a bit further into NHibernate querying. Personally I'm just happy I can now get on with my life!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文