首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > .NET > C# >

|ZYCWPF| NHibernate.Linq中的Where条件怎么进行分开写 多谢

2012-12-14 
|ZYCWPF| NHibernate.Linq中的Where条件如何进行分开写 谢谢System.Linq.Expressions.ExpressionFuncMod

|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();

如上是用一条Lambda写的Where条件,出来的结果是正确的,
但我想分开来写这个Where条件,所以用以下

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();


在NHibernate.Linq.Visitors 148行
case BinaryCriterionType.Value:
  return compareValueToCriteria(left.Value, right.Criteria);
会报:未将对像引用到对像实例

我想这是因为NhibernateLinq是根据他自己的表达式的书写要求后再转为Criteria和条件的


当两个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)
然后再加上下面两个类:


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);
}
}

这样就可以用 where = where1.And(where2).And(where3)这种形式了。

另外,nhibernate linq已经过时了,你应该下载最新的3.3版,可以直接用session.Query方法来查询。

[其他解释]
直接  Where(...).Where(...).Where(...) 

热点排行