这两天一直想写一个动态查询的方式,先是网上查询了一下,发现大家写的差不多都是一样的【如:http://www.cnblogs.com/ASPNET2008/archive/2012/10/28/2743053.html#commentformhttp://www.cnblogs.com/lyj/archive/2008/03/25/1122157.html】,我觉得这里不好的地方就是在查询的时候还是要知道查询的是哪一个列,然后根据这个查询的列是否为空等等的一些判断来进行动态查询的拼接,

这样一来,每一个实体的查询都要对查询字段和数据进行逐一判断,如第一篇例子里面的

 if (planCondition.Project != ) { predicate = predicate.And(c => c.ProjectId == planCondition.Project); }

这里在查询时首先要知道查询的是【ProjectId】字段,然后再根据此字段是否为空来进行查询的拼接,我认为这样就很不灵活了。所以我就准备自己写一个。

我认为动态是这样的:查询的实体是动态的,查询的列是动态的,查询的数据是动态的,查询的方式[如:OR还是And查询,EQUAL查询还是Like查询]。最重要的一点就是动态查询的方法要灵活并且通用。

所以就自己尝试着写了一个方法,由于对Expression的了解很有限,因此在中间也是反复的尝试。刚开始实现的时候尝试了其他的一些方法,最后都因为动态列的类型未知而失败。不过最终还是实现了

下面是一个结合EasyUI 的一个例子:

页面数据:

页面查询的是一个【DataDictionary】实体的数据,查询的数据列是图上的那些(随着需求随时可以改变),点击【查询】按钮列表绑定查询的值。前台的的查询控件

    <table>
<tr>
<td>所属类型:</td>
<td>
<select id="SelParentID" name="SelParentID" style="width: 130px;"></select>
</td>
<td>字典名称:</td>
<td>
<input type="text" id="txtDataText" />
</td>
<td>序号:</td>
<td>
<input type="text" id="txtSortNumt" />
</td>
<td><a href="#" class="easyui-linkbutton" data-options="iconCls:'icon-search'" id="btnSearch" plain="true">查询</a></td>
</tr>
</table>

  按钮事件:

      //查询
$("#btnSearch").click(function () {
var jdata = { "[EQUAL][And]ParentID": 1, "[LIKE][And]DataText": "abc", "[EQUAL][And]SortNum": 789};
$("#DataDicList").datagrid("load", { queryJson: JSON.stringify(jdata) });
});

  

  这里就是使用的方式

    var jdata = { "[EQUAL][And]ParentID": $('#SelParentID').combobox("getValue"), "[LIKE][And]DataText": $("#txtDataText").val(), "[EQUAL][And]SortNum": $("#txtSortNumt").val() };  

在页面只需要指定查询的方式,查询连接的方式,查询字段和字段的值,后台调用的时候再加上要查询的实体,动态查询方式自己去解析就行了,直接把解析的Lambda返给我 ,我拿着他去数据库取数据就行了。

接下来看一下后台,加载列表数据的方法:

   public ActionResult GetAll(int page, int rows, string value, string queryJson)
{
var query = SpecificationBuilder.Create<DataDictionary>();
query.Equals(t => t.DelFlag, 0);
if (!string.IsNullOrEmpty(queryJson))
{
var predicate = HelpClass.GetSerchExtensions<DataDictionary>(queryJson);
query.Predicate = query.Predicate.And(predicate);
}
var allCount = 0;
var listModel = _dataDictionaryBLL.GetAllPage(query.Predicate, a => a.CreateDate, page, rows, out allCount);
var dateInfo = "{\"total\":" + allCount + ",\"rows\":" + JsonConvert.SerializeObject(listModel) + "}";
return Content(dateInfo);
}

  这里

 var query = SpecificationBuilder.Create<DataDictionary>();

  是我另外一个动态查询的例子【这里就是需要知道并指定查询的列名和数据值】,先不用管,关键就只用这样的一句

   var predicate = HelpClass.GetSerchExtensions<DataDictionary>(queryJson);

  得到的就是一个直接可以查询的Lambda表达式【如:{a => (True And (a.ParentID == 00000000-0000-0000-0000-000000000000))}】

下面就看一下这个方法的实现

  #region 把查询条件拼接为Extensions
/// <summary>
/// 把查询条件拼接为Extensions
/// </summary>
/// <typeparam name="TEntity">查询实体</typeparam>
/// <param name="searchJson">查询条件,例如:[like][or]name:123</param>
/// <returns></returns>
public static Expression<Func<TEntity, bool>> GetSerchExtensions<TEntity>(String searchJson) where TEntity : class, new()
{
try
{
var ja = (JArray)JsonConvert.DeserializeObject("[" + searchJson + "]"); //把查询条件转换为Json格式
var enumerableQuery = new EnumerableQuery<KeyValuePair<string, JToken>>(ja[0] as JObject);
return GetSerchExtensions<TEntity>(enumerableQuery);
}
catch (Exception)
{
return null;
}
} /// <summary>
/// 把查询条件拼接为Extensions
/// </summary>
/// <typeparam name="TEntity">查询实体</typeparam>
/// <param name="enumerableQuery"></param>
/// <returns></returns>
public static Expression<Func<TEntity, bool>> GetSerchExtensions<TEntity>(EnumerableQuery<KeyValuePair<string, JToken>> enumerableQuery) where TEntity : class,new()
{
ParameterExpression paramExp = Expression.Parameter(typeof(TEntity), "a");
if (null == enumerableQuery || !enumerableQuery.Any())
{
var valueEqual = Expression.Constant(1);
var expEqual = Expression.Equal(valueEqual, valueEqual);
return Expression.Lambda<Func<TEntity, bool>>(expEqual, paramExp); //如果参数为空,返回一个a=>1=1 的值 }
var modeltypt = typeof(TEntity); //实体类型
var keyList = enumerableQuery.Select(e => e.Key).ToList(); //取出Json 的每个字符串 Expression whereExp = null;
keyList.ForEach(s =>
{
var searchTypeStr = s.Substring(1, s.LastIndexOf("][", StringComparison.Ordinal) - 1); //查询方式 Like
var ab = s.Substring(s.LastIndexOf("][", StringComparison.Ordinal) + 2);
var joinTypeStr = ab.Remove(ab.LastIndexOf("]", StringComparison.Ordinal)); //连接方式 or
var searchField = s.Substring(s.LastIndexOf("]", StringComparison.Ordinal) + 1); //查询的列名 name
var value = enumerableQuery.FirstOrDefault(v => v.Key == s).Value.ToString(); //值 123 var searchType = LogicOperation.LIKE; //查询方式
var joinType = PredicateType.AND; //连接方式 if (Enum.TryParse(searchTypeStr.ToUpper(), out searchType) && Enum.TryParse(joinTypeStr.ToUpper(), out joinType) && modeltypt.GetProperties().Any(p => String.Equals(p.Name, searchField,
StringComparison.CurrentCultureIgnoreCase))) //这个实体有这个列名
{
var firstOrDefault = modeltypt.GetProperties().FirstOrDefault(p => String.Equals(p.Name, searchField, StringComparison.CurrentCultureIgnoreCase));
if (firstOrDefault != null)
{
var selCol = firstOrDefault.Name; //查询的列名
var splitList = value.Split(',').ToList();
for (var i = 0; i < splitList.Count; i++)
{
var expressionFuncEquals = PrepareConditionLambda<TEntity>(selCol, splitList[i], paramExp, searchType); //得到这个查询的表达式
if (i != 0) //累加
{
whereExp = whereExp == null ? expressionFuncEquals : Expression.Or(whereExp, expressionFuncEquals);
}
else
{
whereExp = joinType == PredicateType.OR ? (whereExp == null ? expressionFuncEquals : Expression.Or(whereExp, expressionFuncEquals)) : (whereExp == null ?
expressionFuncEquals : Expression.And(whereExp, expressionFuncEquals));
}
}
}
}
});
return Expression.Lambda<Func<TEntity, bool>>(whereExp, paramExp); ;
} /// <summary>
/// 得到字段查询的表达式
/// </summary>
/// <typeparam name="TEntity">实体</typeparam>
/// <param name="name">查询列名</param>
/// <param name="dateValue">数据值</param>
/// <param name="paramExp">参数</param>
/// <param name="searchType">查询方式(默认是等于查询)</param>
/// <returns></returns>
private static Expression PrepareConditionLambda<TEntity>(string name, object dateValue, ParameterExpression paramExp, LogicOperation searchType = LogicOperation.EQUAL)
{
if (dateValue == null) throw new ArgumentNullException("dateValue");
var exp = Expression.Property(paramExp, name);
var propertyType = typeof(TEntity).GetProperty(name).PropertyType; //得到此字段的数据类型
var value = propertyType == typeof(Guid?) ? new Guid(dateValue.ToString()) : Convert.ChangeType(dateValue, TypeHelper.GetUnNullableType(propertyType)); Expression expEqual = null;
switch (searchType)
{
case LogicOperation.EQUAL: //等于查询
var valueEqual = Expression.Constant(value, propertyType); //值
expEqual = Expression.Equal(exp, valueEqual); //拼接成 t=>t.name=valueEqual
break;
case LogicOperation.LIKE: //模糊查询
var containsMethod = typeof(string).GetMethod("Contains");
var valueLike = Expression.Constant(value, propertyType);
expEqual = Expression.Call(exp, containsMethod, valueLike);
break;
}
return expEqual;
}
#endregion
 /// <summary>
/// 查询方式
/// </summary>
public enum PredicateType
{
AND, OR
} /// <summary>
/// 查询方式
/// </summary>
public enum SearchType
{
Between, Like, Equals
}
/// <summary>
/// 查询方式
/// </summary>
public enum LogicOperation
{
LIKE, //包含,模糊查询
EQUAL, //等于
LT, //小于
GT, //大于
CONTAINS, //包含,In查询
NOTEQUAL //不等于
}

  在拼接查询时候有一个对查询数据值的分割

     var splitList = value.Split(',').ToList();

 查询方式目前先写完了这两个,其他的方式其实都是很简单的了,暂时没有用 ,也就没有写。 

这样做是因为可以实现多数据查询,例如在查询User的Name字段时,在Name文本框中输入【张三,李四】,这样就可以把"张三"和"李四"值都查询出来【关联是用的OR】,由于上面有注释就不再详细解释了,在这里一是把这个方法和大家分享一下,在一个就是对自己这两天工作的一个总结,同时也作为工作笔记放在这里,哪一天遗忘了自己还可以拿出来看看。

这里需要用到的有【json.js】和【Newtonsoft.Json】

补充:

  今天(2017-03-23)突然看到了自己以前写的这个小例子,想继续完善一下,顺便把代码提取出来。

  同时重载了GetSerchExtensions方式,直接解析一个对象,因为我感觉以前封装的那个只支持json字符串的查询不太友好,还多余,因为是对象转json字符串,json字符串转JArray,显得很繁琐,还很容易出错。

  /// <summary>
/// 查询实体
/// </summary>
public class QueryEntity
{
/// <summary>
/// 查询方式
/// </summary>
public LogicOperation LogicOperation { get; set; } /// <summary>
/// 连接方式
/// </summary>
public PredicateType PredicateType { get; set; } /// <summary>
/// 列名
/// </summary>
public string Column { get; set; } /// <summary>
/// 列值
/// </summary>
public object Value { get; set; }
}
        /// <summary>
/// 把查询条件拼接为Extensions
/// </summary>
/// <typeparam name="TEntity">实体类</typeparam>
/// <param name="queryEntitys">查询实体</param>
/// <returns></returns>
public static Expression<Func<TEntity, bool>> GetSerchExtensions<TEntity>(List<QueryEntity> queryEntitys) where TEntity : class, new()
{
var paramExp = Expression.Parameter(typeof(TEntity), "a");
if (null == queryEntitys || !queryEntitys.Any())
{
var valueEqual = Expression.Constant();
var expEqual = Expression.Equal(valueEqual, valueEqual);
return Expression.Lambda<Func<TEntity, bool>>(expEqual, paramExp); //如果参数为空,返回一个a=>1=1 的值 }
var modeltypt = typeof(TEntity); //实体类型
Expression whereExp = null; queryEntitys.ForEach(q =>
{
LogicOperation searchType = q.LogicOperation; //查询方式
PredicateType joinType = q.PredicateType; //连接方式
var searchField = q.Column; //查询的列名 name
var value = q.Value; //值 123
if (modeltypt.GetProperties().Any(p => String.Equals(p.Name, searchField, StringComparison.CurrentCultureIgnoreCase))) //这个实体有这个列名
{
var firstOrDefault = modeltypt.GetProperties().FirstOrDefault(p => String.Equals(p.Name, searchField, StringComparison.CurrentCultureIgnoreCase));
if (firstOrDefault == null) return;
var selCol = firstOrDefault.Name; //查询的列名
var splitList = value.ToString().Split(',').ToList(); //这个位置是的处理是默认认为当查询值中包含,的视为或者的查询:例如 A='abc,def' 处理成 (A='def' OR A='abc'),但是时间上这块无法满足就要查询包含,的数据的求
for (var i = ; i < splitList.Count; i++)
{
if (splitList[i] == null || string.IsNullOrWhiteSpace(splitList[i])) continue;
var expressionFuncEquals = PrepareConditionLambda<TEntity>(selCol, splitList[i], paramExp, searchType); //得到这个查询的表达式
whereExp = i !=
? (whereExp == null ? expressionFuncEquals : Expression.Or(whereExp, expressionFuncEquals))
: (joinType == PredicateType.OR ? (whereExp == null ? expressionFuncEquals : Expression.Or(whereExp, expressionFuncEquals))
: (whereExp == null ? expressionFuncEquals : Expression.And(whereExp, expressionFuncEquals)));
}
}
});
return Expression.Lambda<Func<TEntity, bool>>(whereExp, paramExp); ;
}

使用示例:

 static void Main(string[] args)
{
var query = SpecificationBuilder.Create<VWDepartment>();
query.Equals(d => d.DeptDelFlag, );
query.Equals(d => d.DeptParentID, Guid.NewGuid()); #region 字符串
string queryJson = "{\"[EQUAL][And]DeptParentID\":\"86EE21E7-81C2-49BC-B7D6-76E865DA1D3A\",\"[EQUAL][And]DeptName\":\"abc,ccccc\",\"[EQUAL][And]DeptSort\":789}"; if (!string.IsNullOrEmpty(queryJson))
{
var predicate = Utils.GetSerchExtensions<VWDepartment>(queryJson);
query.Predicate = query.Predicate.And(predicate);
}
#endregion #region 对象 QueryEntity queryEntity = new QueryEntity
{
LogicOperation = LogicOperation.EQUAL,
PredicateType = PredicateType.AND,
Column = "DeptParentID",
Value = Guid.NewGuid()
};
var qqqqq = Utils.GetSerchExtensions<VWDepartment>(new List<QueryEntity>() { queryEntity }); var li = new List<QueryEntity>() { };
li.Add(new QueryEntity
{
LogicOperation = LogicOperation.EQUAL,
PredicateType = PredicateType.AND,
Column = "DeptParentID",
Value = Guid.NewGuid()
}); li.Add(new QueryEntity
{
LogicOperation = LogicOperation.EQUAL,
PredicateType = PredicateType.AND,
Column = "DeptSort",
Value =
});
li.Add(new QueryEntity
{
LogicOperation = LogicOperation.LIKE,
PredicateType = PredicateType.AND,
Column = "ParentDeptName",
Value = "大爷"
});
qqqqq = Utils.GetSerchExtensions<VWDepartment>(li);
#endregion
}

代码环境

win10 + Visual Studio Community 2017

代码下载

自己写的一个关于Linq to Entity 动态查询的例子的更多相关文章

  1. (转)QueryBuilder : 打造优雅的Linq To SQL动态查询

    原文地址:http://www.cnblogs.com/coolcode/archive/2009/09/28/IQueryBuilder.html 首先我们来看看日常比较典型的一种查询Form 这个 ...

  2. 写了一个简单的NodeJS实现的进程间通信的例子

    1. cluster介绍 大家都知道nodejs是一个单进程单线程的服务器引擎,不管有多么的强大硬件,只能利用到单个CPU进行计算.所以,有人开发了第三方的cluster,让node可以利用多核CPU ...

  3. 无聊写了一个最简单的MVC4+Highcharts连数据库例子

    乱搞了个数据库 后面发现没定INT类型 直接将ID当数据显示了 效果图: 前端 @{ Layout = null; } <!DOCTYPE html> <html> <h ...

  4. Linq to Entity 动态拼接查询条件(重点是OR)

    public static class PredicateExtensions { /// <summary> /// 机关函数应用True时:单个AND有效,多个AND有效:单个OR无效 ...

  5. EF架构~在Linq to Entity中使用日期函數

    回到目录 眾所周知,在linq to entity的查询语句中,不允许出现ef不能识别的关键字,如Trim,Substring,TotalDays等.net里的关键字,在EF查询里都是不被支持的,它的 ...

  6. System.Linq.Dynamic 动态查询

    安装 VS->工具栏->NuGet程序管理器,System.Linq.Dynamic 注意: 使用动态查询必须先调用AsQueryable()方法,因为动态扩展仅适用于实现IQueryab ...

  7. C# Linq to Entity 多条件 OR查询

    技术背景:框架MVC,linq to Entity 需要一定的lambda书写能力 问题:在简单的orm中完成一些简单的增删查改是通过where insert delete update 完成的,但是 ...

  8. Linq to Entity 多条件 OR查询

    技术背景:框架MVC,linq to Entity 需要一定的lambda书写能力 问题:在简单的orm中完成一些简单的增删查改是通过where insert delete update 完成的,但是 ...

  9. Linq学习<三> linq to entity

    之前一直用sql选择出数据放在一个集合中,然后再用Linq或者lambda去操作数据,今天学了Linq to entity 才知道原来linq产生是为了Entity.也就是EDM(实体数据模型) 关于 ...

随机推荐

  1. 删除表空间时,遇到了ORA-14404错误

      Oracle中删除表空间时,遇到了ORA-14404错误.   错误信息如下: SQL> DROP TABLESPACE PART1 INCLUDING CONTENTS AND DATAF ...

  2. cd dirname $0

    这个命令的功能是返回脚本正在执行的目录. 可以根据这个目录来定位运行的程序的相对位置. 这样,对shell脚本里面的相对目录的路径代码就比较安全了.在任何一台服务器上面都可以安全执行.

  3. Spring自定义一个拦截器类SomeInterceptor,实现HandlerInterceptor接口及其方法的实例

    利用Spring的拦截器可以在处理器Controller方法执行前和后增加逻辑代码,了解拦截器中preHandle.postHandle和afterCompletion方法执行时机. 自定义一个拦截器 ...

  4. 笔者的编辑语法:MarkDown

    由于博客园里的文章有很多排版不好,一大堆文字堆在一块会影响到阅读. MarkDowm:百科 Markdown 是一种轻量级标记语言,创始人为约翰·格鲁伯(John Gruber).它允许人们“使用易读 ...

  5. FZU 1608 Huge Mission(线段树)

    Problem 1608 Huge Mission Time Limit: 1000 mSec    Memory Limit : 32768 KB Problem Description Oaiei ...

  6. Example to use django queryset

    from django.db.models import get_app, get_models, get_model from django.db import models #get the ce ...

  7. 23 其它话题 - 《Python 核心编程》

  8. 迅为三星Exynos 4412开发板四核Cortex-A9ARM安卓linux开发板

    开发板光盘资料包含:原理图(PDF格式).底板PCB(Allegro格式).驱动程序源码.芯片和LCD数据手册.开发环境.产品使用手册. 4412开发板简介: iTOP-Exynos4412开发板采用 ...

  9. java 方法参数-值调用,引用调用问题

    (博客内容来自于core java卷一) 1. xx调用:程序设计语言中方法参数的传递方式: 引用调用(call by reference):表示方法接收的是调用者提供的变量地址. 值调用(call ...

  10. 【Ext.Net学习笔记】07:后续

    这些笔记都是在这个地址看到的:http://www.qeefee.com/category/extnet 然后跟着敲,去理解的. Ext.NET其实就是基于跨浏览器的ExtJS库和.NET Frame ...