NHibernate 使用 QueryOver 和 Future 加入两个集合

Posted

技术标签:

【中文标题】NHibernate 使用 QueryOver 和 Future 加入两个集合【英文标题】:NHibernate left join two collections with QueryOver and Future 【发布时间】:2017-12-04 09:57:10 【问题描述】:

我有 Project 实体以及 ProjectRole 和 ProjectUser 实体:

public class Project 
    ...
    public virtual IList<ProjectRole> Roles  get; set; 
    public virtual IList<ProjectUser> ProjectUsers  get; set; 


public class ProjectRole 
    public int ProjectID  get; set; 
    public int RoleID  get; set; 


public class ProjectUser 
    public int ProjectID  get; set; 
    public int UserID  get; set; 

可以通过他的 RoleID 或专门通过他的 UserID 将用户分配给 Project。

我希望获得满足以下两个要求之一的所有项目: 用户属于分配给项目的角色(角色集合)或用户直接分配给项目(ProjectUsers)

我发现我不能做两个左连接,因为“不能获取多个包”:

ProjectRole prAlias = null;
ProjectUser puAlias = null;
var projects = s.QueryOver<Project>()
    .Left.JoinAlias(p => p.Roles, () => prAlias)
    .Left.JoinAlias(p => p.ProjectUsers, () => puAlias)
    .Where(p => p.DeletedDate == null)
    .And(() => puAlias.UserID == loggedUserID)
        Restrictions.Or(
            Restrictions.On(() => prAlias.RoleID).IsIn(userRoles),
            Restrictions.Where(() => puAlias.UserID == loggedUserID)
        );

我尝试使用两个单独的查询和未来来做到这一点:

var projectsForUserRoles = s.QueryOver<Project>()
    .Left.JoinAlias(p => p.Roles, () => prAlias)
    .Where(p => p.DeletedDate == null)
    .And(() => prAlias.RoleID).IsIn(userRoles))
    .Future();


var projectsForUser = s.QueryOver<Project>()
    .Left.JoinAlias(p => p.ProjectUsers, () => puAlias)
    .Where(p => p.DeletedDate == null)
    .And(() => puAlias.UserID == loggedUserID)
    .Future();

var projects = projectsForUserRoles.ToList();

但通过这种方式,我只能从 projectsForUserRoles 查询中获得结果,而不是 projectsForUser。两个查询分别工作正常 - 第一个返回 3 条记录,第二个返回 2 条记录。我想得到的是那些(5条记录)结果不同的总和:

SELECT DISTINCT p.ProjectID, p.ProjectName FROM
Projects p
LEFT JOIN ProjectRoles pr ON pr.ProjectID = p.ProjectID
LEFT JOIN ProjectUsers pu ON pu.ProjectID = p.ProjectID
WHERE p.DeletedDate IS NULL
AND(
    pr.RoleID IN (SELECT ur.RoleID FROM UserRoles ur WHERE ur.UserID = 1) 
    OR pu.UserID = 1
)

我错过了什么?我应该如何将两个查询链接到同一实体Project

【问题讨论】:

你试过projectsForUser.Union(projectsForUserRoles).ToList()覆盖哈希码和equals方法吗? 【参考方案1】:

您是否尝试完成内存中的工作?对于你的情况,我觉得这很明智。

using System.Linq;

// ...

var projects = projectsForUserRoles.Union(projectsForUser).ToList();

这就是 Najera in his comment 的建议,但无需担心仅针对这种情况覆盖 GetHascodeEquals:一级缓存将确保您获得相同实体的相同引用。

现在,当您只是过滤它们时,“超过一个袋子”的限制就开始了,这对我来说看起来很奇怪。但是该消息告诉您正在获取。我不太了解queryover,因此无法帮助您将其更改为仅过滤而不是获取。

linq-to-nhibernate 我会写:

using System.Linq;
using NHibernate.Linq;

// ...

var projects = s.Query<Project>()
    .Where(p => p.DeletedDate == null)
    .Where(
        p => p.ProjectUsers.Any(pu => pu.UserID == loggedUserID) ||
            p.Roles.Any(pr => userRoles.Contains(pr.RoleID)))
    .ToList();

【讨论】:

以上是关于NHibernate 使用 QueryOver 和 Future 加入两个集合的主要内容,如果未能解决你的问题,请参考以下文章

使用 NHibernate 3.0 QueryOver 或 LINQ 提供程序的权衡

NHibernate 使用带有 WHERE IN 的 QueryOver

使用 QueryOver 与 NHibernate 进行内部连接的 SQL 查询

NHibernate QueryOver 两次加入一个集合

NHibernate Query<> 与 QueryOver<> 有啥区别?

关于几个属性的NHibernate QueryOver条件