自己写的一个关于Linq to Entity 动态查询的例子
这两天一直想写一个动态查询的方式,先是网上查询了一下,发现大家写的差不多都是一样的【如:http://www.cnblogs.com/ASPNET2008/archive/2012/10/28/2743053.html#commentform和http://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 动态查询的例子的更多相关文章
- (转)QueryBuilder : 打造优雅的Linq To SQL动态查询
原文地址:http://www.cnblogs.com/coolcode/archive/2009/09/28/IQueryBuilder.html 首先我们来看看日常比较典型的一种查询Form 这个 ...
- 写了一个简单的NodeJS实现的进程间通信的例子
1. cluster介绍 大家都知道nodejs是一个单进程单线程的服务器引擎,不管有多么的强大硬件,只能利用到单个CPU进行计算.所以,有人开发了第三方的cluster,让node可以利用多核CPU ...
- 无聊写了一个最简单的MVC4+Highcharts连数据库例子
乱搞了个数据库 后面发现没定INT类型 直接将ID当数据显示了 效果图: 前端 @{ Layout = null; } <!DOCTYPE html> <html> <h ...
- Linq to Entity 动态拼接查询条件(重点是OR)
public static class PredicateExtensions { /// <summary> /// 机关函数应用True时:单个AND有效,多个AND有效:单个OR无效 ...
- EF架构~在Linq to Entity中使用日期函數
回到目录 眾所周知,在linq to entity的查询语句中,不允许出现ef不能识别的关键字,如Trim,Substring,TotalDays等.net里的关键字,在EF查询里都是不被支持的,它的 ...
- System.Linq.Dynamic 动态查询
安装 VS->工具栏->NuGet程序管理器,System.Linq.Dynamic 注意: 使用动态查询必须先调用AsQueryable()方法,因为动态扩展仅适用于实现IQueryab ...
- C# Linq to Entity 多条件 OR查询
技术背景:框架MVC,linq to Entity 需要一定的lambda书写能力 问题:在简单的orm中完成一些简单的增删查改是通过where insert delete update 完成的,但是 ...
- Linq to Entity 多条件 OR查询
技术背景:框架MVC,linq to Entity 需要一定的lambda书写能力 问题:在简单的orm中完成一些简单的增删查改是通过where insert delete update 完成的,但是 ...
- Linq学习<三> linq to entity
之前一直用sql选择出数据放在一个集合中,然后再用Linq或者lambda去操作数据,今天学了Linq to entity 才知道原来linq产生是为了Entity.也就是EDM(实体数据模型) 关于 ...
随机推荐
- Effective Java 39 Make defensive copies when needed
Principle It is essential to make a defensive copy of each mutable parameter to the constructor. Def ...
- Hadoop从伪分布式到真正的分布式
对这两天学习hadoop的一个总结,概念就不提了.直接说部署的事,关于如何部署hadoop网上的资料很多, 比较经典的还是Tim在IBM developworks上的系列文章 http://www.i ...
- MapReduce形象总结
We want to count all to the books in the library.You count up shelf #1,I count up shelf #2. That's a ...
- uboot 2014.04 运行过程记录
uboot启动流程分析,针对S5PV210 BL1阶段,SPL,u-boot-spl.bin 1.首先运行arch/arm/cpu/armv7/start.S 里面的_start函数,进行异常向量表设 ...
- linux下开启SSH,并且允许root用户远程登录,允许无密码登录
参考:http://blog.csdn.net/jia0511/article/details/8237698 1. 允许root用户远程登录 修改ssh服务配置文件 sudo vi /etc/ssh ...
- Linux下Mysql安装
1.下载安装包 首先查看Linux版本: [root@localhost ~]# lsb_release -a LSB Version: :core-4.0-amd64:core-4.0-noarch ...
- Linux 系统常用命令汇总(六) 文件打包与压缩
文件打包与压缩 命令 选项 注解 示例 compress 文件名 压缩指定的文件,压缩后的格式为*.z compress install.log -d 解压被压缩的文件 .z为后缀的文件:compr ...
- [ZZ]Android UI Automated Testing
Google Testing Blog最近发表了一篇Android UI Automated Testing,我把他转载过来,墙外地址:http://googletesting.blogspot.co ...
- POJ 2823 Sliding Window 再探单调队列
重新刷这个经典题,感觉跟以前不一样了,变得更加容易理解了,不讲解了,看代码.注意:要用C++提交,用G++会超时.. 代码: #include <iostream> #include &l ...
- java10-2 toString()方法
public String toString():返回该对象的字符串表示. Integer类下的一个静态方法: public static String toHexString(int i):把一个整 ...