• LINQ基本子句


    出于工作需要,准备把LINQ的相关知识梳理一遍,希望能填补下之前学习漏掉的或是没有注意的地方,也为未来减轻压力~

      LINQ查询表达式的基本语法很容易掌握,它使用C#常见的语言构造,从外观上看,和我们常用的SQL类似,并且查询表达式中的变量可以用匿名类型,所以在很多情况下,不需要指定变量类型就可以构建LINQ表达式。

      LINQ的数据源可以是数据库对象或是XML流等,也可以使实现了IEnumerable或者泛型IEnumberable<T>接口的集合对象。

      LINQ的基本语法包含如下的8个上下文关键字,这些关键字和具体的说明如下:

    关键字 说明
    from 指定范围变量和数据源
    where   根据bool表达式从数据源中筛选数据
    select 指定查询结果中的元素所具有的类型或表现形式
    group 对查询结果按照键值进行分组(IGrouping<TKey,TElement>)
    into 提供一个标识符,它可以充当对join、group或select子句结果的引用
    orderby 对查询出的元素进行排序(ascending/descending)
    join 按照两个指定匹配条件来Equals连接两个数据源
    let 产生一个用于存储查询表达式中的子表达式查询结果的范围变量

    下面依此总结这8个关键字的常用查询语句写法。

    1.from子句

    如果要写一个LINQ表达式,就必须是以from子句开头。个人觉得from子句中需要注意的地方就是多个from子句的书写。

    复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Collections;
    
    namespace LINQ
    {
        /// <summary>
        /// LINQ,重点是感悟from子句中的查询变量的灵活
        /// </summary>
        class Program
        {
            static void Main(string[] args)
            {
                //1单个form子句
                string[] values = { "LINQ学习","LINQ基本语句","from子句","单个from子句"};
                var value = from v in values
                            where v.IndexOf("LINQ") > -1
                            select new { v, v.Length };
                foreach (var n in value)
                {
                    Console.WriteLine("{0},{1}",n.v,n.Length );
                }
                Console.ReadKey(false);
                //2使用LINQ查询ArrayList
                ArrayList gList = new ArrayList();
                gList.Add(new GustInfo {  Name="DebugLZQ", Age=26, Tel="88888888"});
                gList.Add(new GustInfo { Name="博客园",Age=6, Tel ="666666"});
                gList.Add(new GustInfo { Name = "M&MSoft", Age =9, Tel = "55555" });
    
                var query = from GustInfo gust in gList
                            where gust.Age > 9
                            select gust;//范围变量gust制定了数据类型
                foreach (GustInfo g in query)
                {
                    Console.WriteLine("{0} 年龄:{1} 电话:{2}",g.Name,g.Age,g.Tel );
                }
                Console.ReadKey(false);
                //3复合from子句
                List<GustInfo2> gList2 = new List<GustInfo2>() 
                {
                    new GustInfo2{ Name="DebugLZQ",Age=26,TelTable=new List<string>(){"8888888","138******"}},
                    new GustInfo2{ Name="博客园",Age=6,TelTable =new List<string>(){"666666","138******"}},
                    new GustInfo2{ Name="M&MSoft",Age=9,TelTable=new List<string>(){"55555","138******"}}
                };
    
                //gust、tel都是查询变量,作用域为当前查询语句!!!
                var query2 = from gust in gList2
                             from tel in gust.TelTable 
                             where tel.IndexOf("5555") > -1
                             select gust;
                foreach (var g in query2)
                {
                    Console.WriteLine("{0} 年龄{1}",g.Name,g.Age );
                    foreach (var t in g.TelTable)
                    {
                        Console.WriteLine("电话:{0}",t);
                    }
                }
    
                Console.ReadKey(false);
    
                //4多个from子句
                var query3 = from GustInfo gust in gList
                             where gust.Age > 6
                             from GustInfo2 gust2 in gList2
                             where gust2.Age> 9
                             select new { gust, gust2 };//查询结果定制
                foreach (var g in query3)
                {
                    Console.WriteLine("{0}  {1}", g.gust.Name, g.gust2.Name);
                }
    
                Console.ReadKey(false);
            }
        }
    }
    复制代码

    程序的运行结果如下:

     程序中列举了from子句的用法示例,注意复合from子句和多个from子句的书写,同时需要理解范围变量和数据源变量这两个概念。

     2.where子句

    where子句,它是LINQ表达式的元素筛选机制,除了开始和结束的位置,它几乎可以出现在LINQ表达式的任意位置上。

    在一个LINQ表达式中,可以有where子句,也可以没有;可以有一个,也可以有多个;多个where子句之间的逻辑关系相当于逻辑“与”,每个where子句可以包含1个或多个bool逻辑表达式,这些条件成为谓词,谓词逻辑之间用的是“&&”“||”等而不是SQL中的and 、or。

    复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace LINQ_WHERE
    {
        /// <summary>
        /// LINQ where子句
        /// </summary>
        class Program
        {
            static void Main(string[] args)
            {
                //1常见的where语句
                List<GustInfo> gList = new List<GustInfo>() 
                {
                    new GustInfo(){ Name="DebugLZQ", Age=26,Tel="88888888"},
                    new GustInfo(){ Name="cnblogs",Age=6,Tel="666666"},
                    new GustInfo(){ Name="M&MSoft",Age=9,Tel="55555"}
                };
    
                var query = from gust in gList
                            where (gust.Name.Length > 7 || gust.Name.Substring(0, 1) == "M") && gust.Age > 9
                            select new { gust.Name, gust.Age };
                foreach (var g in query)
                {
                    Console.WriteLine("{0},{1}", g.Name, g.Age);
                }
    
                Console.ReadKey(false);
    
                //2.在where子句中使用自定义函数
                var query2 = from GustInfo gust in gList
                             where gust.Name.Length > 5
                             && Check(gust.Name)
                             select gust;
                foreach (var g in query2)
                {
                    Console.WriteLine("{0},{1},{2}", g.Name, g.Age, g.Tel);
                }
                Console.ReadKey(false);
    
                //3.动态谓词的筛选
                //定义动态谓词数组,在实际开发中可以动态获得
                string[] names = { "SB","XXX","***","@@@","一些敏感词"};
    
                var query3 = from GustInfo guest in gList
                             where !names.Contains(guest.Name)
                             select guest;
    
                foreach (var q in query3)
                {
                    Console.WriteLine("{0} 年龄:{1},电话:{2}",q.Name,q.Age,q.Tel );
                }
                Console.ReadKey(false);
            }
    
            //自定义函数
            static bool Check(string name)
            {
                if (name.Substring(0, 1) == "N")
                    return false;
                return true;
            }
        }
    }
    复制代码

    需要注意一些常用的where子句的写法。程序的运行结果如下:

     3.Select子句

    在select子句上可以非常灵活的处理查询到的元素,然后再把结果返回。

    复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace LINQ_select
    {
        /// <summary>
        /// LINQ select
        /// 在select子句上,可以非常灵活的处理查询到的元素,然后再把结果返回
        /// </summary>
        class MyGustInfo
        {
            public string Name { get; set; }
            public int Age { get; set; }
        }
        class Program
        {
            static void Main(string[] args)
            {
                List<GustInfo> gList = new List<GustInfo>() 
                {
                    new GustInfo(){ Name="DebugLZQ", Age=25, Tel="88888888"},
                     new GustInfo(){ Name="cnblogs", Age=6, Tel="666666"},
                      new GustInfo(){ Name="M&M", Age=9, Tel="55555"}
                };
                var query = from gust in gList
                            where gust.Age >= 9 && gust.Age <= 30
                            select gust.Name.Replace("&", "mm");//select子句灵活应用
                var query2 = from gust in gList
                             where gust.Age >= 9 && gust.Age <= 30
                             select MyProc(gust.Name);
                var query3 = from gust in gList
                             where gust.Age >= 9 && gust.Age <= 30
                             select new { gust.Name,gust.Age};
    
                var query4 = from gust in gList
                             where gust.Age >= 9 && gust.Age <= 30
                             select new MyGustInfo {  Name=gust.Name+"My", Age=gust.Age+1};//对查询结果进行投影
    
                foreach (var v in query)
                {
                    Console.WriteLine(v);
                }
                foreach (var v in query2)
                {
                    Console.WriteLine(v);
                }
                foreach (var v in query3)
                {
                    Console.WriteLine(v.Name+v.Age );
                }
                foreach (var v in query4)
                {
                    Console.WriteLine(v.Name+v.Age );
                }
    
                Console.ReadKey(false);
            }
            static string MyProc(string s)
            {
                return s + "Better";
            }
        }
    }
    复制代码

    程序的运行结果如下:

     4.group子句

     根据语法的规定,LINQ表达式必须以from子句开头,以select或group子句结束,所以除了使用select来返回结果外,也可以使用group子句来返回元素分组后的结果。

    group子句返回的是一个基于IGrouping<TKey,TElement>泛型接口的对象序列。

    语法和SQL的group有点区别,不注意的话可能会写错。

    复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace LINQ_group
    {
        /// <summary>
        /// LINQ group子句
        /// </summary>
        class Program
        {
            static void Main(string[] args)
            {
                List<GustInfo> gList = new List<GustInfo>() 
                {
                    new GustInfo(){ Name="DebugLZQ",Age=26,Tel="187********"},
                    new GustInfo(){ Name="Sarah",Age=25,Tel="159********"},
                    new GustInfo(){ Name="Jerry",Age=35,Tel="135********"},
                    new GustInfo(){ Name="M&M",Age=16,Tel="136********"},
                    new GustInfo(){ Name="DebugMan",Age=26,Tel="136********"},
                    new GustInfo(){ Name="Jerry&Tom",Age=19,Tel="136********"},
                };
    
                var query = from guest in gList
                            group guest by guest.Name.Substring(0, 1);//分组键key是string类型
    
                //遍历键值和键值所属元素
                foreach (IGrouping<string, GustInfo> guestGroup in query)
                {
                    Console.WriteLine("分组键:{0}",guestGroup.Key );
                    foreach (var g in guestGroup)
                    {
                        Console.WriteLine("{0} 年龄:{1}  电话:{2}",g.Name,g.Age,g.Tel );
                    }
                }
                Console.ReadKey(false);
    
                Console.WriteLine("-----------------------------------");
    
                var query2 = from guest in gList
                             group guest by guest.Age > 20;//分组键key是bool类型表达式的结果
    
                foreach (IGrouping<bool, GustInfo> guestGroup in query2)
                {
                    Console.WriteLine("年龄是否大于20  分组键:{0}", guestGroup.Key);
                    foreach (var g in guestGroup)
                    {
                        Console.WriteLine("{0} 年龄:{1}  电话:{2}", g.Name, g.Age, g.Tel);
                    }
                }
                Console.ReadKey(false);
            }
        }
    }
    复制代码

    程序的运行结果如下:

     5.into子句

     into子句作为一个临时标识符,用于group、select、join子句中充当其结果的引用。 

    复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace LINQ_into
    {
        /// <summary>
        /// LINQ group
        /// </summary>
        class Program
        {
            static void Main(string[] args)
            {
                List<GustInfo> gList = new List<GustInfo>() 
                {
                    new GustInfo(){ Name="DebugLZQ",Age=26,Tel="187********"},
                    new GustInfo(){ Name="Sarah",Age=25,Tel="159********"},
                    new GustInfo(){ Name="Jerry",Age=35,Tel="135********"},
                    new GustInfo(){ Name="M&M",Age=16,Tel="136********"},
                    new GustInfo(){ Name="DebugMan",Age=26,Tel="136********"},
                    new GustInfo(){ Name="Jerry&Tom",Age=19,Tel="136********"},
                };
    
                //1.into用于group子句
                var query = from guest in gList
                            group guest by guest.Name.Substring(0, 1) into grguest
                            orderby grguest.Key descending
                            select grguest;
    
                var query2 = from guest in gList
                            group guest by guest.Name.Substring(0, 1) into grguest
                            orderby grguest.Key ascending 
                            select grguest;
    
                //2.select 子句中的into子句
                var query3 = from guest in gList
                             select new { NewName = guest.Name, NewAge = guest.Age } into newguest
                             orderby newguest.NewAge
                             select newguest;
    
                foreach (var guestGroup in query)
                {
                    Console.WriteLine("分组键:{0}",guestGroup.Key );
                    foreach (var g in guestGroup)
                    {
                        Console.WriteLine("{0} 电话:{1}",g.Name,g.Tel );
                    }
                }
                Console.ReadKey(false);
    
                foreach (var newg in query3)
                {
                    Console.WriteLine("{0} 年龄:{1}",newg.NewName,newg.NewAge );
                }
    
                Console.ReadKey(false);
            }
        }
    }
    复制代码

    程序运行结果如下:

    6.orderby子句、thenby子句

     LINQ可以按照元素的一个或多个属性对元素进行排序。 

    复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace LINQ_orderby
    {
        class Program
        {
            /// <summary>
            /// LINQ orderby (ascending descending)
            /// </summary>
            /// <param name="args"></param>
            static void Main(string[] args)
            {
                List<GustInfo> gList = new List<GustInfo>() 
                {
                    new GustInfo(){ Name="DebugLZQ",Age=26,Tel="187********"},
                    new GustInfo(){ Name="Sarah",Age=25,Tel="159********"},
                    new GustInfo(){ Name="Jerry",Age=35,Tel="135********"},
                    new GustInfo(){ Name="M&M",Age=16,Tel="136********"},
                    new GustInfo(){ Name="DebugMan",Age=26,Tel="136********"},
                    new GustInfo(){ Name="Jerry&Tom",Age=19,Tel="136********"},
                };
    
                //按照年龄排序
                var query = from guest in gList
                            orderby guest.Age
                            select guest;
                var query1 = from guest in gList
                             orderby guest.Age ascending
                             select guest;
                var query2 = from guest in gList
                             orderby guest.Age descending
                             select guest;
                //按照年龄进行排序,按照名字字数进行次要排序
                var query3 = from guest in gList
                            orderby guest.Age, guest.Name.Length
                            select guest;
                var query4 = from guest in gList
                             orderby guest.Age descending , guest.Name.Length ascending 
                             select guest;
                var query5 = from guest in gList
                             orderby guest.Age, guest.Name.Length,guest.Tel 
                             select guest;
    
                foreach (var guest in query2)
                {
                    Console.WriteLine("{0} 年龄:{1} 电话:{2}",guest.Name,guest.Age,guest.Tel );
                }
                Console.ReadKey(false);
                foreach (var guest in query4)
                {
                    Console.WriteLine("{0} 年龄:{1} 电话:{2}", guest.Name, guest.Age, guest.Tel);
                }
                Console.ReadKey(false);
            }
        }
    }
    复制代码

    程序运行结果如下:

     7.let子句

    let子句用于在LINQ表达式中存储子表达式的计算结果。 

    复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace LINQ_let
    {
        /// <summary>
        /// LINQ let用来存子储表达式的计算结果
        /// </summary>
        class Program
        {
            static void Main(string[] args)
            {
                List<GustInfo> gList = new List<GustInfo>() 
                {
                    new GustInfo(){ Name="DebugLZQ",Age=26,Tel="187********"},
                    new GustInfo(){ Name="Sarah",Age=25,Tel="159********"},
                    new GustInfo(){ Name="Jerry",Age=35,Tel="135********"},
                    new GustInfo(){ Name="M&M",Age=16,Tel="136********"},
                    new GustInfo(){ Name="DebugMan",Age=26,Tel="136********"},
                    new GustInfo(){ Name="Jerry&Tom",Age=19,Tel="136********"},
                };
    
                //使用let子句创建范围变量g,并通过g构建查询表达式
                var query = from guest in gList
                            let g = guest.Name.Substring(0, 1)
                            where g == "D" || g == "J"
                            select guest;
                //也可以不使用let,上面的语句等效于下
                var query2 = from guest in gList
                             where guest.Name.Substring(0, 1) == "D" || guest.Name.Substring(0, 1) == "J"
                             select guest;
    
                foreach (var g in query)
                {
                    Console.WriteLine("{0} 年龄:{1} 电话:{2}",g.Name,g.Age,g.Tel );
                }
                Console.ReadKey(false);
                Console.WriteLine("不使用let,等效的语句结果");
                foreach (var g in query2)
                {
                    Console.WriteLine("{0} 年龄:{1} 电话:{2}", g.Name, g.Age, g.Tel);
                }
                Console.ReadKey(false);
            }
        }
    }
    复制代码

    程序的运行结果如下:

    8.join子句

    如果一个数据源中元素的某个属性可以跟另外一个数据源的中元素的某个属性进行相等比较,那么这两个数据源可以用join子句进行关联。

    join子句使用equals关键字进行相等比较,而不是常用的双等号。 

    复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace LINQ_join
    {
        class Program
        {
            static void Main(string[] args)
            {
                //定义两个数据源
                List<GustInfo> gList = new List<GustInfo>() 
                {
                    new GustInfo(){ Name="DebugLZQ",Age=26,Tel="187********"},
                    new GustInfo(){ Name="Sarah",Age=25,Tel="159********"},
                    new GustInfo(){ Name="Jerry",Age=35,Tel="135********"},
                    new GustInfo(){ Name="M&M",Age=16,Tel="136********"},
                    new GustInfo(){ Name="DebugMan",Age=26,Tel="136********"},
                    new GustInfo(){ Name="Jerry&Tom",Age=19,Tel="136********"},
                };
    
                List<GuestTitle> titleList = new List<GuestTitle>() 
                {
                    new GuestTitle(){Name="DebugLZQ",Title="Soft Engineer"},
                    new GuestTitle(){Name="DebugLZQ",Title="Team Leader"},
                    new GuestTitle(){Name="Sarah",Title="Test Engineer"},
                    new GuestTitle(){Name="Jerry",Title="Head Master"}
                };
                //1.根据姓名进行内连接
                var query = from guest in gList
                            join title in titleList on guest.Name equals title.Name
                            select new { Name=guest.Name ,Title=title.Title,Age=guest.Age };
    
                foreach (var g in query)
                {
                    Console.WriteLine("{0} {1} 年龄:{2}",g.Name,g.Title ,g.Age );
                }
                Console.ReadKey(false);
    
                //前面的多个from实现相同的作用:与内连接区别在于:这个中间的操作是叉乘获得笛卡尔积
                var query2=from guest in gList 
                           from title in titleList 
                           where guest.Name==title.Name
                           select new { Name = guest.Name, Title = title.Title, Age = guest.Age };
    
                foreach (var g in query2)
                {
                    Console.WriteLine("{0} {1} 年龄:{2}", g.Name, g.Title, g.Age);
                }
                Console.ReadKey(false);
    
                //2.根据姓名进行分组连接
                //根据名字分组后,得到每个名字下的全部名称
                var query3 = from guest in gList
                             join title in titleList on guest.Name equals title.Name into tgroup
                             select new { Name=guest.Name,Titles=tgroup };
    
                foreach (var g in query3)
                {
                    Console.WriteLine(g.Name);
                    foreach (var g2 in g.Titles)
                    {
                        Console.WriteLine("    {0}",g2.Title );
                    }
                }
                Console.ReadKey(false);
                //3.根据姓名进行左外连接
                //无职务的输出为空缺
                var query4 = from guest in gList
                             join title in titleList on guest.Name equals title.Name into tgroup
                             from subtitle in tgroup.DefaultIfEmpty()
                             select new { Name=guest.Name,Title=subtitle==null?"空缺":subtitle.Title };
                foreach (var g in query4)
                {
                    Console.WriteLine("{0}  {1}  ",g.Name ,g.Title );
                }
                Console.ReadKey(false);
                
            }
        }
    }
    复制代码

    程序结果如下:

     以上就是LINQ的基本子句的常用用法,文字不是很多,因为代码中都有详细的注释说明。其他的一些查询运算符,将在后面几篇博文中陆续总结~~ 

     推荐文献:MSDN LINQ(语言集成查询)  查询关键字(C# 参考)

  • 相关阅读:
    .NET Core 之 MSBuild 介绍
    棒!使用.NET Core构建3D游戏引擎
    在ASP.NET Core MVC中构建简单 Web Api
    TypeScript 中的 SOLID 原则
    《C# 并发编程 · 经典实例》读书笔记
    ASP.NET Core 中的那些认证中间件及一些重要知识点
    消息队列 Kafka 的基本知识及 .NET Core 客户端
    【数列区间询问中的分块思想】
    2013 Multi-University Training Contest 8
    2013 Multi-University Training Contest 7
  • 原文地址:https://www.cnblogs.com/douqiumiao/p/2932241.html
Copyright © 2020-2023  润新知