DataRow.Field Expression [DataTable动态linq]

我们一般在学习Linq查询时,查询条件都是写死的。但是我们在实际使用过程中肯定不能这样,而是需要动态创建Linq查询条件,这里我们就需要用到Linq.Expression,用Expression来创建查询条件。

            DataTable dt = new DataTable();
            dt.Columns.Add("ID", typeof(int));
            dt.Columns.Add("Name", typeof(string));
            dt.Columns.Add("Height", typeof(float));
            for(int i=0; i<1000;i++)
            {
                dt.Rows.Add(i + 1, "HSC" + (i + 1).ToString(), (i + 1) * 1.21);
            }

我们这里用DataTable为数据源举例,因为网上linq to DataTable的动态查询示例实在太少了。请见下面代码:我们有一个3列的DataTable dt,我们需要筛选出Height<5.0的所有行。

如果写死查询条件的话非常简单,就下面一句话:

var query = dt.AsEnumerable().Where(s => s.Field<float>("Height") < 5.0).Select(s => s);

或者用函数委托,如下面的写法,但是这也是写死的查询。 

Func<DataRow, bool> fun = r => r.Field<float>("Height") < 5.0;
var q = dt.AsEnumerable().Where(fun).Select(r => r);

或者用下面的写死的Expression也可以。 

Expression<Func<DataRow, bool>> exp = s => s.Field<float>("Height")<5.0;
var q1 = dt.AsEnumerable().Where(exp.Compile()).Select(s => s);

 但是我们需要的是动态创建查询条件,在网上搜索了2天,看了各种关于Expression和Expression tree的介绍,如果只是通过对象的属性来筛选的话还是比较简单的,用ParameterExpresssion表示DataRow, MemberExpression表示属性,ConstanExpress表示常量,最后在用一下Expression.LessThan()就可以了。但是我们如果把DataRow.Field当作属性来用(我一开始就是这么做的),那编译始终不过。Field是DataRow的一个方法,这里我们就要用反射来获取方法,再用MethodCallExpression表示DataRow.Field()方法,然后在获取对应列的数据DataRow.Field(“Height"),才可以拿来比较用。

但是网络上关于如何使用DataRow.Field()方法反射的Express实在是太少了,看了N多文章还是一知半解。就算有一些看似可以的方法,也没有写清楚如何在Linq里面调用。

这里我把我摸索出来的成功的方法写出来供大家参考,希望大家可以少走一些弯路。

        private Func<DataRow, bool> GetLambdaExp()
        {
            ParameterExpression r = Expression.Parameter(typeof(DataRow), "r"); //DataRow参数:r

            ConstantExpression expHeight = Expression.Constant("Height", typeof(string)); //"Height" 字符串常量
            List<Expression> list = new List<Expression>();
            list.Add(r); list.Add(expHeight); //参数列表:DataRow, "Height"
            //获取Field方法反射 MethodInfo,!!!一定需要MakeGenericMethod!!!, 其中的float类型为r.Field<float>("Height")的返回值类型
            MethodInfo mi = typeof(DataRowExtensions).GetMethod("Field", new Type[] { typeof(DataRow), typeof(string) }).MakeGenericMethod(typeof(float));
            MethodCallExpression MC = Expression.Call(null, mi, list); //创建MethodCallExpression

            ConstantExpression c5 = Expression.Constant((float)5.0, typeof(float));//创建float常量5.0
            Expression con = Expression.LessThan(MC, c5); //Expression: r.Field<float>("Height")<5.0
            var expLam = Expression.Lambda<Func<DataRow, bool>>(con, r);//Func<DataRow, bool> Expression

            //var q = dt.AsEnumerable().Where(expLam.Compile()).Select(s => s);//如果需要直接调用

            return expLam.Compile();
        }

最后Linq调用我们创建好的动态查询Expression

var q = dt.AsEnumerable().Where(GetLambdaExp()).Select(r => r);
//用Expression动态创建和上面类似的查询

这里只是用MethodInfo和Expression实现了对于DataRow的Field的最基本的动态查询函数,各位自己再完善一下,加入各种组合查询条件。 

-------------------------------------------------------------------- 分割线 ---------------------------------------------------------------

这里我又做了一个自定义的动态Linq查询,自定义的查询条件存储在Dictionary里面,然后根据Dictionary里面的条件创建动态Linq Expression查询。

private void bt_TestMapping_Click(object sender, EventArgs e)
        {
            Dictionary<string, string> FilterExpression = new Dictionary<string, string>();
            FilterExpression.Add("`Height`>5.0", "AND");
            FilterExpression.Add("`ID`<=10", "AND");
            FilterExpression.Add("`Name`!='HSC100'", "AND");
            FilterExpression.Add("`Age`>2", "AND");
            FilterExpression.Add("`Weight`>1.20", "AND");

            ParameterExpression r = Expression.Parameter(typeof(DataRow), "r"); //DataRow参数:r
            Expression con = Expression.Constant(true); //All nested conditions
            foreach (string key in FilterExpression.Keys)
            {
                string[] conArr = SplitConditionStr(key);
                Type FieldType = dt.Columns[conArr[0]].DataType;
                
                ConstantExpression expFieldName = Expression.Constant(conArr[0], typeof(string)); //"Height" 字符串常量
                ConstantExpression expValue;// = Expression.Constant(conArr[2], FieldType);//创建float常量5.0 }
                switch (FieldType.Name)
                {
                    case "Single": { expValue = Expression.Constant(float.Parse(conArr[2]), FieldType); } break;
                    case "Double": { expValue = Expression.Constant(double.Parse(conArr[2]), FieldType); } break;
                    case "Int32": { expValue = Expression.Constant(int.Parse(conArr[2]), FieldType); } break;
                    case "Int64": { expValue = Expression.Constant(long.Parse(conArr[2]), FieldType); } break;
                    default: { expValue = Expression.Constant(conArr[2], FieldType); } break;
                }
                
                List<Expression> list = new List<Expression>();
                list.Add(r); list.Add(expFieldName); //参数列表:DataRow, "Height"
                MethodInfo mi = typeof(DataRowExtensions).GetMethod("Field", new Type[] { typeof(DataRow), typeof(string) }).MakeGenericMethod(FieldType);
                MethodCallExpression MC = Expression.Call(null, mi, list); //创建MethodCallExpression

                Expression SingleExpCon; 
                switch (conArr[1])
                {
                    case ">=": { SingleExpCon = Expression.GreaterThanOrEqual(MC, expValue); } break;
                    case "<=": { SingleExpCon = Expression.LessThanOrEqual(MC, expValue); } break;
                    case "!=": { SingleExpCon = Expression.NotEqual(MC, expValue); } break;
                    case ">": { SingleExpCon = Expression.GreaterThan(MC, expValue); } break;
                    case "<": { SingleExpCon = Expression.LessThan(MC, expValue); } break;
                    case "=": { SingleExpCon = Expression.Equal(MC, expValue); } break;
                    default: { SingleExpCon = Expression.Equal(MC, expValue); } break;
                }

                switch (FilterExpression[key].ToUpper())
                {
                    case "AND": { con = Expression.And(con, SingleExpCon); } break;
                    case "OR": { con = Expression.Or(con, SingleExpCon); }; break;
                    case "AND NOT": { con = Expression.And(con, Expression.Not(SingleExpCon)); }; break;
                    case "OR NOT": { con = Expression.Or(con, Expression.Not(SingleExpCon)); }; break;
                    default: con = Expression.Add(con, SingleExpCon); break;
                }
            }

            //MessageBox.Show(con.ToString());

            var expLam = Expression.Lambda<Func<DataRow, bool>>(con, r);//Func<DataRow, bool> Expression
            var q = dt.AsEnumerable().Where(expLam.Compile()).Select(s => s);
            foreach (var row in q)
            {
                dataGridView1.Rows[dt.Rows.IndexOf(row)].Selected = true;
            }
        }
       string[] SplitConditionStr(string Condition)
        {
            string[] cons = new string[3];
            if (Condition.Contains("!="))
            {
                cons = Regex.Split(Condition, @"!=");
                return new string[] { cons[0].Replace("`", "").Replace("'", ""), @"!=", cons[1].Replace("`", "").Replace("'", "") };
            }
            else if (Condition.Contains(">="))
            {
                cons = Regex.Split(Condition, @">=");
                return new string[] { cons[0].Replace("`", "").Replace("'", ""), @">=", cons[1].Replace("`", "").Replace("'", "") };
            }
            else if (Condition.Contains("<="))
            {
                cons = Regex.Split(Condition, @"<=");
                return new string[] { cons[0].Replace("`", "").Replace("'", ""), @"<=", cons[1].Replace("`", "").Replace("'", "") };
            }
            else
            {
                char[] comparetors = new char[] { '=', '>', '<' };
                cons = Condition.Split(comparetors);
                if (Condition.Contains("="))
                {
                    return new string[] { cons[0].Replace("`", "").Replace("'", ""), @"=", cons[1].Replace("`", "").Replace("'", "") };
                }
                else if (Condition.Contains(">"))
                {
                    return new string[] { cons[0].Replace("`", "").Replace("'", ""), @">", cons[1].Replace("`", "").Replace("'", "") };
                }
                else//(Condition.Contains("<"))
                {
                    return new string[] { cons[0].Replace("`", "").Replace("'", ""), @"<", cons[1].Replace("`", "").Replace("'", "") };
                }
            }
        }

 

 

 

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值