|ZYCWPF| NHibernate.Linq中的Where条件如何进行分开写 谢谢
System.Linq.Expressions.Expression<Func<Model.Pub.PVHistory, bool>> where3 = l => l.PVType == 4
&& l.PVGuid == "1234"
&& l.PVIP == "127.0.0.1";
var count = Factory.CreateInstance.PVHistoryDao.NHibernateSession.Linq<Model.Pub.PVHistory>().Where(where3).Count();
using System.Linq;
using System.Linq.Expressions;
public static class LambdaExpressionExtensions
{
public static Expression<TFunc> And<TFunc>(this Expression<TFunc> expr1, Expression<TFunc> expr2)
{
//if (expr1.ReturnType != typeof(bool) || expr2.ReturnType != typeof(bool))
// throw new ArgumentException("both lambda expressions must return boolean type");
//if (expr1.Parameters.Zip(expr2.Parameters, (p1, p2) => p1.Type == p2.Type).Any(x => x == false))
// throw new ArgumentException("expr1 and expr2 must have exactly the same parameters");
var p = expr1.Parameters;
var left = Expression.Invoke(expr1, p.ToArray());
var right = Expression.Invoke(expr2, p.ToArray());
var expr = Expression.And(left, right);
return Expression.Lambda<TFunc>(expr, p);
}
}
System.Linq.Expressions.Expression<Func<Model.Pub.PVHistory, bool>> where1 = l => l.PVType == 4;
System.Linq.Expressions.Expression<Func<Model.Pub.PVHistory, bool>> where2 = l => l.PVGuid == "1234";
System.Linq.Expressions.Expression<Func<Model.Pub.PVHistory, bool>> where3 = l => l.PVIP == "127.0.0.1";
var where = where1.And(where2).And(where3);
var count = Factory.CreateInstance.PVHistoryDao.NHibernateSession.Linq<Model.Pub.PVHistory>().Where(where).Count();
当两个Where用扩展方法Add后,这个Lambda的目录树就不符合NhibernateLinq的规则了
那应该如何来修改这个NhibernateLinq的源码而能实现条件可以进行组合?
因为经常会出现要先判断后再加条件的需求,
我试了
System.Linq.Expressions.Expression<Func<Model.Pub.PVHistory, bool>> where3 = l => l.PVType == 4
&& (hasValue == true ? l.PVGuid == "1234" : true)
&& (hasName == true ? l.PVIP == "127.0.0.1" : true);
Nhibernate.Linq也不支持这种写法,
所以要有组条件的功能。
问:那要实现NHibernate.Linq的条件组合应该怎么来修改呢?
我用的Nhiernate版本为:2.1.2.4000
谢谢
[最优解释]
上次给出的方法是分别执行两个lambda表达式,并把结果进行and操作,用expression.Compile()可以看到表达式是这样的形式:
l => (Invoke(l => (Invoke(l => (l.PVType = 4),l) And Invoke(l => (l.PVGuid =
"1234"),l)),l) And Invoke(l => (l.PVIP = "127.0.0.1"),l))
也就是相当于:where = x => where1(x) && where2(x) && where3(x)
因为嵌套了lambda,所以这样的表达式无法转换成sql语句。
通常的做法就是像楼上说的把where“串联”起来就可以了:
var count = Factory.CreateInstance.PVHistoryDao.NHibernateSession.Linq<Model.Pub.PVHistory>().Where(where1).Where(where2).Where(where3).Count();
如果where的数量不确定,可以用数组来传递,像这样:
var criteria = new[] {where1, where2, where3};
var q = Factory.CreateInstance.PVHistoryDao.NHibernateSession.Linq<PVHistory>() as IQueryable<PVHistory>;
Array.ForEach(criteria, where =>q = q.Where(where));
var count = q.Count();
[其他解释]
where = x => where1(x) && where2(x) && where3(3)
[其他解释]
如果一定要用修改Expression的方式来实现,那就得把表达式做成下面这种形式:
l => (((l.PVType = 4) && (l.PVGuid = "1234")) && (l.PVIP = "127.0.0.1"))
因为这里实际上有三个参数“l”(虽然它们的名字相同,但实际三个lambda有三个不同的参数),所以要把第二,三个表达式的参数改成第一个。
这里有点麻烦的是做这样的修改要用到ExpressionVisitor,而3.5没有这个类,所以先得从下面这个链接去把ExpressionVisitor类完整复制过来:
http://msdn.microsoft.com/en-us/library/bb882521(v=vs.90)
然后再加上下面两个类:
这样就可以用 where = where1.And(where2).And(where3)这种形式了。
public class MyExpressionVisitor : ExpressionVisitor
{
private Expression[] _nodes1, _nodes2;
protected override Expression Visit(Expression node)
{
var index = Array.IndexOf(_nodes1, node);
return index >= 0 ? _nodes2[index] : base.Visit(node);
}
public Expression Replace(Expression expr, Expression[] nodes1, Expression[] nodes2)
{
_nodes1 = nodes1;
_nodes2 = nodes2;
return Visit(expr);
}
}
public static class LambdaExpressionExtensions
{
public static Expression<TFunc> And<TFunc>(this Expression<TFunc> expr1, Expression<TFunc> expr2)
{
if (expr1.Type.GetMethod("Invoke").ReturnType != typeof(Boolean))
throw new ArgumentException("lambda expressions must return boolean type");
var p = expr1.Parameters.ToArray();
var right = new MyExpressionVisitor().Replace(expr2.Body, expr2.Parameters.ToArray(), p);
var expr = Expression.AndAlso(expr1.Body, right);
return Expression.Lambda<TFunc>(expr, p);
}
}