Linq to Entity 多条件 OR查询

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

问题:在简单的orm中完成一些简单的增删查改是通过where insert delete update 完成的,但是在这个过程中出现了一个需求:多项条件的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 == 0 || m.Name == "住客");
                //第二句解析出来的sql是  select * from membertype where commercialtenantid=0 and name=‘住客‘
                dbContext.MemberType.Where(m => m.CommercialTenantID == 0 && 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 > 0)
                {
                    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 > 0 || m.ID == 1);
                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 > 0)
                {
                    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 > 0 || m.ID == 1);
                iqm = iqm.Union(iqmtwo);
                iqm.ToList();

            }
        }

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

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

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

 1 exec sp_executesql N‘SELECT TOP (10)
 2 [Project4].[C1] AS [C1],
 3 [Project4].[C2] AS [C2],
 4 [Project4].[C3] AS [C3],
 5 [Project4].[C4] AS [C4],
 6 [Project4].[C5] AS [C5],
 7 [Project4].[C6] AS [C6],
 8 [Project4].[C7] AS [C7],
 9 [Project4].[C8] AS [C8],
10 [Project4].[C9] AS [C9],
11 [Project4].[C10] AS [C10]
12 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]
13     FROM ( SELECT
14         [Distinct1].[C1] AS [C1],
15         [Distinct1].[C2] AS [C2],
16         [Distinct1].[C3] AS [C3],
17         [Distinct1].[C4] AS [C4],
18         [Distinct1].[C5] AS [C5],
19         [Distinct1].[C6] AS [C6],
20         [Distinct1].[C7] AS [C7],
21         [Distinct1].[C8] AS [C8],
22         [Distinct1].[C9] AS [C9],
23         [Distinct1].[C10] AS [C10]
24         FROM ( SELECT DISTINCT
25             [UnionAll1].[ID] AS [C1],
26             [UnionAll1].[CommercialTenantID] AS [C2],
27             [UnionAll1].[Name] AS [C3],
28             [UnionAll1].[Status] AS [C4],
29             [UnionAll1].[Discount] AS [C5],
30             [UnionAll1].[GiveIntegralScale] AS [C6],
31             [UnionAll1].[Creator] AS [C7],
32             [UnionAll1].[CreatorID] AS [C8],
33             [UnionAll1].[GMT_Create] AS [C9],
34             [UnionAll1].[GMT_Modified] AS [C10]
35             FROM  (SELECT
36                 [Extent1].[ID] AS [ID],
37                 [Extent1].[CommercialTenantID] AS [CommercialTenantID],
38                 [Extent1].[Name] AS [Name],
39                 [Extent1].[Status] AS [Status],
40                 [Extent1].[Discount] AS [Discount],
41                 [Extent1].[GiveIntegralScale] AS [GiveIntegralScale],
42                 [Extent1].[Creator] AS [Creator],
43                 [Extent1].[CreatorID] AS [CreatorID],
44                 [Extent1].[GMT_Create] AS [GMT_Create],
45                 [Extent1].[GMT_Modified] AS [GMT_Modified]
46                 FROM [dbo].[commercialtenant_membertype] AS [Extent1]
47                 WHERE [Extent1].[CommercialTenantID] = @p__linq__0
48             UNION ALL
49                 SELECT
50                 [Extent2].[ID] AS [ID],
51                 [Extent2].[CommercialTenantID] AS [CommercialTenantID],
52                 [Extent2].[Name] AS [Name],
53                 [Extent2].[Status] AS [Status],
54                 [Extent2].[Discount] AS [Discount],
55                 [Extent2].[GiveIntegralScale] AS [GiveIntegralScale],
56                 [Extent2].[Creator] AS [Creator],
57                 [Extent2].[CreatorID] AS [CreatorID],
58                 [Extent2].[GMT_Create] AS [GMT_Create],
59                 [Extent2].[GMT_Modified] AS [GMT_Modified]
60                 FROM [dbo].[commercialtenant_membertype] AS [Extent2]
61                 WHERE (0 = [Extent2].[CommercialTenantID]) AND (N‘‘住客‘‘ = [Extent2].[Name])) AS [UnionAll1]
62         )  AS [Distinct1]
63     )  AS [Project4]
64 )  AS [Project4]
65 WHERE [Project4].[row_number] > 0
66 ORDER BY [Project4].[C1] ASC‘,N‘@p__linq__0 int‘,@p__linq__0=1

sql 想看的可以打开看看

最后提供一种扩展方法

四:多条件之间均为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 == 0) return source.Where(x => false); // no matches!
            if (predicates.Length == 1) return source.Where(predicates[0]); // simple

            var param = Expression.Parameter(typeof(T), "x");
            Expression body = Expression.Invoke(predicates[0], param);
            for (int i = 1; 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 > 0)
                {
                    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 == 0 && m.Name == "住客");
                predicates.Add(m=>m.ID>0);
                //这两个条件之间为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 > 0;
Expression<Func<MemberType, bool>> ortype = c => c.CommercialTenantID == 0 && 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 == 0) return source.Where(x => false); // no matches!
            if (predicates.Length == 1) return source.Where(predicates[0]); // simple

            var param = Expression.Parameter(typeof(T), "x");
            Expression body = Expression.Invoke(predicates[0], param);
            for (int i = 1; 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);
        }

    }

expression

时间: 2024-08-03 23:33:58

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

Linq To Entity 多表联合查询

using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace LinqToEntity { class Program { static void Main(string[] args) { //点击项目右键-添加->新建项->ADO.NET实体数据模型,然后..选择你需要的表,点击完成.这里我选择是数据库是sales表的名字是xs,xs_kc默认生成了一个实体容器

Entity Framework多表多条件动态查询

方式一  Linq To Entity形式: /// <summary> /// 查询的数据 /// </summary> /// <param name="order">升序asc(默认)还是降序desc</param> /// <param name="sort">排序字段</param> /// <param name="search">查询条件</p

zTree初体验--MVC linq动态多条件OR查询

工作需要,使用zTree实现了一个点击显示下拉复选框列表选中项作为查询条件的功能.简单记录下菜鸟级开发历程. zTree:是一个依靠jQuery实现的多功能树控件.通过简单引用配置就可使用. 具体使用: (1).脚本.样式引用 注意:引用顺序,zTree.js一定放最后. <link rel="stylesheet" href="@Url.Content("~/Scripts/jquery.plugins/zTree/zTreeStyle.css")

Linq to Sql 多条件查询

Linq To Sql 多条件查询 string proName = this.txtName.Text.Trim();string lowPrice = this.txtLowPrice.Text.Trim();string highPrice = this.txtHighPrice.Text.Trim(); decimal? lowPrice1 = null, highPrice1 = null;if (!string.IsNullOrEmpty(lowPrice)){        low

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

在前面的系列中,我们已经讨论了LINQ简单查询的大部分特性,了解了LINQ的支持计术和语法形式.至此,我们应该可以创建出大部分相对简单的LINQ查询.在本篇中,除了对前面的知识做个简单的总结,还会介绍几种创建更复杂查询的方式,让我们在面对更复杂的场景时也能轻松面对,包括:子查询.创建策略和数据转换. 子查询 在创建一个复杂的查询时,通常我们需要用到子查询.相信大家都记得SQL查询里的子查询,在创建LINQ查询时也是如此.在LINQ中,对于方法语法,一个子查询包含在另外一个查询的lambda表达式

初识Linq to Entity

背景:C# .Net MVC ,以及已经开发了近1年半的我,做了好久但是对于这个技术没有总结,这是一个机会,写写我理解的东西吧. 技术介绍: LINQ(Language Integrated Query,语言集成查询)是一组用于C#和VB.NET语言的扩展,它允许编写C#或者VB.net代码,以与查询数据库相同的方式操作内存数据.LINQ提供了丰富的类似SQL的查询语法,功能强大且容易上手.下图汇总展示了LINQ技术的官方实现集合: 正如上图所示,LINQ to Entities 是LINQ技术

在LINQ中实现多条件联合主键LEFT JOIN

我昨天遇到一个LINQ下使用多条件比对产生LEFT JOIN的问题,经过深入研究,终于解决了,也让我学到了新的东西,特地拿来分享. 实例:有一张库存异常变更视图KCYD,仓库ID[Ckid]和物品ID[SpxxId]是该视图的唯一约束.有一张物品表ITEM,物品ID[ITEM_ID]是主键.还有一张表是统计正品和次品库存数量的视图SPKC,仓库ID[CKID]和物品ID[SPXXID]是该视图的唯一约束.现在的要求是根据条件查询库存异常变更的物品信息,即要求KCYD左联ITEM再左联SPKC.K

linq中的contains条件

linq中的contains条件 在sql查询语句中,in 在linq 中用contains,并且contains前面是数组,而后面是列名,如: SELECT distinct BH FROM cd  where (LB = '身份') AND (LM IN ('合同工','临时工')) 转换成Linq 是这样的: 如果sf是变量,是在程序中根据用户选择的条件临时添加的,sf可以为空,也可不为空, 在程序中定义sf,在程序中赋值为:sf="合同工","临时工" va

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

一.首先了解下Entity Framework 自动关联查询: Entity Framework 自动关联查询,有三种方法:Lazy Loading(延迟加载),Eager Loading(预先加载),Explicit Loading(显式加载),其中Lazy Loading和Explicit Loading都是延迟加载. (注:由于Entity Framework版本的不同,以及采用不同的模式(DB First,Model First,Code First)来构建的Entity,最终导致可能自