• 简单的Asp.net mvc 里动态生成Linq的Ef查询扩展


    *要解决的问题

    需求总是改变,有的甚至不能叫需求,一个小小的请求:帮我加个"用户名"的查询条件吧,我想有"注册时间"范围查询,"三围"可以有吗?

    我们需要一个简单,好用,强大的后台,那么这些可以有,应该有!

    *怎么解决

    因为一个小小请求的修改,都要修改cs程序,重新编译发布的项目,不是一个好项目,特别是一个大点的项目

    ,发布会出现版本问题,而请求人员希望问题马上可以得到解决,那么解决的方法就显而易见了:

    只修改模板,满足查询,验证等问题:

    关于模板里实现验证的方法请查看我的上篇博文ASP.NET MVC 3.0前后台统一验证类UniValidate,附源码

    关于模板里实现动态查询,现在开始介绍:

    我们要现实的功能很简单:通过增加表单控件就可以加入新的查询条件

    类似的页面如下

    *开始实现

    我要实现一个直接接受form,生成动态查询的ef扩展

    /// <summary>通过页面控件动态构建查询</summary>

    public static IQueryable<TSource> WhereDynamic<TSource>(this IQueryable<TSource> source,

    NameValueCollection nameValues) where TSource : class

    重载扩展

    public static IQueryable<TSource> WhereDynamic<TSource>(this IQueryable<TSource> source,

    HttpRequestBase request) where TSource : class

    {

    return WhereDynamic(source, new NameValueCollection { request.QueryString, request.Form });

    }

    从这个方法出发,思路就很简单了,就是取得form里的值,动态构建表达式树,有的同学会有疑问,为什么不用对象做为参数

    1. 因为form的控件名字部分需要有查询关系的标注,名字与model名字不一致,这样要得到对象,要重写ModelBinder
    2. 表达树已经有了类型检查, ModelBinder也有,有重复,而且加大了复杂性.

    生成表达树的代码

            /// <summary>通过页面控件动态构建查询</summary>
            public static IQueryable<TSource> WhereDynamic<TSource>(this IQueryable<TSource> source,
                                    NameValueCollection nameValues) where TSource : class
            {
                if (nameValues.Count > 0)
                {
                    //构建 c=>Body中的c
                    ParameterExpression param = Expression.Parameter(typeof(TSource), "c");
                    //构建c=>Body中的Body
                    var body = GetExpressoinBody(param, nameValues);
                    if (body != null)
                    {
                        //将二者拼为c=>Body
                        var expression = Expression.Lambda<Func<TSource, bool>>(body, param);
                        //传到Where中当做参数,类型为Expression<Func<T,bool>>
                        return source.Where(expression);
                    }
                }
                return source;
            }
            /// <summary>构建body</summary>
            private static Expression GetExpressoinBody(ParameterExpression param, NameValueCollection nameValues)
            {
                var list = new List<Expression>();
                if (nameValues.Count > 0)
                {
                    var plist = param.Type.GetRuntimeProperties().ToDictionary(z => z.Name);//可以加缓存改善性能
                    foreach (var item in nameValues.AllKeys)
                        if (item.EndsWith(">"))//可能大小查询
                        {
                            string key = item.TrimEnd('>');
                            if (!plist.ContainsKey(key) || nameValues[item].Length <= 0) continue;
                            var rType = plist[key].GetMethod.ReturnType;
                            if (rType == typeof(string)) continue;
                            var e1 = Expression.Property(param, key);
                            object dValue;
                            if (TryParser(nameValues[item], rType, out dValue))
                                list.Add(Expression.GreaterThan(e1, Expression.Constant(dValue)));
                        }
                        else if (item.EndsWith("<"))//可能大小查询
                        {
                            string key = item.TrimEnd('<');
                            if (!plist.ContainsKey(key) || nameValues[item].Length <= 0) continue;
                            var rType = plist[key].GetMethod.ReturnType;
                            if (rType == typeof(string)) continue;
                            var e1 = Expression.Property(param, key);
                            object dValue;
                            if (TryParser(nameValues[item], rType, out dValue))
                            {
                                if (rType == typeof(DateTime)) dValue = ((DateTime)dValue).AddDays(1);
                                list.Add(Expression.LessThan(e1, Expression.Constant(dValue)));
                            }
                        }
                        else if (plist.ContainsKey(item) && nameValues[item].Length > 0)
                        {
                            var e1 = Expression.Property(param, item);
                            var rType = plist[item].GetMethod.ReturnType;
                            if (rType == typeof(string))//可能是like查询
                            {
                                var value = nameValues[item].Trim('%');
                                var e2 = Expression.Constant(value);
                                if (nameValues[item].Length - value.Length >= 2)
                                    list.Add(Expression.Call(e1, "Contains", null, new Expression[] { e2 }));
                                else if (nameValues[item].StartsWith("%"))
                                    list.Add(Expression.Call(e1, "EndsWith", null, new Expression[] { e2 }));
                                else if (nameValues[item].EndsWith("%"))
                                    list.Add(Expression.Call(e1, "StartsWith", null, new Expression[] { e2 }));
                                else
                                    list.Add(Expression.Equal(e1, e2));
                            }
    
                            else if (nameValues[item].IndexOf(",") > 0)//可能是in查询
                            {
                                if (rType == typeof(short))
                                {
                                    var searchList = TryParser<short>(nameValues[item]);
                                    if (searchList.Any())
                                        list.Add(Expression.Call(Expression.Constant(searchList), "Contains", null, new Expression[] { e1 }));
                                }
                                else if (rType == typeof(int))
                                {
                                    var searchList = TryParser<int>(nameValues[item]);
                                    if (searchList.Any())
                                        list.Add(Expression.Call(Expression.Constant(searchList), "Contains", null, new Expression[] { e1 }));
                                }
                                else if (rType == typeof(long))
                                {
                                    var searchList = TryParser<long>(nameValues[item]);
                                    if (searchList.Any())
                                        list.Add(Expression.Call(Expression.Constant(searchList), "Contains", null, new Expression[] { e1 }));
                                }
                            }
                            else
                            {
                                object dValue;
                                if (TryParser(nameValues[item], rType, out dValue))
                                    list.Add(Expression.Equal(e1, Expression.Constant(dValue)));
                            }
                        }
                }
                return list.Count > 0 ? list.Aggregate(Expression.AndAlso) : null;
            }
    

     只处理大于,小于,in,like,等于的操作,其他的可以自己添加,因为大于小于可能为同一个字段,所以查询关系标注在了名称里了

    *其他函数,用于类型转换

    private static List<T> TryParser<T>(string value)

    {

    string[] searchArray = value.Split(',');

    List<T> dList = new List<T>();

    foreach (var l in searchArray)

    {

    try

    {

    T dValue = (T)Convert.ChangeType(l, typeof(T));

    dList.Add(dValue);

    }

    catch { }

    }

    return dList;

    }

    private static bool TryParser(string value, Type outType, out object dValue)

    {

    try

    {

    dValue = Convert.ChangeType(value, outType);

    return true;

    }

    catch

    {

    dValue = null;

    return false;

    }

    }

    *动态处理分页

    public static IQueryable<TSource> Page<TSource>(this IQueryable<TSource> source, DataPage dp) where TSource : class

    {

    dp.RowCount = source.Count();

    Type type = typeof(TSource);

    ParameterExpression param = Expression.Parameter(type, "c");

    string callMethod = "OrderByDescending";

    PropertyInfo property;

    if (dp.OrderField == null)

    property = type.GetRuntimeProperties().First();

    else

    {

    //处理正反排序

    string[] orderFileds = dp.OrderField.Split(' ');

    if (orderFileds.Length == 2)

    {

    dp.OrderField = orderFileds[0].Trim();

    if (String.Compare(orderFileds[1].Trim(), "asc", StringComparison.OrdinalIgnoreCase) == 0) callMethod = "OrderBy";

    }

    property = type.GetProperty(dp.OrderField) ?? type.GetRuntimeProperties().First();

    }

    LambdaExpression le = Expression.Lambda(Expression.MakeMemberAccess(param, property), param);

    MethodCallExpression resultExp = Expression.Call(typeof(Queryable), callMethod, new[] { type, property.PropertyType }

    , source.Expression, Expression.Quote(le));

    return source.Provider.CreateQuery<TSource>(resultExp).Skip((dp.PageIndex - 1) * dp.PageSize).Take(dp.PageSize);

    }

    *如何使用,主要体现在表单里

    名字<input name="username" type="text" style="width: 40px" />正常的查询,如果值里有%号处理成like查询

    ID<input name="id" type="text" value="62" style="width: 30px" /><input name="id" type="text" value="63" style="width: 30px" /> in查询

    <input name="addtime>" type="text" value="" style="width: 80px" /><input name="addtime<" type="text" value="" style="width: 80px" /> 大于小于查询

    Controller里的调用

    public PartialViewResult WherePage(DataPage dp)

    {

    dp.PageSize = 10;

    var tempList = _dbEntities.act_comment.WhereDynamic(Request).Page(dp);

    ViewBag.sql = tempList.ToString();

    return View(tempList.ToList());

    }

    Ef扩展的好处是查询条件可以串接,如上例可以再加查询条件

    var tempList = _dbEntities.act_comment.WhereDynamic(Request).Where(z=>z.forid==1).Page(dp);

    *其他

    参考了重典 http://www.cnblogs.com/chsword/archive/2010/12/27/searchmodel_1.html ,原理相同,里面有的,我这都没有细说,比如比拼接sql的优点,

    关于表达式树可以学习脑袋的: http://www.cnblogs.com/Ninputer/archive/2009/08/28/expression_tree1.html

    代码是从老项目copy出来的,没有单独的项目,看需要,如果有必要,可以考虑整理一份源码,代码上面都有.

  • 相关阅读:
    获取系统当前时间
    使用键盘控制窗体的移动
    打开和关闭输入法编辑器
    屏蔽系统的Ctrl+c/x/v操作
    按Esc键实现关闭窗体
    屏蔽Alt+F4关闭窗体
    将回车键转换为Tab键
    node.js入门学习(六)--express
    curl POST如何查看响应的Header(转)
    node.js入门学习(五)--Demo模块化改造
  • 原文地址:https://www.cnblogs.com/ark/p/2860966.html
Copyright © 2020-2023  润新知