在NHibernate中,使用Disjunction会得到双重结果

本文关键字:结果 Disjunction NHibernate 使用 | 更新日期: 2023-09-27 18:14:17

我试图用DetachedCriteria进行选择,我想在运行时添加几个由OR分隔的条件。

如果我使用:

Restrictions.Or( cond1, Restrictions.Or(cond2, Restrictions.Or(cond3, cond4)) )

我得到我想要的结果。

但是如果我像这样使用析取:

var disjunction = Restrictions.Disjunction();
disjunction.Add(cond1);
disjunction.Add(cond2);
disjunction.Add(cond3);
disjunction.Add(cond4);

并且我有cond1和cond2为真的实体,在结果中我得到它们两次(相同的实体在列表结果中返回两次)。

我不希望使用QueryOver,因为我正试图完成一些很难用QueryOver做的事情(我试图做的最终结果是从json的过滤器中获得sql查询)。

是什么导致析取返回双精度?有没有办法在结尾添加一个DISTINCT ?我做错了吗,我不应该在同一个表的不同条件下使用析取法吗?

更新:

对于DISTINCT部分:
criteria.SetResultTransformer(new NHibernate.Transform.DistinctRootEntityResultTransformer());

Projections.Distinct(Projections.Id())

真正的解决方案如Radim Köhler所述-正确使用子查询

在NHibernate中,使用Disjunction会得到双重结果

一个小借口:这个问题没有提供任何映射,也缺少查询…所以我们只能猜测问题出在哪里。但是让我们试着提供一些解释

为什么接收不明显?

让我们有两个表(如问题下面的一个评论所示)

父:

ParentId | Name
1        | Parent_A
2        | Parent_B

孩子:

ChildId | Color | ParentId
1       | green | 1
2       | grey  | 1
3       | gold  | 1
4       | green | 2

有了这个,我们将在纯SQL中创建一个简单的选择

SELECT p.ParentId, p.Name
FROM Parent AS p
  INNER JOIN Child AS c
    ON p.ParentId = c.ParentId
WHERE 
  c.Color = 'green' OR c.Color = 'grey' OR c.Color = 'gold'

查询的结果是什么?

1 | Parent_A
1 | Parent_A
1 | Parent_A
2 | Parent_B

如果我们将其转换成类似的标准:

var sesion = ... // get session 
var parent = sesion.CreateCriteria<Parent>();
var children = parent.CreateCriteria("Children");
// restrict the children
children.Add(Restrictions.Disjunction()
    .Add(Restrictions.Eq("Color", "green"))
    .Add(Restrictions.Eq("Color", "grey"))
    .Add(Restrictions.Eq("Color", "gold"))
    );
var list = parent
    .SetMaxResults(10) // does not matter in our example, but ... it should be used always
    .List<Parent>();

这是标准c#代码,这将导致多个父(因为事实,相同的SQL将生成如上所述)

正如我们所看到的,问题肯定是在nhibernate端而不是。真的!NHibernate不仅是无辜的,而且还做了需要做的事情。

解决方案

解决方案在子选择

在SQL中是这样的

SELECT p.ParentId, p.Name
FROM Parent AS p
WHERE p.ParentId IN (
  SELECT c.ParentId
  FROM Child AS c
    WHERE c.ParentId = p.ParentId
    AND c.Color = 'green' OR c.Color = 'grey' OR c.Color = 'gold'
)

这将为我们提供我们最可能想要的结果:

1 | Parent_A
2 | Parent_B

在NHibernate中怎么做呢?

var sesion = ... // get session 
var parent = sesion.CreateCriteria<Parent>();
//var children = parent.CreateCriteria("Children");
var children = DetachedCriteria.For(typeof(Child));
// restrict the children
children.Add(Restrictions.Disjunction()
    .Add(Restrictions.Eq("Color", "green"))
    .Add(Restrictions.Eq("Color", "grey"))
    .Add(Restrictions.Eq("Color", "gold"))
    );
// ad SELECT into this sub-select
children.SetProjection( Projections.Property("ParentId"));
// filter the parent
parent
    .Add(Subqueries.PropertyIn("ParentId", children));

var list = parent
    .SetMaxResults(10) // does not matter in our example, but ... it should be used always
    .List<Parent>();

现在,我们有子选择(DetachedCriteriaSubqueries的NHibernate特性),没有更多的重复!