QueryOver 集合包含所有值

Posted

技术标签:

【中文标题】QueryOver 集合包含所有值【英文标题】:QueryOver collection contains all values 【发布时间】:2014-01-14 15:01:39 【问题描述】:

简化域:

public class MasterDocument 
    Guid ID;
    Program StorageCompartment;
    ISet<DocumentCompartment> Compartments;

public class Program 
    int ID;
    string GroupName;

public class DocumentCompartment 
    int ID;
    Program AssociatedCompartment;
    MasterDocument AssociatedDocument;

public class Document 
    Guid ID;
    MasterDocument MasterDocument;
    //Many more properties

我知道这有点令人费解,但是由于安全问题,我们已经通过将某些记录(如文档)放入与它们所属的程序/隔间(可互换术语)相对应的不同数据库来处理安全问题,因此存在这样的架构到。 MasterDocument、Program 和 DocumentCompartment 的表位于“主”数据库中,其中包含所有隔间的信息,而几个不同的数据库将各自包含自己的 Documents 表。无论如何,关于问题:

我正在尝试构建一个查询,通过该查询传递给我一个组名列表,并且我只想要没有关联隔间且不包含在该组名列表中的文档。

举个例子: Doc1 与隔间 P1 关联 Doc2 与 P2、P3 和 P7 相关联 Doc3 与 P1 和 P3 关联 我想检查组:P1、P3、P4、P7(这些是我有权访问的组)

我应该取回 Doc1 和 Doc3,因为我没有 P2 的权限,而 Doc2 需要它。我可以通过以下查询使用 LINQ 提供程序来做到这一点:

string[] groups = new[]  "P1", "P3", "P4", "P7" ;
return Session.Query<Document>().Where(doc => doc.MasterDocument.Compartments.All(comp => groups.Contains(comp.AssociatedCompartment.GroupName));

(还有关于上述内容的注释:如果我尝试将该逻辑封装在 Document 类中并将该方法传递给“Where”方法,例如 return Session.Query().Where(doc => doc.CanAccess(组)),然后我得到一个 System.NotSupportedException。我有点理解为什么,但如果有解决方法,那就太好了。)

生成的 SQL 如下所示:

exec sp_executesql 
N'select
doc.DocumentGuid as guid
from Documents doc 
where  not (exists 
(select comp.DocumentCompartmentID 
 from Master.MasterDocuments master, 
      Master.DocumentCompartments comp, 
      Master.Programs prog 
 where doc.DocumentGuid=master.DocumentGuid and 
       master.DocumentGuid=comp.DocumentGuid and 
       comp.CompartmentID=prog.ProgramID and  
       not (prog.ADGroupName in ('P1', 'P3', 'P4', 'P7'))
 ))',

我现在正试图弄清楚如何使用 NHibernate QueryOver 语法进行相同的查询。不幸的是,我没有足够的知识或经验来知道如何编写它。对此的任何帮助将不胜感激!

【问题讨论】:

【参考方案1】:

我相信你想要这样的东西:

IList<Guid> results = Session.QueryOver<Document>(() => documentAlias)
    .WithSubquery.WhereNotExists(
        QueryOver.Of<MasterDocument>()
                .Where(md => md.ID == documentAlias.MasterDocument.ID) // Not sure about this
            .JoinQueryOver(md => md.Compartments)
            .JoinQueryOver(cmp => cmp.AssociatedCompartment)
                .WhereNot(acmp => acmp.GroupName.IsIn(new[]  "P1", "P3", "P4", "P7" )))
    .Select(doc => doc.ID)
    .List<Guid>();

我不确定MasterDocument.IDDocument.MasterDocument.ID 之间的比较在我评论的地方是否正确,但这应该会让你朝着正确的方向开始。

几点说明:

一般情况下,不能将 LINQ 语法与 QueryOver 一起使用。它们看起来相似,但它们是不同的查询技术。 有关 QueryOver 的基本介绍,请查看 nhibernate.info 上的 this guide。不幸的是,就官方文档而言,这与您所能获得的一样好 这基于您提供的类假设了一些关于您的映射的事情。

【讨论】:

嗯,这似乎奏效了。我实际上并没有创建任何单元测试来确保它可以做我想做的事情,但是调用我的查询方法的单元测试至少现在不会在 NHibernate 代码深处抛出异常。 顺便说一句,您对是否可以在 Document 类中封装 LINQ 逻辑有任何评论,所以我可以这样称呼它:Session.Query().Where(doc => doc.可以访问(组))?我真正想做的是拦截我们的查询,检查 T 是否是特定类型(例如 ISecurable,它定义了 bool CanAccess(...)),并插入表达式以仅在我“可以访问”时返回实体它。如前所述,内联逻辑有效,但尝试在方法中调用该逻辑无效。 一般来说,您不能从 QueryOver 查询内部调用实体上的方法。将 QueryOver 视为直接转换为 SQL。因此,CanAccess 在 SQL 查询中没有意义。您必须对实体进行水合,然后然后调用该方法。 参考文档中现在有一个QueryOver section。 @Frédéric:我想这已经有一段时间了。我认为它有点稀疏,但谢谢你提到它。另外,感谢您的编辑!

以上是关于QueryOver 集合包含所有值的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Nhibernate 中对子集合进行 QueryOver

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

nHibernate QueryOver:过滤主元素长度和子元素长度

查询集合中的所有文档以查找某个文档是不是包含值

如何使用 mongodb (node.js) 在集合中创建一个包含所有值的数组

NHibernate QueryOver 与 SelectList