上文说到LinqToNhibernate的DateTime处理上存在一个陷阱。仔细想想的话,其实应该不仅仅是针对DateTime,而是LinqToNhibernate只能处理到hbm映射过的property这一个级别,再取property的property的时候就会有一些莫名其妙的问题。
这次来谈谈今天写代码的时候碰到的另一个问题:无法使用表达式参数。
先来看看我原来写的代码:
代码
public
IQueryable
<
ChildEntity
>
FindForIVOC(string bu, Guid centreId, string name, string idNo, IEnumerable
<
Guid
>
programIds
, Guid? serviceId, string className)
{
return
Session.Linq
<
ChildEntity
>
(entityName)
.
Where
(child
=>
child.Centre.Id.Equals(centreId)
&&
child.Name.
Contains
(name)
&&
child.IdNo.
Contains
(idNo))
.
Where
(JoinProgram(programIds, now))
.
Where
(JoinService(serviceId, now))
.
Where
(JoinClass(className, now))
.OrderBy(OrderByIdNo())
.ThenBy(OrderById());
}
为了以后方便的使用其中一部分的where子句,我理所当然的建立了JoinProgram、JoinService、JoinClass等等几个方法返回Expression<Func<ChildEntity, bool>>对象
之后我在得到的IQueryable<TEntity>对象上调用Count(),结果跑了异常:Expression argument must be of type ICollection.
搜索了一下这个异常message,没有太多的结果,只是这里提了一下,说解决之道是把IQueryable换乘ICollection。。。囧。。。
重新审视一下异常堆栈
代码
[InvalidOperationException: Expression argument must be of type ICollection.]
NHibernate.Linq.Visitors.WhereArgumentsVisitor.GetCollectionContainsCriteria(Expression list, Expression containedExpr)
+
194
NHibernate.Linq.Visitors.WhereArgumentsVisitor.VisitMethodCall(MethodCallExpression expr)
+
388
NHibernate.Linq.Visitors.ExpressionVisitor.Visit(Expression exp)
+
575
NHibernate.Linq.Visitors.NHibernateExpressionVisitor.Visit(Expression exp)
+
274
NHibernate.Linq.Visitors.WhereArgumentsVisitor.VisitAndAlsoExpression(BinaryExpression expr)
+
96
NHibernate.Linq.Visitors.WhereArgumentsVisitor.VisitBinary(BinaryExpression expr)
+
52
NHibernate.Linq.Visitors.ExpressionVisitor.Visit(Expression exp)
+
194
NHibernate.Linq.Visitors.NHibernateExpressionVisitor.Visit(Expression exp)
+
274
NHibernate.Linq.Visitors.WhereArgumentsVisitor.GetExistsCriteria(MethodCallExpression expr)
+
493
NHibernate.Linq.Visitors.WhereArgumentsVisitor.VisitMethodCall(MethodCallExpression expr)
+
1234
NHibernate.Linq.Visitors.ExpressionVisitor.Visit(Expression exp)
+
575
NHibernate.Linq.Visitors.NHibernateExpressionVisitor.Visit(Expression exp)
+
274
NHibernate.Linq.Visitors.ExpressionVisitor.VisitConditional(ConditionalExpression c)
+
62
NHibernate.Linq.Visitors.ExpressionVisitor.Visit(Expression exp)
+
319
NHibernate.Linq.Visitors.NHibernateExpressionVisitor.Visit(Expression exp)
+
274
NHibernate.Linq.Visitors.ExpressionVisitor.VisitLambda(LambdaExpression lambda)
+
21
NHibernate.Linq.Visitors.ExpressionVisitor.Visit(Expression exp)
+
639
NHibernate.Linq.Visitors.NHibernateExpressionVisitor.Visit(Expression exp)
+
274
NHibernate.Linq.Visitors.WhereArgumentsVisitor.VisitUnary(UnaryExpression expr)
+
34
NHibernate.Linq.Visitors.ExpressionVisitor.Visit(Expression exp)
+
140
NHibernate.Linq.Visitors.NHibernateExpressionVisitor.Visit(Expression exp)
+
274
NHibernate.Linq.Visitors.RootVisitor.HandleWhereCall(MethodCallExpression call)
+
92
NHibernate.Linq.Visitors.RootVisitor.VisitMethodCall(MethodCallExpression expr)
+
907
NHibernate.Linq.Visitors.ExpressionVisitor.Visit(Expression exp)
+
575
NHibernate.Linq.Visitors.NHibernateExpressionVisitor.Visit(Expression exp)
+
274
NHibernate.Linq.Visitors.RootVisitor.VisitMethodCall(MethodCallExpression expr)
+
53
NHibernate.Linq.Visitors.ExpressionVisitor.Visit(Expression exp)
+
575
NHibernate.Linq.Visitors.NHibernateExpressionVisitor.Visit(Expression exp)
+
274
NHibernate.Linq.Visitors.NHibernateQueryTranslator.TranslateInternal(Expression expression)
+
81
NHibernate.Linq.Visitors.NHibernateQueryTranslator.Translate(Expression expression, QueryOptions queryOptions)
+
43
NHibernate.Linq.NHibernateQueryProvider.TranslateExpression(Expression expression)
+
575
NHibernate.Linq.NHibernateQueryProvider.Execute(Expression expression)
+
17
NHibernate.Linq.QueryProvider.System.Linq.IQueryProvider.Execute(Expression expression)
+
13
System.Linq.Queryable.Count(IQueryable`
1
source)
+
310
SNBusinessLogic.ServiceImpl.Pager.Page(IQueryable`
1
entities, Int32 startRow, Int32 maxResult)
in
D:\singapore\skoolnet\src\trunk\SkoolNet\SNBusinessLogic\ServiceImpl\Pager.cs:
12
SNBusinessLogic.ServiceImpl.ChildServiceImpl.FindForIVOC(Guid centreId, String name, String idNo, IEnumerable`
1
programIds, Nullable`
1
serviceId, String className, Int32 startRow, Int32 maxResult)
in
D:\singapore\skoolnet\src\trunk\SkoolNet\SNBusinessLogic\ServiceImpl\ChildServiceImpl.cs:
49
SNUserControls.Child.SearchChild.LoadSearchResults()
in
D:\singapore\skoolnet\src\trunk\SkoolNet\SNUserControls\Child\SearchChild.ascx.cs:
101
SNUserControls.Child.SearchChild.btnSearch_Click(Object sender, EventArgs e)
in
D:\singapore\skoolnet\src\trunk\SkoolNet\SNUserControls\Child\SearchChild.ascx.cs:
71
System.Web.UI.WebControls.ImageButton.OnClick(ImageClickEventArgs e)
+
98
System.Web.UI.WebControls.ImageButton.RaisePostBackEvent(String eventArgument)
+
161
System.Web.UI.Page.RaisePostBackEvent(IPostBackEventHandler sourceControl, String eventArgument)
+
29
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
+
2981
发现异常是在处理where子句的地方抛出来的。为了定位错误,只留下一个参数为Func的where子句,OK了,在加上一个where子句就发生上面的错误。
到网上找了找,虽然没有找到明确的说明,但是给出的例子里多个where语句确实是用表达式树把多个Expression合并成一个Expression传入where的。
以我的理解能力而言,大量使用表达式树实在是对代码的可读性造成很大的破坏,我还是老老实实的让我的各个方法返回Func<ChildEntity, bool>然后拼where语句的时候调用委托好了,比如说当我把代码改成这样,就可以顺利执行了
代码
public
IQueryable
<
ChildEntity
>
FindForIVOC(
string
bu, Guid centreId,
string
name,
string
idNo, IEnumerable
<
Guid
>
programIds
, Guid
?
serviceId,
string
className)
{
var entityName
=
bu
+
ChildEntity.StaticEntityClassName;
var now
=
DateTime.Now;
return
Session.Linq
<
ChildEntity
>
(entityName)
.Where(child
=>
child.Centre.Id.Equals(centreId)
&&
child.Name.Contains(name)
&&
child.IdNo.Contains(idNo)
&&
JoinProgram(programIds, now).Invoke(child));
//
.Where(JoinService(serviceId, now))
//
.Where(JoinClass(className, now))
//
.OrderBy(OrderByIdNo())
//
.ThenBy(OrderById());
}
总之LinqToNHibernate还是无法像LinqToSql那样非常方便随意的使用,不知道NH3.0内置的Linq Provider会怎么样呢?上个周末NH3.0的Alpha1已经发布了,期待正式版~~~