簡單的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出來的,沒有單獨的項目,看需要,如果有必要,可以考慮整理一份源碼,代碼上面都有.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM