流畅的 nHibernate 查询 => QueryOver、Join、Distinct
Fluent nHibernate Query => QueryOver, Join, Distinct
我尝试将该查询更改为 QueryOver<>
以便能够在(生成的 sql)查询
中执行 Distinct
操作
var result = (from x in Session.Query<Events>()
join o in Session.Query<Receivers>() on x.ID equals o.ID
where x.Owner.ID == 1 //the user is the owner of that Event (not null)
||
x.EVType.ID == 123 //(not null)
||
x.Receivers.Count(y => y.User.ID == 1) > 0 //the user is one of the Event Receivers
select x.StartDate)
.Distinct();
我试过类似的东西
Events x = null;
List<Receivers> t = null;
var result = Session.QueryOver<Events>(() => x)
.JoinAlias(() => x.Receivers, () => t)
.Where(() => x.Owner.ID == 1
||
x.EVType.ID == 123
||
t.Count(y => y.User.ID == 1) > 0)
.TransformUsing(Transformers.DistinctRootEntity)
.Select(a => a.StartDate)
.List();
但后来我遇到了 Value can not be null. Parameter name: source
异常。有什么想法可以解决该查询吗?
编辑
感谢 xanatos 的回答,最终 SQL 查询是正确的(我使用了他的第二种方法):
SELECT distinct this_.StartDate as y0_
FROM Events this_
WHERE
(
this_.UserID = ?
or
this_.EventTypeID = ?
or
exists (SELECT this_0_.ID as y0_
FROM Receivers this_0_
WHERE this_0_.UserID = ?)
)
希望这个回答可以帮助到其他人。此错误是由声明
引起的
List<Receivers> t = null;
后跟查询表达式
t.Count(y => y.User.ID == 1) > 0
QueryOver 文档指出 "The variable can be declared anywhere (but should be empty/default at runtime)." 因为在这种情况下,占位符是一个列表,您必须将它初始化为一个空列表。
List<Receivers> t = new List<Receivers>();
否则,当您尝试在占位符对象上引用 Count 方法或任何其他方法时,源 (t) 将为空。
然而,这仍然会留下一个问题,如@fex 和@xanatos,其中从别名 List t 引用 Count() 是没有意义的,因为它不会转换为 SQL。相反,您应该创建一个子查询。查看他们的答案以获得更全面的答案。
"In QueryOver, aliases are assigned using an empty variable. The variable can be declared anywhere (but should be empty/default at runtime). The compiler can then check the syntax against the variable is used correctly, but at runtime the variable is not evaluated (it's just used as a placeholder for the alias)." http://nhibernate.info/blog/2009/12/17/queryover-in-nh-3-0.html
像您一样将 List<Receivers> t
设置为空集合(正如您在评论中提到的那样)意味着您检查的是本地空集合中的事件 ID - 根本没有意义。
您可以尝试使用子查询进行查询(应该可以,但我不确定,我没有测试就写了它,"by hand"):
Receivers receiversSubQueryAlias = null;
var subquery = session.QueryOver<Events>()
.JoinQueryOver<Receivers>(x => x.Receivers, () => receiversSubqueryAlias, JoinType.Inner)
.Where(()=> receiversSubQueryAlias.UserId == 1)
.Select(x => x.Id)
.TransformUsing(Transformers.DistinctRootEntity);
Events eventsAlias = null;
var mainQueryResults = session.QueryOver<Events>(() => eventsAilas)
.Where(Restrictions.Disjunction()
.Add(() => eventAlias.OwnerId == 1)
.Add(() => eventAlias.EVType.Id == 123)
.Add(Subqueries.WhereProperty<Events>(() => eventAlias.Id).In(subquery))
).Select(x => x.StartDate)
.TransformUsing(Transformers.DistinctRootEntity)
.List();
如@fex 所写,您不能简单地执行 new List<Receivers>
。问题是您不能将 QueryOver 与 "LINQ"(t.Count(...)
部分)混合使用。 QueryOver "parser" 尝试执行 "locally" t.Count(...)
而不是在 SQL.
中执行
正如别人所写,TransformUsing(Transformers.DistinctRootEntity)
是客户端。如果你想做一个 DISTINCT 服务器端,你必须使用 Projections.Distinct
.
您必须进行显式子查询。这里有两种查询变体。第一个更类似于 LINQ 查询,第二个不使用 Count
但使用 Exist(在 LINQ 中,您可以通过将 Count(...) > 0
更改为 Any(...)
请注意,当您使用 .Select()
时,您通常必须明确告诉 NHibernate .List<something>()
的类型
Events x = null;
Receivers t = null;
// Similar to LINQ, with COUNT
var subquery2 = QueryOver.Of<Receivers>(() => t)
.Where(() => t.SOMETHING == x.SOMETHING) // The JOIN clause between Receivers and Events
.ToRowCountQuery();
var result2 = Session.QueryOver<Events>(() => x)
.Where(Restrictions.Disjunction()
.Add(() => x.Owner.ID == 1)
.Add(() => x.EVType.ID == 123)
.Add(Subqueries.WhereValue(0).Lt(subquery2))
)
.Select(Projections.Distinct(Projections.Property(() => x.StartDate)))
.List<DateTime>();
// With EXIST
var subquery = QueryOver.Of<Receivers>(() => t)
.Where(() => t.SOMETHING == x.SOMETHING) // The JOIN clause between Receivers and Events
.Select(t1 => t1.ID);
var result = Session.QueryOver<Events>(() => x)
.Where(Restrictions.Disjunction()
.Add(() => x.Owner.ID == 1)
.Add(() => x.EVType.ID == 123)
.Add(Subqueries.WhereExists(subquery))
)
.Select(Projections.Distinct(Projections.Property(() => x.StartDate)))
.List<DateTime>();
请注意,您必须在子查询中设置 "manually" JOIN 条件。
我尝试将该查询更改为 QueryOver<>
以便能够在(生成的 sql)查询
Distinct
操作
var result = (from x in Session.Query<Events>()
join o in Session.Query<Receivers>() on x.ID equals o.ID
where x.Owner.ID == 1 //the user is the owner of that Event (not null)
||
x.EVType.ID == 123 //(not null)
||
x.Receivers.Count(y => y.User.ID == 1) > 0 //the user is one of the Event Receivers
select x.StartDate)
.Distinct();
我试过类似的东西
Events x = null;
List<Receivers> t = null;
var result = Session.QueryOver<Events>(() => x)
.JoinAlias(() => x.Receivers, () => t)
.Where(() => x.Owner.ID == 1
||
x.EVType.ID == 123
||
t.Count(y => y.User.ID == 1) > 0)
.TransformUsing(Transformers.DistinctRootEntity)
.Select(a => a.StartDate)
.List();
但后来我遇到了 Value can not be null. Parameter name: source
异常。有什么想法可以解决该查询吗?
编辑
感谢 xanatos 的回答,最终 SQL 查询是正确的(我使用了他的第二种方法):
SELECT distinct this_.StartDate as y0_
FROM Events this_
WHERE
(
this_.UserID = ?
or
this_.EventTypeID = ?
or
exists (SELECT this_0_.ID as y0_
FROM Receivers this_0_
WHERE this_0_.UserID = ?)
)
希望这个回答可以帮助到其他人。此错误是由声明
引起的List<Receivers> t = null;
后跟查询表达式
t.Count(y => y.User.ID == 1) > 0
QueryOver 文档指出 "The variable can be declared anywhere (but should be empty/default at runtime)." 因为在这种情况下,占位符是一个列表,您必须将它初始化为一个空列表。
List<Receivers> t = new List<Receivers>();
否则,当您尝试在占位符对象上引用 Count 方法或任何其他方法时,源 (t) 将为空。
然而,这仍然会留下一个问题,如@fex 和@xanatos,其中从别名 List t 引用 Count() 是没有意义的,因为它不会转换为 SQL。相反,您应该创建一个子查询。查看他们的答案以获得更全面的答案。
"In QueryOver, aliases are assigned using an empty variable. The variable can be declared anywhere (but should be empty/default at runtime). The compiler can then check the syntax against the variable is used correctly, but at runtime the variable is not evaluated (it's just used as a placeholder for the alias)." http://nhibernate.info/blog/2009/12/17/queryover-in-nh-3-0.html
像您一样将 List<Receivers> t
设置为空集合(正如您在评论中提到的那样)意味着您检查的是本地空集合中的事件 ID - 根本没有意义。
您可以尝试使用子查询进行查询(应该可以,但我不确定,我没有测试就写了它,"by hand"):
Receivers receiversSubQueryAlias = null;
var subquery = session.QueryOver<Events>()
.JoinQueryOver<Receivers>(x => x.Receivers, () => receiversSubqueryAlias, JoinType.Inner)
.Where(()=> receiversSubQueryAlias.UserId == 1)
.Select(x => x.Id)
.TransformUsing(Transformers.DistinctRootEntity);
Events eventsAlias = null;
var mainQueryResults = session.QueryOver<Events>(() => eventsAilas)
.Where(Restrictions.Disjunction()
.Add(() => eventAlias.OwnerId == 1)
.Add(() => eventAlias.EVType.Id == 123)
.Add(Subqueries.WhereProperty<Events>(() => eventAlias.Id).In(subquery))
).Select(x => x.StartDate)
.TransformUsing(Transformers.DistinctRootEntity)
.List();
如@fex 所写,您不能简单地执行 new List<Receivers>
。问题是您不能将 QueryOver 与 "LINQ"(t.Count(...)
部分)混合使用。 QueryOver "parser" 尝试执行 "locally" t.Count(...)
而不是在 SQL.
正如别人所写,TransformUsing(Transformers.DistinctRootEntity)
是客户端。如果你想做一个 DISTINCT 服务器端,你必须使用 Projections.Distinct
.
您必须进行显式子查询。这里有两种查询变体。第一个更类似于 LINQ 查询,第二个不使用 Count
但使用 Exist(在 LINQ 中,您可以通过将 Count(...) > 0
更改为 Any(...)
请注意,当您使用 .Select()
时,您通常必须明确告诉 NHibernate .List<something>()
Events x = null;
Receivers t = null;
// Similar to LINQ, with COUNT
var subquery2 = QueryOver.Of<Receivers>(() => t)
.Where(() => t.SOMETHING == x.SOMETHING) // The JOIN clause between Receivers and Events
.ToRowCountQuery();
var result2 = Session.QueryOver<Events>(() => x)
.Where(Restrictions.Disjunction()
.Add(() => x.Owner.ID == 1)
.Add(() => x.EVType.ID == 123)
.Add(Subqueries.WhereValue(0).Lt(subquery2))
)
.Select(Projections.Distinct(Projections.Property(() => x.StartDate)))
.List<DateTime>();
// With EXIST
var subquery = QueryOver.Of<Receivers>(() => t)
.Where(() => t.SOMETHING == x.SOMETHING) // The JOIN clause between Receivers and Events
.Select(t1 => t1.ID);
var result = Session.QueryOver<Events>(() => x)
.Where(Restrictions.Disjunction()
.Add(() => x.Owner.ID == 1)
.Add(() => x.EVType.ID == 123)
.Add(Subqueries.WhereExists(subquery))
)
.Select(Projections.Distinct(Projections.Property(() => x.StartDate)))
.List<DateTime>();
请注意,您必须在子查询中设置 "manually" JOIN 条件。