流利的nHibernate查询=> QueryOver,Join,Distinct

时间:2015-02-19 15:49:00

标签: c# fluent-nhibernate

我尝试将该查询更改为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&#39;回答,最后的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 = ?)
)

3 个答案:

答案 0 :(得分:3)

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

List<Receivers> t设置为空集合(正如您在评论中所提到的)意味着您检查本地空集合中的事件ID - 根本没有意义。

您可以尝试使用子查询进行查询(应该可以工作,但我不确定,我是在没有测试的情况下编写的,“手工”):

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

答案 1 :(得分:2)

希望这个答案可以帮助别人。此错误是由声明

引起的
List<Receivers> t = null;

后跟查询表达式

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

QueryOver文档声明“变量可以在任何地方声明(但在运行时应该为空/默认值)。”由于在这种情况下,占位符是List,您必须将其初始化为空列表。

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

否则,当您尝试引用Count方法或占位符对象上的任何其他方法时,source(t)将为null。

然而,这仍然存在@fex和@xanatos的问题,其中从别名List t引用Count()是没有意义的,因为它不会转换为SQL。相反,你应该创建一个子查询。请参阅他们的答案以获得更全面的答案。

答案 2 :(得分:2)

正如@fex所写,你不能简单地做new List<Receivers>。问题是你不能将QueryOver与“LINQ”(t.Count(...)部分)混合使用。 QueryOver“解析器”尝试“本地”执行t.Count(...),而不是在SQL中执行它。

正如其他人所写,TransformUsing(Transformers.DistinctRootEntity)是客户端。如果要进行DISTINCT服务器端,则必须使用Projections.Distinct

您必须创建一个显式子查询。这里有两种查询变体。第一个更类似于LINQ查询,第二个不使用Count但使用Exist(在LINQ中,您可以通过使用{{1更改Count(...) > 0来完成相同操作}}

请注意,当您使用Any(...)时,通常必须明确告诉NHibernate .Select()的类型

.List<something>()

请注意,您必须在子查询中“手动”设置JOIN条件。