现在的位置: 首页 > 综合 > 正文

LinqToNhibernate又叫我郁闷了:无法使用表达式参数

2012年05月19日 ⁄ 综合 ⁄ 共 6451字 ⁄ 字号 评论关闭

上文说到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已经发布了,期待正式版~~~ 

抱歉!评论已关闭.