上文说到LinqToNhibernate的DateTime处理上存在一个陷阱。仔细想想的话,其实应该不仅仅是针对DateTime,而是LinqToNhibernate只能处理到hbm映射过的property这一个级别,再取property的property的时候就会有一些莫名其妙的问题。
这次来谈谈今天写代码的时候碰到的另一个问题:无法使用表达式参数。
先来看看我原来写的代码:
, 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。。。囧。。。
重新审视一下异常堆栈
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语句的时候调用委托好了,比如说当我把代码改成这样,就可以顺利执行了
, 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已经发布了,期待正式版~~~