简体   繁体   English

Fluent nHibernate Query => QueryOver,Join,Distinct

[英]Fluent nHibernate Query => QueryOver, Join, Distinct

I try to change that query to QueryOver<> to be able to do the Distinct operation yet inside the (generated sql) query 我尝试将该查询更改为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();

I tried something like that 我尝试过类似的东西

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();

but then I got the Value can not be null. Parameter name: source 但后来我得到的Value can not be null. Parameter name: source Value can not be null. Parameter name: source exception. Value can not be null. Parameter name: source异常。 Any ideas how can I fix that query ? 任何想法如何修复该查询?

edit 编辑

thanks to the xanatos' answer, the final SQL query is correct (I used his 2nd approach): 感谢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 = ?)
)

"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 “在QueryOver中,使用空变量分配别名。变量可以在任何地方声明(但在运行时应该是空的/默认的。)然后编译器可以检查对变量的语法是否正确使用,但在运行时变量不是评估(它只是用作别名的占位符)。http://nhibernate.info/blog/2009/12/17/queryover-in-nh-3-0.html

Setting List<Receivers> t to empty collection as you did (as you have mentioned in comments) means that you check is event id in local empty collection - doesn't have sense at all. List<Receivers> t设置为空集合(如您在评论中所述)意味着您检查本地空集合中的事件ID - 根本没有意义。

You can try do your query with subquery (should work but i'm not sure, I wrote it without testing, "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();

Hopefully this answer can help others. 希望这个答案可以帮助别人。 This error was being caused by declaring 此错误是由声明引起的

List<Receivers> t = null;

followed by the query expression 后跟查询表达式

 t.Count(y => y.User.ID == 1) > 0

The QueryOver documentation states "The variable can be declared anywhere (but should be empty/default at runtime)." QueryOver文档声明“变量可以在任何地方声明(但在运行时应该为空/默认值)。” Since in this case, the place holder is a List, you must initialize it as an empty list. 由于在这种情况下,占位符是List,因此必须将其初始化为空列表。

List<Receivers> t = new List<Receivers>();

Otherwise, when you try to reference the Count method, or any other method on the placeholder object, the source (t) will be null. 否则,当您尝试引用Count方法或占位符对象上的任何其他方法时,source(t)将为null。

This however still leaves a problem as @fex and @xanatos, in which it makes no sense to reference Count() from the alias List t, as it won't convert into SQL. 然而,这仍然存在@fex和@xanatos的问题,其中从别名List t引用Count()是没有意义的,因为它不会转换为SQL。 Instead you should be creating a subquery. 相反,你应该创建一个子查询。 See their answers for more comprehensive answer. 查看他们的答案以获得更全面的答案

As written by @fex, you can't simply do a new List<Receivers> . 正如@fex所写,你不能简单地做一个new List<Receivers> The problem is that you can't mix QueryOver with "LINQ" (the t.Count(...) part). 问题是你不能将QueryOver与“LINQ”( t.Count(...)部分)混合使用。 The QueryOver "parser" tries to execute "locally" the t.Count(...) instead of executing it in SQL. QueryOver“解析器”尝试“本地”执行t.Count(...)而不是在SQL中执行它。

As written by someone else, TransformUsing(Transformers.DistinctRootEntity) is client-side. 由其他人编写, TransformUsing(Transformers.DistinctRootEntity)是客户端。 If you want to do a DISTINCT server-side you have to use Projections.Distinct . 如果要进行DISTINCT服务器端,则必须使用Projections.Distinct

You have to make an explicit subquery. 您必须创建一个显式子查询。 Here there are two variants of the query. 这里有两种查询变体。 the first one is more similar to the LINQ query, the second one doesn't use the Count but uses the Exist (in LINQ you could have done the same by changing the Count(...) > 0 with a Any(...) 第一个更类似于LINQ查询,第二个不使用Count但使用Exist(在LINQ中,您可以通过使用Any(...) Count(...) > 0更改Count(...) > 0来完成相同的操作Any(...)

Note that when you use a .Select() you normally have to explicitly tell the NHibernate the type of the .List<something>() 请注意,当您使用.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>();

Note that you'll have to set "manually" the JOIN condition in the subquery. 请注意,您必须在子查询中“手动”设置JOIN条件。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM