【问题标题】:How to efficiently filter a collection of child objects by a property with NHibernate如何通过 NHibernate 的属性有效地过滤子对象的集合
【发布时间】:2010-06-23 05:52:35
【问题描述】:

几个小时以来,我一直在尝试过滤一组子对象,但没有成功,我终于举手了!我是 NHibernate 的新手,并希望对此有一些指示。我尝试了各种 ICriteria 等,但都没有运气。我就是不明白。

我有一个父对象“发布”,其中包含一组子对象“评论”。该集合被映射为一个集合,在 Comment 端具有反转。

我要做的是只返回状态枚举值为“Comment.Approved”的 cmets

实体类的相关部分如下:

public class Post
{
    public virtual Guid Id { get; protected set; }
    private ICollection<Comment> _comments;
    public virtual ICollection<Comment> Comments
    {
        get { return _comments; }
        protected set { _comments = value; }
    }
}

public class Comment
{
    public virtual Guid Id { get; protected set; }
    public virtual Post Post { get; set; }
    public virtual CommentStatus Status { get; set; }

 }

我现在的检索代码是这样的:

var Id = __SomeGuidHere__;
var post = _session
            .CreateCriteria<Post>()
            .Add(Restrictions.Eq("Id", Id))
            .UniqueResult<Post>();

var comments = _session.CreateFilter(post.Comments, "where Status = :status").SetParameter("status", CommentStatus.Approved).List<Comment>();

虽然这样可行,但 SQL 似乎效率不高,但我希望能够将以下 SQL 转换为 HQL 或某种 ICriteria 中的类似内容:

SELECT * FROM posts p LEFT JOIN comments c ON p.PostId = c.PostId AND c.Status = 0 WHERE p.PostId = '66a2bf13-1330-4414-ac8a-9d9b00ea0705';

我在这里查看了与此类查询相关的各种答案,但似乎都没有解决这种特定情况。

这里可能缺少一些非常简单的东西,但我现在太累了,看不到它。希望有更好的 NHibernate 能够为我指明正确的方向。

感谢您的宝贵时间。

编辑:仍在为此苦苦挣扎,这里的一些答案很好,因为我开始认为我的帖子实体需要重新考虑以执行过滤本身,或者我应该实现一个 ViewModel 来过滤我想要的 cmets。但是,即使仅从学术角度来看,问题仍然存在。

我已将选择更新为 HQL 并尝试:

var post = _session
            .CreateQuery("select p from Post as p left join fetch p.Comments as c where p.Id = :id and c.Id in (select ac from p.Comments ac where ac.Status = :status)")
            .SetParameter("id", Id)
            .SetParameter("status", CommentStatus.Approved)
            .UniqueResult<Post>();

只要帖子有已获批准的评论,此方法就有效,否则由于在 where 子句中使用“AND”生成的 SQL,我没有收到任何帖子。

有人吗?我现在被难住了!

更新:感谢所有回复的人,它很有用,并迫使我重新评估我的模型的某些部分。由于最常使用 cmets 作为帖子的子项是在查看帖子时,因此在这种情况下应该只能查看已批准的 cmets。在我能想到的大多数其他场景中,cmets 将被直接访问并按状态过滤,这当然是直截了当的。

我已更新我的映射以过滤所有帖子 > 评论加载以仅加载已批准的帖子,如下所示(在 FluentNHibernate 中):

 HasMany(x => x.Comments).Where(x => x.Status == CommentStatus.Approved)
            .AsSet()
            .Inverse()
            .KeyColumn("PostId")
            .ForeignKeyConstraintName("PostComments")
            .OrderBy("CreatedOn")
            .Cascade.AllDeleteOrphan();

我希望我可以将所有都标记为答案,因为所有这些都有助于我解决这个问题,但彼得是第一个指出我可能错误地考虑模型的人。

谢谢大家。

【问题讨论】:

  • 你想解决什么问题(除了好奇心)?我的猜测是 cmets 在审核后获得批准。有效的 cmets 将始终获得批准;垃圾邮件或攻击性邮件将被删除。因此,我预计绝大多数 cmets 将获得批准。如果是这种情况,那么将它们全部返回(在 FK 上选择)并在 UI 中过滤可能比预先过滤它们更有效。
  • 实际上垃圾邮件 cmets 不会被删除,因为它们将在贝叶斯过滤器中使用,以确定哪些是垃圾邮件,哪些不是将来,所以我认为很有可能为帖子选择所有 cmets,然后 UI 过滤可能会变得非常低效。
  • 但是我又是新手,所以我可能会说垃圾:)
  • 您可以考虑将垃圾邮件 cmets 移动到它们自己的表中,以便可以更轻松地将它们作为一组或在离线过程中进行分析。垃圾评论没有理由与帖子保持关联,您将节省为每个帖子过滤掉它们的开销。

标签: nhibernate filtering subquery


【解决方案1】:

根据我的说法,SQL 非常适合您的描述。

数据库有自己的优化器,并且知道如何将您的数据有效地送到您家门口。

【讨论】:

  • 感谢您的评论,我希望 Post 对象有一组经过过滤的 cmets,而不必在其余代码中使用第二个对象 (var cmets)。这可能只是我在行动中的固执:) 如果有一个替代方案可以让我保持我的模型,我宁愿拥有它。让我感到烦恼的是,post 对象仍然有我不想挂在它上面的 cmets!
  • 那么危险是post对象中的“cmets”字段在不同的情况下会有不同的含义,很快就会变得复杂。考虑添加一个 getApprovedComment() 或其他东西。通常,您在请求中只需要这些东西一次,然后延迟加载将是有效的。如果您确实需要它们多次,您可以在该方法中缓存结果。
  • 嗯,这很有趣,我的知识还不够,请您再解释一下。我明白您所说的 cmets 的意思,我认为这些 cmets 具有不同的含义,即在这种情况下实际上是被批准的 cmets,并且在管理场景中可能是“垃圾邮件 cmets”、“狡猾的 cmets”和已批准的 cmets。您是否建议 post 对象应该有多个集合,每个集合都有不同的映射?
  • 我会在对象或服务层中使用方法,这取决于代码库中使用的约定。我不是关于 svc.getApprovedMethods(post) 或 post.getApprovedMethods() 的原教旨主义者。两者都“唱”出它们对我的意义。如果你喜欢瘦的对象,服务方法会更好,如果你不介意“胖”的对象,第二个会更自然。
  • 好的,您的评论回复:具有新含义的 cmets 真的让我思考 - 我什么时候可以作为帖子的子项访问 cmets?不经常!可能仅在展示帖子以供查看时,这意味着 90% 的时间我们只想要已批准的帖子。我将更改映射以反映这一点。
【解决方案2】:

我认为这会很好:

var comments =
_session.CreateCriteria<Comment>
.CreateAlias("Post", "post")
.Add (Restriction.Eq("Status", status))
.Add (Restriction.Eq("post.Id", Id)).List();

如果您需要 .CreateAlias 部分,我不知道(如果您不需要它,也许 Post.Id 也可以工作 - 但我不确定)。

【讨论】:

  • 我知道你从哪里来,但这个查询只返回我真正想要的帖子对象,它的集合被过滤到任何具有我正在寻找的状态的帖子,如果这是可能的。如果不是,我将不得不忍受我所拥有的,但它似乎应该是直截了当的!
  • 在这种情况下,我将在 Post 类中创建一个属性,该属性将返回正确的 cmets(过滤 Comments 属性) - 但这取决于您的应用程序(如果这种“次优”查询是可以接受的)
  • 一般来说,我会先开始实施 - 然后测试它 - 如果性能不好 - 我会开始优化这些情况(不进行过早优化)
【解决方案3】:

我会使用IEnumerable&lt;Comment&gt; 的扩展方法来解决这个问题:

public static IEnumerable<Comment> FilterByStatus(this IEnumerable<Comment> comments, CommentStatus status)
{
    return comments.Where(x => x.Status == status);
}

让 NHibernate 返回整个集合,然后根据需要对其进行过滤。

【讨论】:

  • 开始认为这个,或者来自 bernhardrusch 或 Peter 的其他方法之一可能是我唯一的选择。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多