技术背景:框架MVC,linq to Entity 需要一定的lambda书写能力

问题:在简单的orm中完成一些简单的增删查改是通过where insert delete update 完成的,但是在这个过程中出现了一个需求:多项条件的and 和or 的组合查询

众所周知直接通过linq 写的话很方便,但是我们的框架使用了linq to entity,如果只用lambda来写比较复杂的and 和or 查询就比较麻烦了。

一:简单and 和or 查询

public void TextFoZK()
{
using (var dbContext = new CRMDbContext())
{
//第一句解析出来的sql是 select * from membertype where commercialtenantid=0 or name='住客'
dbContext.MemberType.Where(m => m.CommercialTenantID == || m.Name == "住客");
//第二句解析出来的sql是 select * from membertype where commercialtenantid=0 and name='住客'
dbContext.MemberType.Where(m => m.CommercialTenantID == && m.Name == "住客");
}
}

二:复杂逻辑的and 和or 查询

public void TextFoZK(int status, string name, int commercialtenantid)
{
using (var dbContext = new CRMDbContext())
{
IQueryable<MemberType> iqm = dbContext.MemberType;
if (status > )
{
iqm = iqm.Where(m => m.Status == status);
}
if (!string.IsNullOrEmpty(name))
{
iqm = iqm.Where(m => m.Name == name && m.CommercialTenantID == commercialtenantid);
}
iqm = iqm.Where(m => m.ID > || m.ID == );
iqm.ToList();
//select * from membertype where (status=1) and (name='住客' and commercialtenantid=1) and (id>0 or id=1)
}
}

这里使用了IQuerable的扩展方法where ,代表着每个iquerable之间为and 关系,但是又可以包含or

三:复杂and 和 or

public void TextFoZK(int status, string name, int commercialtenantid)
{
using (var dbContext = new CRMDbContext())
{
IQueryable<MemberType> iqm = dbContext.MemberType;
if (status > )
{
iqm = iqm.Where(m => m.Status == status);
}
if (!string.IsNullOrEmpty(name))
{
iqm = iqm.Where(m => m.Name == name && m.CommercialTenantID == commercialtenantid);
}
//重新声明一个iq,两个iq 之间为or 关系
IQueryable<MemberType> iqmtwo = dbContext.MemberType;
iqmtwo = iqmtwo.Where(m => m.ID > || m.ID == );
iqm = iqm.Union(iqmtwo);
iqm.ToList(); }
}

这里使用了iquerable中的扩展方法union 可以把多个iq方法合成为一个iq ,之间为union all  关系

第一个iq 为一个结果集,第二个为一个结果集,最后合并两个结果集。

可以满足一个sql过程中查询多处结果的要求,但是生成的sql还是有点麻烦

exec sp_executesql N'SELECT TOP (10)
[Project4].[C1] AS [C1],
[Project4].[C2] AS [C2],
[Project4].[C3] AS [C3],
[Project4].[C4] AS [C4],
[Project4].[C5] AS [C5],
[Project4].[C6] AS [C6],
[Project4].[C7] AS [C7],
[Project4].[C8] AS [C8],
[Project4].[C9] AS [C9],
[Project4].[C10] AS [C10]
FROM ( SELECT [Project4].[C1] AS [C1], [Project4].[C2] AS [C2], [Project4].[C3] AS [C3], [Project4].[C4] AS [C4], [Project4].[C5] AS [C5], [Project4].[C6] AS [C6], [Project4].[C7] AS [C7], [Project4].[C8] AS [C8], [Project4].[C9] AS [C9], [Project4].[C10] AS [C10], row_number() OVER (ORDER BY [Project4].[C1] ASC) AS [row_number]
FROM ( SELECT
[Distinct1].[C1] AS [C1],
[Distinct1].[C2] AS [C2],
[Distinct1].[C3] AS [C3],
[Distinct1].[C4] AS [C4],
[Distinct1].[C5] AS [C5],
[Distinct1].[C6] AS [C6],
[Distinct1].[C7] AS [C7],
[Distinct1].[C8] AS [C8],
[Distinct1].[C9] AS [C9],
[Distinct1].[C10] AS [C10]
FROM ( SELECT DISTINCT
[UnionAll1].[ID] AS [C1],
[UnionAll1].[CommercialTenantID] AS [C2],
[UnionAll1].[Name] AS [C3],
[UnionAll1].[Status] AS [C4],
[UnionAll1].[Discount] AS [C5],
[UnionAll1].[GiveIntegralScale] AS [C6],
[UnionAll1].[Creator] AS [C7],
[UnionAll1].[CreatorID] AS [C8],
[UnionAll1].[GMT_Create] AS [C9],
[UnionAll1].[GMT_Modified] AS [C10]
FROM (SELECT
[Extent1].[ID] AS [ID],
[Extent1].[CommercialTenantID] AS [CommercialTenantID],
[Extent1].[Name] AS [Name],
[Extent1].[Status] AS [Status],
[Extent1].[Discount] AS [Discount],
[Extent1].[GiveIntegralScale] AS [GiveIntegralScale],
[Extent1].[Creator] AS [Creator],
[Extent1].[CreatorID] AS [CreatorID],
[Extent1].[GMT_Create] AS [GMT_Create],
[Extent1].[GMT_Modified] AS [GMT_Modified]
FROM [dbo].[commercialtenant_membertype] AS [Extent1]
WHERE [Extent1].[CommercialTenantID] = @p__linq__0
UNION ALL
SELECT
[Extent2].[ID] AS [ID],
[Extent2].[CommercialTenantID] AS [CommercialTenantID],
[Extent2].[Name] AS [Name],
[Extent2].[Status] AS [Status],
[Extent2].[Discount] AS [Discount],
[Extent2].[GiveIntegralScale] AS [GiveIntegralScale],
[Extent2].[Creator] AS [Creator],
[Extent2].[CreatorID] AS [CreatorID],
[Extent2].[GMT_Create] AS [GMT_Create],
[Extent2].[GMT_Modified] AS [GMT_Modified]
FROM [dbo].[commercialtenant_membertype] AS [Extent2]
WHERE (0 = [Extent2].[CommercialTenantID]) AND (N''住客'' = [Extent2].[Name])) AS [UnionAll1]
) AS [Distinct1]
) AS [Project4]
) AS [Project4]
WHERE [Project4].[row_number] > 0
ORDER BY [Project4].[C1] ASC',N'@p__linq__0 int',@p__linq__0=1

最后提供一种扩展方法

四:多条件之间均为or

/// <summary>
/// 传入条件之间为OR查询
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <param name="predicates"></param>
/// <returns></returns>
public static IQueryable<T> WhereOR<T>(this IQueryable<T> source, params Expression<Func<T, bool>>[] predicates)
{
if (source == null) throw new ArgumentNullException("source");
if (predicates == null) throw new ArgumentNullException("predicates");
if (predicates.Length == ) return source.Where(x => false); // no matches!
if (predicates.Length == ) return source.Where(predicates[]); // simple var param = Expression.Parameter(typeof(T), "x");
Expression body = Expression.Invoke(predicates[], param);
for (int i = ; i < predicates.Length; i++)
{
body = Expression.OrElse(body, Expression.Invoke(predicates[i], param));
}
var lambda = Expression.Lambda<Func<T, bool>>(body, param);
return source.Where(lambda);
}
public void TextFoZK(int status, string name, int commercialtenantid)
{
using (var dbContext = new CRMDbContext())
{
IQueryable<MemberType> iqm = dbContext.MemberType;
if (status > )
{
iqm = iqm.Where(m => m.Status == status);
}
if (!string.IsNullOrEmpty(name))
{
iqm = iqm.Where(m => m.Name == name && m.CommercialTenantID == commercialtenantid);
}
var predicates = new List<Expression<Func<MemberType, bool>>>();
predicates.Add(m => m.CommercialTenantID == && m.Name == "住客");
predicates.Add(m=>m.ID>);
//这两个条件之间为or
//与iqm之间为and
//如果要与iqm之间为or 也可以使用union方法,但是总感觉有点麻烦
iqm = iqm.WhereOR(predicates.ToArray());
iqm.ToList();
//select * from membertype where (status=1) and (name='住客' and commercialtenantid=1) or (id>0 or id=1)
}
}

我感觉已经研究到这一步了索性就再往深的看一看,于是我找到了IQuerable的where 和union 的底层方法

public static IQueryable<TSource> Union<TSource>(this IQueryable<TSource> source1, IEnumerable<TSource> source2)
{
if (source1 == null)
{
throw System.Linq.Error.ArgumentNull("source1");
}
if (source2 == null)
{
throw System.Linq.Error.ArgumentNull("source2");
}
return source1.Provider.CreateQuery<TSource>(Expression.Call(null, ((MethodInfo) MethodBase.GetCurrentMethod()).MakeGenericMethod(new Type[] { typeof(TSource) }), new Expression[] { source1.Expression, GetSourceExpression<TSource>(source2) }));
} [__DynamicallyInvokable]
public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
{
if (source == null)
{
throw System.Linq.Error.ArgumentNull("source");
}
if (predicate == null)
{
throw System.Linq.Error.ArgumentNull("predicate");
}
return source.Provider.CreateQuery<TSource>(Expression.Call(null, ((MethodInfo) MethodBase.GetCurrentMethod()).MakeGenericMethod(new Type[] { typeof(TSource) }), new Expression[] { source.Expression, Expression.Quote(predicate) }));
}

只是浅薄只能看到这一步。

最后我还求助大神,大神又提出一个方案使用的是expresion方法,这个其实就是我上面提供的whereor 方法内的同样技术,只不过我是封装了的。

Expression<Func<MemberType, bool>> funtyps = c => c.ID > ;
Expression<Func<MemberType, bool>> ortype = c => c.CommercialTenantID == && c.Name == "住客";
funtyps = funtyps.Or(ortype);
iqmemebertype = iqmemebertype.Where(funtyps);

应该是是要更好的方案,我只是记录我目前理解的方法。

最后附上关于expression的底层方法or 和 and ,提供了express语句之间可或与查询的接口

/// <summary>
/// 用于多条件动态查询
/// zk(-_-)
/// </summary>
public static class PredicateBuilderUtility
{
public static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge)
{
// build parameter map (from parameters of second to parameters of first)
var map = first.Parameters.Select((f, i) => new { f, s = second.Parameters[i] }).ToDictionary(p => p.s, p => p.f);
// replace parameters in the second lambda expression with parameters from the first
var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body);
// apply composition of lambda expression bodies to parameters from the first expression
return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);
}
/// <summary>
/// 动态And
/// </summary>
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
{
return first.Compose(second, Expression.AndAlso);
}
/// <summary>
/// 动态Or
/// </summary>
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
{
return first.Compose(second, Expression.Or);
}
/// <summary>
/// 传入条件之间为OR查询
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <param name="predicates"></param>
/// <returns></returns>
public static IQueryable<T> WhereOR<T>(this IQueryable<T> source, params Expression<Func<T, bool>>[] predicates)
{
if (source == null) throw new ArgumentNullException("source");
if (predicates == null) throw new ArgumentNullException("predicates");
if (predicates.Length == ) return source.Where(x => false); // no matches!
if (predicates.Length == ) return source.Where(predicates[]); // simple var param = Expression.Parameter(typeof(T), "x");
Expression body = Expression.Invoke(predicates[], param);
for (int i = ; i < predicates.Length; i++)
{
body = Expression.OrElse(body, Expression.Invoke(predicates[i], param));
}
var lambda = Expression.Lambda<Func<T, bool>>(body, param);
return source.Where(lambda);
} }

C# Linq to Entity 多条件 OR查询的更多相关文章

  1. Linq to Entity 多条件 OR查询

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

  2. Linq to Entity 时间差作为筛选条件产生的问题

    前言 在使用 Linq to Entity 的時候,會把之前 Linq to SQL 的想法就帶進去,寫好之後編譯也都不會出錯,但是實際上在跑的時候就會出現錯誤訊息了,這點真的要注意了.這次我遇到問題 ...

  3. Linq to Entity经验:表达式转换

    http://www.cnblogs.com/ASPNET2008/archive/2012/10/27/2742434.html 最近一年的项目,我主要负责一些小型项目(就是指企业内部的小项目),在 ...

  4. LINQ之路 8: 解释查询(Interpreted Queries)

    LINQ提供了两个平行的架构:针对本地对象集合的本地查询(local queries),以及针对远程数据源的解释查询(Interpreted queries). 在讨论LINQ to SQL等具体技术 ...

  5. LINQ之路 7:子查询、创建策略和数据转换

    在前面的系列中,我们已经讨论了LINQ简单查询的大部分特性,了解了LINQ的支持计术和语法形式.至此,我们应该可以创建出大部分相对简单的LINQ查询.在本篇中,除了对前面的知识做个简单的总结,还会介绍 ...

  6. asp.net mvc多条件+分页查询解决方案

    开发环境vs2010 css:bootstrap js:jquery bootstrap paginator 原先只是想做个mvc的分页,但是一般的数据展现都需要检索条件,而且是多个条件,所以就变成了 ...

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

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

  8. 关于Entity Framework自动关联查询与自动关联更新导航属性对应的实体注意事项说明

    一.首先了解下Entity Framework 自动关联查询: Entity Framework 自动关联查询,有三种方法:Lazy Loading(延迟加载),Eager Loading(预先加载) ...

  9. Linq to Entity中连接两个数据库时要注意的问题

    Linq to Entity中连接两个数据库时要注意的问题 今天大学同学问了我一个问题,Linq to Entity中连接两个数据库时,报错“指定的 LINQ 表达式包含对与不同上下文关联的查询的引用 ...

随机推荐

  1. Java:配置环境(Mac)——Eclipse;修改JDK版本后,Eclipse打不开

    1.官网下载 选择一个速度快的镜像 推荐东软这个 2.双击下载的安装包,下一步 其中有一步是选择Eclipse版本,SE选第一个,EE第二个.仔细审题吧. 3.配置JDK 应用.关闭 4.测试:写个H ...

  2. TTL与非门电路分析

    TTL与非门(TTL推挽式与非门)是TTL集成逻辑门的一种,主要由三极管和二极管构成.如图(a)所示,它由输入级,中间级,输出级三部分组成.TTL与非门的优点在于输出阻抗低,带负载能力强,工作速度快. ...

  3. 基于GitLab的Code Review教程

    一.前言 1.本文主要内容 GitLab Code Review机制说明 Git Workflow 与 Git Code Review Workflow GitLab Code Review 配置说明 ...

  4. HTML导出excel

    在博客园找到的相关问题http://q.cnblogs.com/q/12952  还有相关的回答http://www.cnblogs.com/zhouxin/archive/2009/12/11/16 ...

  5. windows笔记本触摸板的快捷键教程

    自从习惯了macbook的触摸板,根本就懒得使用鼠标.即实用,又便捷.但切换到windows笔记本的时候,总是不习惯使用触摸板. 今天查了一下微软的教程,发现windows现的已经做的相当不错了.但是 ...

  6. UI自动化之日志

    Python自动化测试中,日志输出功能是不能缺少的一部分.让我们来看看如何实现日志的输出吧 一.控制台输出日志 def get_logger(): try: if not os.path.exists ...

  7. [题解] P2513 [HAOI2009]逆序对数列

    动态规划,卡常数 题目地址 设\(F[X][Y]\)代表长度为\(X\)的序列,存在\(Y\)组逆序对的方案数量. 考虑\(F[X][i]\)向\(F[X+1][i]\)转移: 把数字\(X+1\)添 ...

  8. 三十六、fetch

    https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API

  9. MySQL大小写敏感

    MySQL大小写敏感说明 - TonyWu - 博客园https://www.cnblogs.com/wzmenjoy/p/4244545.html

  10. AirPods 2 & Android

    AirPods 2 & Android AirBattery https://play.google.com/store/apps/details?id=friedrich.georg.air ...