Linq是什么

  • LINQ是Language Integrate Query的缩写,意为语言集成查询,是微软在.Net Framework 4.5版中推出的主要特性之一。
  • 它为开发人员提供了统一的数据查询模式,并与.Net开发语言(如C#和VB.Net)集成,很大程度上简化了数据查询的编码和调试工作,提供了数据查询的性能。
  • LINQ中查询表达式访问的是一个对象,而该对象可以表示为各种类型的数据源。比如SQL Server数据库,XML文档,ADO.NET数据集,以及内存中的数据集合等。
  • 在.NET类库中,LINQ相关类库都在Systenq命名空间中,该命名空间提供支持使用LINQ进行查询的类和接口,其中主要是以下两个接口和两个类:
    • IEnumerable< T >接口:它表示可以查询的数据集合,一个查询通常是逐个对集合对象中的元素进行筛选操作,返回一个新的IEnumerable< T >对象,用来保存查询结果。
    • IQueryable< T >接口:它继承自IEnumerable< T >接口,表示一个可以查询的表达式目录树。
    • Enumerable类:它通过对IEnumerable< T >提供扩展方法,实现LINQ标准查询运算。包括过滤、导航、排序、关联、求和、求最大值、求最小值等操作。
    • Queryable类:它通过对IQueryable< T >提供扩展方法,实现LINQ标准查询运算。包括过滤、导航、排序、关联、求和、求最大值、求最小值等操作。
  • Linq提供以下数据源的查询功能:
    • 支持 IEnumerable 或泛型 IEnumerable 接口的任何对象集合
    • SQL Server 数据库
    • XML文档
    • ADO.NET数据库
    • Web 服务和其他数据库

传统查询与Linq查询

使用Linq需要先引入命名空间System.Linq;

eg:查询等级>=2的雇员

    class Employee
    {
        public int Id { get;private set; }
        public string Name { get;private set; }
        public int Age { get;private set; }
        public int LevelNum { get;private set; }

        public Employee(int id,string name,int age,int levelNum)
        {
            this.Id = id;
            this.Name = name;
            this.Age = age;
            this.LevelNum = levelNum;
        }

        public override string ToString()
        {
            return string.Format("ID={0},Name={1},Age={2},LevelNum={3}", this.Id, this.Name, this.Age, this.LevelNum);
        }
    }

    class EmpLevel
    {
        public int Id { get; private set; }
        public int LevelNum { get; private set; }
        public string LevelName { get; private set; }
        public float Salary { get; private set; }

        public EmpLevel(int id,int levelNum,string levelName,float salary)
        {
            this.Id = id;
            this.LevelName = levelName;
            this.LevelNum = levelNum;
            this.Salary = salary;
        }

        public override string ToString()
        {
            return string.Format("ID={0},LevelNum={1},LevelName={2},Salary={3}", this.Id, this.LevelNum, this.LevelName, this.Salary);
        }
    }
  
    class Test
    {
        List<EmpLevel> empLeArray;
        List<Employee> empArray;
        List<Employee> empQueryResult;

        //构造函数
        public Test()
        {
            empArray = new List<Employee>();
            empLeArray = new List<EmpLevel>();
            empQueryResult = new List<Employee>();

            empArray.Add(new Employee(1, "张三", 30, 2));
            empArray.Add(new Employee(4, "王一", 36, 2));
            empArray.Add(new Employee(2, "李四", 20, 1));
            empArray.Add(new Employee(3, "王五", 35, 3));

            empLeArray.Add(new EmpLevel(1, 1, "1级", 5000));
            empLeArray.Add(new EmpLevel(2, 2, "2级", 8000));
            empLeArray.Add(new EmpLevel(3, 3, "3级", 10000));
        }
        
        //普通写法
        public void Test1()
        {
            foreach (Employee emp in empArray)
            {
                if (emp.LevelNum >= 2)
                {
                    empQueryResult.Add(emp);
                }
            }
            foreach (Employee emp in empQueryResult)
            {
                Console.WriteLine(emp.ToString());
            }
        }
        
        //Linq写法
        //var关键字是一种定义变量的数据类型
        //var可以代替数据类型,在编译时根据上下文来判断你想使用什么样的类型
        public void Test2()
        {
            var resule = from m in empArray
                         where m.LevelNum >= 2
                         select m;
            foreach (var item in resule)
            {
                Console.WriteLine(item);
            }
        }
    }

Linq扩展方法查询

扩展方法是什么

  • 扩展方法能够向现有类型“添加”方法,而无需创建新的派生类型、重新编译或以其他方式修改原始类型。
  • 扩展方法是一种特殊的静态方法,调用扩展方法与调用在类型中实际定义的方法之间没有明显的差异

扩展方法如何编写

  • 扩展方法被定义为静态方法,但它们是通过实例方法语法进行调用的。
  • 只有静态类才能增加扩展方法
  • 它们的第一个参数指定该方法作用于哪个类型,并且该参数以 this 修饰符为前缀。
  • 扩展方法必须要有三个参数分别是this,要扩展的类型、对象名称,比如public static string Test(this string name)。
  • 只有使用 using 指令将命名空间显式导入到源代码中之后,扩展方法才能位于范围中。
  • 调用扩展方法必须要使用对象来调用
    static class Test2
    {
        public static void Test1()
        {
            string str = "Hello";
            string strR = str.Test();
            Console.WriteLine(strR);
        }
        
        //这里Test就是一个拓展方法
        public static string Test(this string name)
        {
            return name + "World";
        }
    }

Linq扩展方法

eg:查询等级>=2的雇员

        //linq扩展方法查询
        public void Test3()
        {
            var result = empArray.Where(FilterCondition);//使用where扩展方法过滤出符合的项目
            foreach (var item in result)
            {
                Console.WriteLine(item);
            }
        }
        //拓展方法过滤条件
        private bool FilterCondition(Employee employee)
        {
            if (employee.LevelNum >= 2)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        //扩展方法使用lambda表达式
        public void Test4()
        {
            var result = empArray.Where(e => e.LevelNum >= 2);
            foreach (var item in result)
            {
                Console.WriteLine(item);
            }
        }

Linq联合查询(并集)

普通方法

eg:查询雇员与雇员级别的并集

//联合查询查询出两个集合的并集(雇员与雇员级别)
        public void Test5()
        {
            var result = from e in empArray
                         from l in empLeArray
                         select new { e, l };

            foreach (var item in result)
            {
                Console.WriteLine(item.ToString());
            }
        }

eg:输出所有级别大于等于2的雇员姓名、级别名称、工资

        public void Test6()
        {
            var result = from e in empArray
                         from l in empLeArray
                         where e.LevelNum == l.LevelNum && e.LevelNum >= 2
                         select new { e.Name, l.LevelName, l.Salary };
            foreach (var item in result)
            {
                Console.WriteLine(item.ToString());
            }
        }

Join On方法

eg:输出所有级别大于等于2的雇员姓名、级别名称、工资

        public void Test8()
        {
            var result = from e in empArray
                         join l in empLeArray
                         on e.LevelNum equals l.LevelNum
                         where e.LevelNum == l.LevelNum && e.LevelNum >= 2
                         select new { e.Name, l.LevelName, l.Salary };
            foreach (var item in result)
            {
                Console.WriteLine(item.ToString());
          

Linq查询结果排序

eg:输出所有级别大于等于2的雇员id、姓名、级别名称、工资,按照编号和级别降序排序

        public void Test7()
        {
            var result = from e in empArray
                         from l in empLeArray
                         where e.LevelNum == l.LevelNum && e.LevelNum >= 2
                         orderby e.Id descending, e.LevelNum descending
                         select new { e.Id, e.Name, l.LevelName, l.Salary };
            foreach (var item in result)
            {
                Console.WriteLine(item.ToString());
            }
        }
  • 排序关键字:orderby
  • 降序关键字:descending
  • 写在前面的排序条件优先级更高

group by into进行分组查询

eg:对雇员信息集合,按照级别进行分组查询,查询统计每个级别的人员数量

        public void Test9()
        {
            var result = from e in empArray
                         group e by e.LevelNum into g
                         select new { g.Key, counts = g.Count() };

            foreach (var item in result)
            {
                Console.WriteLine("级别:" + item.Key + " 个数:" + item.counts);
            }
        }

any all关键字

  • any表示是否至少有一个满足查询条件
  • all表示必须都满足查询条件

eg:集合中是否有/都是以张三为姓名的项目

        public void Test10()
        {
            bool result = empArray.Any(m => m.Name == "张三");
            Console.WriteLine("集合中是否有以张三为姓名的项目:" + result);
        }
        public void Test11()
        {
            bool result = empArray.All(m => m.Name == "张三");
            Console.WriteLine("集合中是否都是以张三为姓名的项目:" + result);

附录:输出结果

        static void Main(string[] args)
        {
            Test test = new Test();
            test.Test1();
            Console.WriteLine("---------------------");
            test.Test2();
            Console.WriteLine("---------------------");
            Test2.Test1();
            Console.WriteLine("---------------------");
            test.Test3();
            Console.WriteLine("---------------------");
            test.Test4();
            Console.WriteLine("---------------------");
            test.Test5();
            Console.WriteLine("---------------------");
            test.Test6();
            Console.WriteLine("---------------------");
            test.Test7();
            Console.WriteLine("---------------------");
            test.Test8();
            Console.WriteLine("---------------------");
            test.Test9();
            Console.WriteLine("---------------------");
            test.Test10();
            Console.WriteLine("---------------------");
            test.Test11();
            Console.ReadKey();
        }

/*输出:
ID=1,Name=张三,Age=30,LevelNum=2
ID=4,Name=王一,Age=36,LevelNum=2
ID=3,Name=王五,Age=35,LevelNum=3
---------------------
ID=1,Name=张三,Age=30,LevelNum=2
ID=4,Name=王一,Age=36,LevelNum=2
ID=3,Name=王五,Age=35,LevelNum=3
---------------------
HelloWorld
---------------------
ID=1,Name=张三,Age=30,LevelNum=2
ID=4,Name=王一,Age=36,LevelNum=2
ID=3,Name=王五,Age=35,LevelNum=3
---------------------
ID=1,Name=张三,Age=30,LevelNum=2
ID=4,Name=王一,Age=36,LevelNum=2
ID=3,Name=王五,Age=35,LevelNum=3
---------------------
{ e = ID=1,Name=张三,Age=30,LevelNum=2, l = ID=1,LevelNum=1,LevelName=1级,Salary=5000 }
{ e = ID=1,Name=张三,Age=30,LevelNum=2, l = ID=2,LevelNum=2,LevelName=2级,Salary=8000 }
{ e = ID=1,Name=张三,Age=30,LevelNum=2, l = ID=3,LevelNum=3,LevelName=3级,Salary=10000 }
{ e = ID=4,Name=王一,Age=36,LevelNum=2, l = ID=1,LevelNum=1,LevelName=1级,Salary=5000 }
{ e = ID=4,Name=王一,Age=36,LevelNum=2, l = ID=2,LevelNum=2,LevelName=2级,Salary=8000 }
{ e = ID=4,Name=王一,Age=36,LevelNum=2, l = ID=3,LevelNum=3,LevelName=3级,Salary=10000 }
{ e = ID=2,Name=李四,Age=20,LevelNum=1, l = ID=1,LevelNum=1,LevelName=1级,Salary=5000 }
{ e = ID=2,Name=李四,Age=20,LevelNum=1, l = ID=2,LevelNum=2,LevelName=2级,Salary=8000 }
{ e = ID=2,Name=李四,Age=20,LevelNum=1, l = ID=3,LevelNum=3,LevelName=3级,Salary=10000 }
{ e = ID=3,Name=王五,Age=35,LevelNum=3, l = ID=1,LevelNum=1,LevelName=1级,Salary=5000 }
{ e = ID=3,Name=王五,Age=35,LevelNum=3, l = ID=2,LevelNum=2,LevelName=2级,Salary=8000 }
{ e = ID=3,Name=王五,Age=35,LevelNum=3, l = ID=3,LevelNum=3,LevelName=3级,Salary=10000 }
---------------------
{ Name = 张三, LevelName = 2级, Salary = 8000 }
{ Name = 王一, LevelName = 2级, Salary = 8000 }
{ Name = 王五, LevelName = 3级, Salary = 10000 }
---------------------
{ Id = 4, Name = 王一, LevelName = 2级, Salary = 8000 }
{ Id = 3, Name = 王五, LevelName = 3级, Salary = 10000 }
{ Id = 1, Name = 张三, LevelName = 2级, Salary = 8000 }
---------------------
{ Name = 张三, LevelName = 2级, Salary = 8000 }
{ Name = 王一, LevelName = 2级, Salary = 8000 }
{ Name = 王五, LevelName = 3级, Salary = 10000 }
---------------------
级别:2 个数:2
级别:1 个数:1
级别:3 个数:1
---------------------
集合中是否有以张三为姓名的项目:True
---------------------
集合中是否都是以张三为姓名的项目:False
*/

附录:参考资料

https://baike.baidu.com/item/LINQ/4462670?fr=aladdin

https://www.bilibili.com/video/BV1G7411c773

https://www.cnblogs.com/marshhu/p/6939763.html

https://www.cnblogs.com/sf-2020/p/14190468.html

https://blog.csdn.net/han_yankun2009/article/details/25334021