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