这两天一直想写一个动态查询的方式,先是网上查询了一下,发现大家写的差不多都是一样的【如: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. jquery.qrcode.js 插件生成二维码

    下载地址:https://github.com/jeromeetienne/jquery-qrcode 例子: <!doctype html> <html> <head& ...

  2. 关于Redis中的Replication

    一.简介 Redis的replication机制允许slave从master那里通过网络传输拷贝到完整的数据备份.具有以下特点: 异步复制 可以配置一主多从 可以配置从服务器可以级联从服务器,既 M- ...

  3. Python Tomcat Script(多实例)

    之前书写过 Tomcat 单实例的 Python 脚本,本次增加 Tomcat 多实例的操作脚本. 1:准备 安装所需 Python 插件 A方法: pip install argparse B方法: ...

  4. oracle 高水位线详解

    一.什么是水线(High Water Mark)? 所有的oracle段(segments,在此,为了理解方便,建议把segment作为表的一个同义词) 都有一个在段内容纳数据的上限,我们把这个上限称 ...

  5. git的使用(二)

    1.几个概念 (1)工作区指当前编辑代码的地方,是.git仓库所在的文件夹. (2)暂存区是一个概念,并不存在这个区. (3)仓库是.git文件夹,是运行git init命令时自动创建的,默认是隐藏的 ...

  6. uva 120 stacks of flapjacks ——yhx

     Stacks of Flapjacks  Background Stacks and Queues are often considered the bread and butter of data ...

  7. bootstrap学习总结-js组件(四)

    这次我们来看下js组件的使用,本篇文章会有点长,希望大家可以耐心看,相信收获会有不少.不少园友加我好友,表示喜欢我写文字的风格,简单明了,这里,再次谢谢你们的支持.一方面,博主自身技术有限,写的东西都 ...

  8. MySchool

    USE [MySchool] GO /****** Object: Table [dbo].[Grade] Script Date: 08/06/2014 15:03:17 ******/ SET A ...

  9. java22 - 1 多线程之 单线程和多线程的图解

  10. java 12-1 StringBuffer类

    线程安全(多线程讲解) 安全 -- 同步 -- 数据是安全的--效率低一些 不安全 -- 不同步 -- 数据不安全--效率高一些 安全和效率问题是永远困扰我们的问题. 安全:医院的网站,银行网站 效率 ...