如何使用NHibernate通过属性有效地过滤子对象的集合

时间:2010-06-23 05:52:35

标签: nhibernate filtering subquery

我一直在尝试过滤一些子对象的集合几个小时,并且终于举手了!我是NHibernate的新手,并且希望对此提出几点建议。我已经尝试了各种ICriteria等没有运气。我只是没有得到它。

我有一个父对象'Post',其中包含一组子对象'Comment'。该集合在Comment侧被映射为带反转的集合。

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

实体类的相关部分如下:

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来过滤我想要的评论。然而,问题仍然存在,即使只是从学术角度来看。

我已将选择更新为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,我没有发帖。

任何?我现在难过了!

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

我已更新我的映射以过滤所有帖子&gt;注释加载仅按如下方式加载已批准的帖子(在FluentNHibernate中):

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

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

感谢所有人。

3 个答案:

答案 0 :(得分:0)

据我所知,SQL非常适合您描述的内容。

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

答案 1 :(得分:0)

我认为这样可行:

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

如果您需要.CreateAlias部分(如果您不需要它,也许Post.Id也会起作用 - 但我不确定),我不是不舒服。

答案 2 :(得分:0)

我会使用IEnumerable<Comment>的扩展方法解决这个问题:

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

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