代码改变世界

EF动态拼接查询

2017-06-15 17:24  NO.27  阅读(1928)  评论(0编辑  收藏  举报

1、业务中遇到个问题,需要查询如某表的id为1或者2或者3,这里是根据传递参数获取如:传递1,2或者1,3或者1,2,3这里在sql中很好拼接如下:

select * from table where id =1 or id=2 or name=3//3代表另一个字段

2、但是如果是EF语句就不好实现了,因为参数不是固定的,踌躇半天写出个很low的代码如下:

                if (list.Length == 1)
                {
                    if (list[0] == "3")
                    {
                        finalMainEvent = finalMainEvent.Where(i => i.EU_IsRemove == (int)PublicEnmu.IsOperate.Yes);
                    }
                    else
                    {
                        int curId = GetDicValue(list[0]);
                        finalMainEvent = finalMainEvent.Where(i => i.Evt_EventSatus == curId);
                    }
                }
                else if (list.Length == 3)
                {
                    finalMainEvent = finalMainEvent.Where(i => (i.EU_IsRemove == (int)PublicEnmu.IsOperate.Yes || i.Evt_EventSatus == 33 || i.Evt_EventSatus == 34));
                }
                else
                {
                    if (list.Contains("3"))
                    {
                        var cur = list.Where(i => !i.Contains("3")).FirstOrDefault();
                        int curId = GetDicValue(cur);
                        finalMainEvent = finalMainEvent.Where(i => (i.EU_IsRemove == (int)PublicEnmu.IsOperate.Yes || i.Evt_EventSatus == curId));
                    }
                    else
                    {
                        finalMainEvent = finalMainEvent.Where(i => (i.Evt_EventSatus == 33 || i.Evt_EventSatus == 34));
                    }
                }

虽然解决了问题,但是这样写扩展性太不好,如果不只有三个参数,有30个参数呢?

3、看了半天网络,看到个Expand方法可以实现,解决步骤如下:

A:首先通过Nuget下载Linqkit安装到对应项目

B:页面引入命名空间:using LinqKit;

C:添加扩展类如下:

    public static class PredicateBuilder
    {
        public static Expression<Func<T, bool>> True<T>() { return f => true; }
        public static Expression<Func<T, bool>> False<T>() { return f => false; }

        public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1,
                                                            Expression<Func<T, bool>> expr2)
        {
            var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
            return Expression.Lambda<Func<T, bool>>
                  (Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters);
        }

        public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1,
                                                             Expression<Func<T, bool>> expr2)
        {
            var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
            return Expression.Lambda<Func<T, bool>>
                  (Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters);
        }
    }

D:修改逻辑如下:

 if (list.Count() > 0)
                {
                    var predicate = PredicateBuilder.False<NewEventListItem>();
                    foreach (var item in list)
                    {
                        if (item != "3")
                        {
                            int curId = GetDicValue(list[0]);
                            predicate = predicate.Or(a => a.Evt_EventSatus == curId);

                        }
                        else
                        {
                            predicate = predicate.Or(a => a.EU_IsRemove == (int)PublicEnmu.IsOperate.Yes);
                        }
                    }
                    finalMainEvent = finalMainEvent.AsExpandable().Where(predicate);
                }

注意1:这里要加入AsExpandable()方法或者.Expand(),否则会报错(在指定的 LINQ to Entities 查询表达式中未绑定参数“a”。)

注意2:上述代码False很重要,因为如果写为True会影响查询结果,类似如下效果:

 

select * from table where name='一班' or id=1 or id=2 or id=3

所以需要改为False实现如下效果:

select * from table where name='一班' and (id=1 or id=2 or id=3)

这样问题就解决了。参考文章:

http://www.albahari.com/nutshell/predicatebuilder.aspx

http://blog.csdn.net/laokaizzz/article/details/28439447

最近又发现一个纯依赖于表达式树的功能,无需任何插件(因为引用linqkit插件会在系统中该项目和引用该项目的其他项目的配置文件添加配置信息):

                //使用表达式树方式
                Expression<Func<NewEventListItem, bool>> total = i => false;
                ParameterExpression paraExp = Expression.Parameter(typeof(NewEventListItem), "x");
                Expression totalR = Expression.Constant(false);
                foreach (var item in list)
                {

                    if (item!="3") {
                        
                        int curId = GetDicValue(item);
                        Expression status = Expression.Property(paraExp, typeof(NewEventListItem).GetProperty("Evt_EventSatus"));
                        Expression paraExp__Dm_GUID__Equal = Expression.Equal(Expression.Convert(status,typeof(int)), Expression.Constant(curId));                        
                        
                        totalR = Expression.Or(paraExp__Dm_GUID__Equal, totalR);
                    }else
                    {
                        Expression status = Expression.Property(paraExp, typeof(NewEventListItem).GetProperty("EU_IsRemove"));
                        Expression paraExp__Dm_GUID__Equal = Expression.Equal(Expression.Convert(status, typeof(int)), Expression.Constant((int)PublicEnmu.IsOperate.Yes));

                        totalR = Expression.Or(paraExp__Dm_GUID__Equal, totalR);
                    }
                }

                total = Expression.Lambda<Func<NewEventListItem, bool>>(totalR, paraExp);
                var totalres = finalMainEvent.Where(total);

 

 

感谢!有不对的地方欢迎指正!