• 第十节:Linq用法大全(一)


     从该节开始,连着三节都讲介绍Linq的各种用法,分类如下图,主要围绕下图进行讲解:

    参考文档:

      101个例子:https://github.com/lupino3/101-linq-samples-core

      官方API:https://docs.microsoft.com/zh-cn/dotnet/api/system.linq.queryable.cast?view=netcore-3.1

    一. where用法

    1.作用:实现过滤查询。

    2. 案例:

    (1). 筛选2020 年1月22日或之后雇用的雇员:

    var q = from e in db.Employees
            where e.HireDate >= new DateTime(2020, 1, 22)
            select e;

    二. First/Last/Single/ElementAt

    通过一张表格对比它们用法: 

       总结:以First和FirstOrDefault为例,First当没有元素符合条件,则抛异常;而FirstOrDefault没有元素符合条件,则返回默认值,如:引用类型的默认值为null,int的默认值为0等等。当确信序列中一定有满足条件的元素时,使用First方法,取到元素后,无需再判断是否为null。Last和LastOrDefault、Single和SingleOrDefault均同理。

    1. First/FirstOrDefault

    (1). 作用:返回集合中符合条件的第一个元素,其实质就是在SQL语句中加TOP (1)

    (2). 案例:

    A. 获取顾客表中的第一个顾客。

    var c = db.Customers.First();

    B. 获取运费大于 10.00 的第一个订单。

    Order ord = db.Orders.First(o => o.Freight > 10.00M);
    或
    Order ord = db.Orders.Where(o => o.Freight > 10.00M).First();

    2. Last/LastOrDefault

    (1). 作用:

      返回集合中符合条件的最后一个元素

    3.Single/SingleOrDefault

    (1). Single: 返回序列中的唯一记录,如果没有或返回多条记录,则引发异常。

    (2). SingleOrDefault: 回序列中的唯一记录;如果该序列为空,则返回默认值;如果该序列包含多个元素,则引发异常。

    4. ElementAt/ElementAtOrDefault

    (1). ElementAt:返回指定索引的元素,如果索引超过集合长度,则抛出异常。

    (2). ElemnetAtOrDefault:返回指定索引的元素,如果索引超过集合长度,则返回元素的默认值,不抛出异常。

    PS:索引从0开始。

                    List<Product> pList = new List<Product>()
                    {
                      new Product(){pId="01",pName="ypf01"},
                      new Product(){pId="02",pName="ypf02"},
                      new Product(){pId="03",pName="ypf03"}
                     };
                    //获取索引为1的信息,这里为:pId="02",pName="ypf02"
                    var d1 = pList.ElementAt(1);
                    //超出索引报错
                    try
                    {
                        var d2 = pList.ElementAt(3);
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                    }
                    //超出索引,不报错,返回默认值,这里为null
                    var d3 = pList.ElementAtOrDefault(3);

    最后总结:

        1、当集合中只有一个元素时,可以使用Single

        2、当集合中不包含任何元素但需要返回默认值时,可以使用SingleOrDefault。

        3、当集合中包含多个元素想抛出异常时,可以使用SingleSingleOrDefault。

        4、无论集合中是否有元素,我们都想要返回一个记录时,可以使用FirstFirstOrDefault。

        5、当集合中不包含任何元素但需要返回默认值时,可以使用FirstOrDefault。

    三. Select用法

    1. 说明

      用于查询。包括:简单用法、匿名类型(2种)、指定类型、嵌套形式(2种)、调用本地方法 几种用法。

    2. 简单用法

    (1). 获取所有顾客的联系人姓名

    var q = from c in db.Customers
            select c.ContactName;

    3. 匿名类型

    (1). 获取顾客的姓名和电话。

    var q = from e in db.Customers
            select new
            {
              e.Name,
              e.Phone
            };  

    指定名称的写法:

    var q = from e in db.Customers
            select new
            {
               myName= e.Name,
               myPhone = e.Phone
            };   

    (2). 获取产品ID和对应价格的一半。(可以对原有值进行一系列运算)

    var q =
        from p in db.Products
        select new
        {
            p.ProductID,
            HalfPrice = p.UnitPrice / 2
        };

    4. 指定类型

    获取顾客的姓名

    var q = from e in db.Customers
            select new myName
            {
               firstName= e.firstName,
               lastName = e.lastName
            };  
    PS:其中myName类中包括firstName和lastName两个属性.

    5. 嵌套类型

    (1). 匿名对象中的属性也是个匿名对象. 

    var q =
        from c in db.Customers
        select new {
            c.CustomerID,
            CompanyInfo = new {c.CompanyName, c.City, c.Country},
            ContactInfo = new {c.ContactName, c.ContactTitle}
        };

    (2). 匿名对象中的属性包含一个集合.

    var q =
        from o in db.Orders
        select new {
            o.OrderID,
            DiscountedProducts =
                from od in o.OrderDetails
                where od.Discount > 0.0
                select od,
            FreeShippingDiscount = o.Freight
        };

    6. 调用本地方法

     获取电话等一系列信息,并把电话转换成国际格式.

    var q = from c in db.Customers
             where c.Country == "UK" || c.Country == "USA"
             select new
             {
                 c.CustomerID,
                 c.CompanyName,
                 Phone = c.Phone,
                 InternationalPhone = 
                 PhoneNumberConverter(c.Country, c.Phone)
             };

     转换电话的方法.

    public string PhoneNumberConverter(string Country, string Phone)
    {
        Phone = Phone.Replace(" ", "").Replace(")", ")-");
        switch (Country)
        {
            case "USA":
                return "1-" + Phone;
            case "UK":
                return "44-" + Phone;
            default:
                return Phone;
        }
    }

    四. Distinct/Count/Sum/Min/Max/Average

     1. Distinct去重

    例: 查询顾客覆盖的国家.

    var q= (from c in db.Customers
            select c.city)
    .Distinct(); 等价于SQL:SELECT DISTINCT [City] FROM [Customers]

    2. Count求个数

      对应的SQL语句: select count(*) from xxx

    (1). 简单形式

    例:求顾客的数量

    var count= db.Customers.Count();

    (2).带条件形式

    例:求年龄大约20的顾客数量

    var count=db.Customers.Count(c=>c.age>20);
    等价于
    var count=db.Customers.Where.(c=>c.age>20).Count();

    PS:

      LongCount说明:返回集合中的元素个数,返回LONG类型;不延迟。对于元素个数较多的集合可视情况可以选用LongCount来统计元素个数,它返回long类型,比较精确。生成SQL语句为:SELECT COUNT_BIG(*) FROM .

    3. Sum求和

      对应的SQL语句: select sum(*) from xxx

    (1). 简单形式

     例如:求所有订单的总运费。

    var q=db.Product.Select(o=>o.Freight).Sum();

    (2).映射形式

     例如:求所有订单的总运费。

    var q=db.Product.Sum(o=>o.Freight);

    (3).元素

    例如: 求每个城市中订单的销售总额.

    var q=from o in db.Orders
          group o by o.city into g
          select new {
                CityName=g.key,
                TotalMoney=g.Sum(p=>p.unitPrice);
          };

    4. Min求最小值

      对应的SQL语句: select min(*) from xxx

    (1). 简单形式

     例如:查找产品的最低单价。

    var q=db.Products.Select(p=>p.unitPrice).Min();

    (2).映射形式

     例如:查找产品的最低单价。

    var q=db.Products.Min(p=>p.unitPrice);

    (3).元素

    例如:查找每个类别中单价最低的产品的相关信息。

    var productDetails=
          from p in db.Products
          group p by p.CategoryID into g
          select new {
                 CategoryID = g.Key,
                 CheapestProducts=
                       from q in g
                       where q.UnitPrice==g.Min(m=>m.UnitPrice)
                       select q
          };

    5.Max求最大值

      对应的SQL语句: select max(*) from xxx

    (1). 简单形式

     例如:查找产品的最高单价。

    var q=db.Products.Select(p=>p.unitPrice).Max();

    (2).映射形式

     例如:查找产品的最高单价。

    var q=db.Products.Max(p=>p.unitPrice);

    (3).元素

    例如:查找每个类别中单价最高的产品的相关信息。

    var productDetails=
          from p in db.Products
          group p by p.CategoryID into g
          select new {
                 CategoryID = g.Key,
                 HighestProducts=
                       from q in g
                       where q.UnitPrice==g.Max(m=>m.UnitPrice)
                       select q
          };

    6. Average求平均值

      对应的SQL语句: select avg(*) from xxx

    (1). 简单形式

     例如:查找所有产品的平均价。

    var q=db.Products.Select(p=>p.unitPrice).Average();

    (2).映射形式

     例如:查找产品的平均价。

    var q=db.Products.Average(p=>p.unitPrice);

    (3).元素

    例如:查找每个类别中单价高于平均价的产品的相关信息。

    var productDetails=
          from p in db.Products
          group p by p.CategoryID into g
          select new {
                 CategoryID = g.Key,
                 myProducts=
                       from q in g
                       where q.UnitPrice > g.Average(m=>m.UnitPrice)
                       select q
          };

    五. 关联查询(Join)

      用到的用户表和用户登录记录表如下:

      

      这里类比SQL语句里的查询,查询包括内连接和外连接,其中,

    1.内连接分为:隐式内连接和显示内连接.

    特点:二者只是写法不同,查询出来的结果都是多表交叉共有的。

    (1).隐式内连接: 多个from并联拼接

    (2).显示内连接: join-in-on拼接,注意没有into哦!加上into就成外连接了。

    PS:这里的内连接相当于sql中的等值连接inner join。

    2.外连接分为:左外连接和右外连接.

    (1).左外连接:查询出JOIN左边表的全部数据,JOIN右边的表不匹配的数据用NULL来填充。

    (2).右外连接:查询出JOIN右边表的全部数据,JOIN左边的表不匹配的数据用NULL来填充。

    PS:linq中没有sql中的left/right join, 只有join,左外连接和右外连接通过颠倒数据的顺序来实现。

      注:外连接join后必须有into,然后可以加上XX.DefaultIfEmpty(),表示对于引用类型将返回null,而对于值类型则返回0。对于结构体类型,则会根据其成员类型将它们相应地初始化为null(引用类型)或0(值类型),

    如果仅需要统计右表的个数或者其它属性,可以省略XX.DefaultIfEmpty, 但如果需要点出来右表的字段,则不能省。

    3. 分析几个场景,一对一,一对多,而且还要统计个数的案例

    (1).用户表-用户详情表(一对一):用内连接

    (2).用户表-用户登录记录表(一对零,一对多):用左外连接,用户表为左,如果统计个数需要用Distinct()去重.

     1                 //4.查询账号中含有admin的所有用户的用户昵称、账号、和登录信息
     2                 //4.1 隐式内连接(匿名类且不指定名称)
     3                 Console.WriteLine("---------------04-多表关联查询--------------------");
     4                 Console.WriteLine("---------------4.1 隐式内连接(匿名类且不指定名称)--------------------");
     5                 var uList1 = (from a in db.Sys_UserInfor
     6                               from b in db.LoginRecords
     7                               where a.id == b.userId 
     8                               select new
     9                               {
    10                                   a.userName,
    11                                   a.userAccount,
    12                                   b.loginCity,
    13                                   b.loginIp,
    14                                   b.loginTime
    15                               }).ToList();
    16                 foreach (var item in uList1)
    17                 {
    18                     Console.WriteLine("姓名:{0},账号:{1},登录城市:{2},登录IP:{3},登录时间:{4}", item.userName, item.userAccount, item.loginCity, item.loginIp, item.loginTime);
    19                 }
    20                 //4.2 显式内链接(匿名类 且部分列指定名称)  
    21                 Console.WriteLine("---------------4.2 显式内链接(匿名类 且部分列指定名称)  --------------------");
    22                 var uList2 = (from a in db.Sys_UserInfor
    23                               join b in db.LoginRecords on a.id equals b.userId                         
    24                               select new
    25                               {
    26                                   UserName = a.userName,
    27                                   UserAccount = a.userAccount,
    28                                   b.loginCity,
    29                                   b.loginIp,
    30                                   b.loginTime
    31                               }).ToList();
    32                 foreach (var item in uList2)
    33                 {
    34                     Console.WriteLine("姓名:{0},账号:{1},登录城市:{2},登录IP:{3},登录时间:{4}", item.UserName, item.UserAccount, item.loginCity, item.loginIp, item.loginTime);
    35                 }
    36                 //4.3 查询所有用户的登录信息(左外连接的方式)
    37                 //join时必须将join后的表into到一个新的变量XX中,然后要用XX.DefaultIfEmpty()表示外连接。
    38                 //DefaultIfEmpty使用了泛型中的default关键字。default关键字对于引用类型将返回null,而对于值类型则返回0。对于结构体类型,则会根据其成员类型将它们相应地初始化为null(引用类型)或0(值类型)
    39                 Console.WriteLine("-----------------------4.3 查询所有用户的登录信息(左外连接的方式)----------------------------");
    40                 var uList3 = (from a in db.Sys_UserInfor
    41                               join b in db.LoginRecords on a.id equals b.userId into fk
    42                               from c in fk.DefaultIfEmpty()
    43                               select new
    44                               {
    45                                   UserName = a.userName,
    46                                   UserAccount = a.userAccount,
    47                                   c.loginCity,
    48                                   c.loginIp,
    49                                   c.loginTime
    50                               }).ToList();
    51                 foreach (var item in uList3)
    52                 {
    53                     Console.WriteLine("姓名:{0},账号:{1},登录城市:{2},登录IP:{3},登录时间:{4}", item.UserName, item.UserAccount, item.loginCity, item.loginIp, item.loginTime);
    54                 }
    55                 // 4.4 查询所有用户的登录信息(右外连接的方式)
    56                 Console.WriteLine("-----------------------4.4 查询所有用户的登录信息(右外连接的方式)----------------------------");
    57                 var uList4 = (from a in db.LoginRecords
    58                               join b in db.Sys_UserInfor on a.userId equals b.id into fk
    59                               from c in fk.DefaultIfEmpty()
    60                               select new
    61                               {
    62                                   UserName = c.userName,
    63                                   UserAccount = c.userAccount,
    64                                   a.loginCity,
    65                                   a.loginIp,
    66                                   a.loginTime
    67                               }).ToList();
    68                 foreach (var item in uList4)
    69                 {
    70                     Console.WriteLine("姓名:{0},账号:{1},登录城市:{2},登录IP:{3},登录时间:{4}", item.UserName, item.UserAccount, item.loginCity, item.loginIp, item.loginTime);
    71                 }
    72                 //4.5 查询每个用户的登录次数(用且应该用左外连接 )
    73                 //注:这里需要加一个Distinct()去重,否则同一个账号会查出来多条数据重复了
    74                 Console.WriteLine("-----------------------4.5 查询每个用户的登录次数(用且应该用左外连接 )----------------------------");
    75                 var uList5 = (from a in db.Sys_UserInfor
    76                               join b in db.LoginRecords on a.id equals b.userId into fk
    77                               select new
    78                               {
    79                                   UserName = a.userName,
    80                                   UserAccount = a.userAccount,
    81                                   loginCount = fk.Count()
    82                               }).Distinct().ToList();
    83                 foreach (var item in uList5)
    84                 {
    85                     Console.WriteLine($"姓名:{item.UserName},账号:{item.UserAccount},登录次数:{item.loginCount}");
    86                 }

     运行 结果:

    特别注意:

      上面的linq左外连接,是直接操控数据库,会映射成标准的left join的SQL语句,所以上面写法没问题;但是如果用linq进行做外链接,操作的是查询出来list,则select的时候必须对右表判空!!!

    (1). 直接操控数据库

     1           var uList3 = (from a in db.Sys_UserInfor
     2                         join b in db.LoginRecords on a.id equals b.userId into fk
     3                         from c in fk.DefaultIfEmpty()
     4                         select new
     5                         {
     6                              UserName = a.userName,
     7                              UserAccount = a.userAccount,
     8                              c.loginCity,
     9                              c.loginIp,
    10                              c.loginTime
    11                          }).ToList();

    (2).操控已经查询出来的list(必须对右表进行判空处理)

     1    var sList=db.Sys_UserInfor.ToList();
     2    var rList=db.LoginRecords.ToList();        
     4    var uList3 = (from a in db.Sys_UserInfor
     5                  join b in db.LoginRecords on a.id equals b.userId into fk
     6                  from c in fk.DefaultIfEmpty()
     7                  select new
     8                  {
     9                     UserName = a.userName,
    10                     UserAccount = a.userAccount,
    11                     loginCity = c==null?"":c.loginCity,
    12                     loginIp = c==null?"":c.loginIp,
    13                     loginTime = c==null?"":c.loginTime
    14                  }).ToList();

    !

    • 作       者 : Yaopengfei(姚鹏飞)
    • 博客地址 : http://www.cnblogs.com/yaopengfei/
    • 声     明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
    • 声     明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
     
  • 相关阅读:
    springboot controller传参,对象映射
    将已有的lng lat 字段转换成point类型字段
    导入csv 到mysql数据库
    spring 数据库字段映射
    spring restTemplate使用方法
    mongo 大数据量更新注意事项
    mongo大数据量更新服务端超时解决: Cursor not found, cursor id: 82792803897
    JS 判断是否为null
    JS 日期格式化
    杨氏矩阵:查找x是否在矩阵中,第K大数
  • 原文地址:https://www.cnblogs.com/yaopengfei/p/11683231.html
Copyright © 2020-2023  润新知