10、vs2022_linq

一、相关概念
LINQ数据的基本组成:序列和元素。(序列是任何实现了IEnumerable<T>接口的对象)

复制代码
//数据源
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
//使用for foreach
foreach (var num in numbers)
    if(num % 2 == 0)
    {
        Console.WriteLine(num);
    }
Console.WriteLine("---------------------"); //分割线

//linq的三种写法

//1、声明式查询语法
var num1 = from x in numbers
           where x % 2 == 0
           select x;
foreach (var x in num1)
    Console.WriteLine(x);
Console.WriteLine("---------------------"); //分割线
//2、方法语法
var num2 = numbers.Where(x => x % 2 == 0);
foreach (var x in num2)
    Console.WriteLine(x);
Console.WriteLine("---------------------"); //分割线

//3、结合使用查询语法和方法语法
//偶数的个数
int count = (from x in numbers
            where x % 2 == 0
            select x).Count();
Console.WriteLine("偶数是{0}个",count); 
复制代码

 

查询运算符:是LINQ中进行序列转换的方法。
查询运算符可接受一个输入序列,并将其转换为一个输出序列(在System.Linq命名空间的Enumerable类中定义了约40种查询运算符)。

select、from、in、where、orderby、group、by、into、join、equals、on、descending、ascending、let等等

本地查询:本地序列进行的查询操作称为本地查询或LINQ-to-objects查询。
解释型查询:从远程数据源(如SQL Server数据库)中动态获取的序列进行查询。
查询:是一个使用查询运算符在枚举序列的过程中对序列进行转换的表达式

//输出字符串长度大于或等于4位的姓名
string[] names = { "Tom", "Dick", "Harry" };
IEnumerable<string> filteredNames = System.Linq.Enumerable.Where(names, x => x.Length >= 4);
foreach (string n in filteredNames)
    Console.WriteLine(n);//输出Dick,Harry

 

二、 本地查询:

1.流式语法(最基础同时也是最灵活的编写LINQ表达式的方式)

//输出字符串中含有字母a的姓名,并转为大写
string[] names = { "Tom", "Dick", "Harry", "Marry", "Jay" };
IEnumerable<string> query = names
    .Where(x => x.Contains("a"))
    .OrderBy(x => x.Length)
    .Select(x => x.ToUpper());
foreach (string n in query)
    Console.WriteLine(n); //输出JAY MARY HARRY

Where、OrderBy和Select都是标准的查询运算符。它们都会解析为Enumerable类的扩展方法

在上面代码里,Lambda表达式会把值传给Where等查询运算符。

.Where(one => one.xx = xxx) 条件查询  【Where可多个连用】

.Contains(one.xx)  包含 

.OrderBy(one => one.Id)  以Id排序

.Select(one => new {xx = one.xx})  选择实体属性,返回匿名对象 

.Skip(int)  跳过多少条记录

.Take(int)  获取多少条记录

Join内连  连接2个实体组合条件查询 【 from A in EnA join B in EnB on A.Id equals B.Id ... 以某个属性值作为连接点,从而组合两个实体】

Join左连 【from A in EnA join B in EnB on A.Id equals B.Id into newList from one in newList.DefaultIfEmpty() 】

2.查询表达式语法(像是在C#中内嵌SQL)

复制代码
//查询表达式语法
//输出字符串中含有字母a的姓名,并转为大写
string[] names = { "Tom", "Dick", "Harry", "Marry", "Jay" };
IEnumerable<string> query = from n in names
                            where n.Contains("a")
                            orderby n.Length
                            select n.ToUpper();
foreach (string n in query)
    Console.WriteLine(n); //输出JAY MARY HARRY
复制代码

查询表达式语法VS方法语法
查询表达式语法与方法语法存在着紧密的关系
1、CLR本身并不理解查询表达式语法,它只理解方法语法。
2、编译器负责在编译时将查询表达式语法翻译为方法语法。
3、大部分方法语法都有对应的查询表达式语法形式:如Select()对应select、OrderBy()对 应orderby
4、部分查询方法目前在C#中还没有对应的查询语句:如Count()和Max(), 这是只能采用以下替代方案:
方法语法:查询表达式语法+方法语法的混合方式.

3. 对象初始化器:

//定义辅助类
class tempclass
{
    public string old;
    public string latest;
}

将其映射到对象初始化器

复制代码
//将其映射到对象初始化器
string[] names = { "Tom", "Dick", "Harry", "Mary", "Jay" };
IEnumerable<tempclass> temp = from n in names
                              select new tempclass
                              {
                                  old = n,
                                  //将原name中含有a、e、i、o的替换掉
                                  latest = n.Replace("a", "").Replace("e", "").Replace("i", "").Replace("o", "")
                              };
//输出temp中,替换后的latest
foreach (string n in temp.Select(x => x.latest))
    Console.WriteLine(n); //输出Tm Dck Hrry Mry Jy
Console.WriteLine("-----------"); //分割线

//输出temp中,去掉aeio后长度大于2的name IEnumerable<string> query = from t in temp where t.latest.Length > 2 select t.old; foreach (string n in query) Console.WriteLine(n); //输出Dick Harry Mary
复制代码

优化(使用匿名类型来定义中间结果的形式,避免了编写特殊的类)

复制代码
//使用匿名类型,避免编写class
string[] names = { "Tom", "Dick", "Harry", "Mary", "Jay" };
var query = from n in names
            select new
            {
                old = n,
                latest = n.Replace("a", "").Replace("e", "").Replace("i", "").Replace("o", "")
            } into temp
            where temp.latest.Length > 2
            select temp.old;
foreach (string n in query)
    Console.WriteLine(n); //输出Dick Harry Mary
复制代码

三、获取数据源
在 LINQ 查询中,第一步是指定数据源。 和大多数编程语言相同,在使用 C# 时也必须先声明变量,然后才能使用它。 在 LINQ 查询中,先使用 from 子句引入数据源 (customers) 和范围变量 (cust)。

复制代码
//数据源customers
var customers = new[] {
    new { Name = "Tom", City = "London", Amount = 50 },
    new { Name = "Devon", City = "London", Amount = 70 },
    new { Name = "Dick", City = "Paris" , Amount = 80 },
    new { Name = "Harry", City = "NewYork" , Amount = 90},
};

var query = from cus in customers
            select cus;
foreach (var res in query)
    Console.WriteLine(res); //返回{ Name = Tom, City = London, Amount = 50 }{ Name = Devon, City = London, Amount = 70 }{ Name = Dick, City = Paris, Amount = 80 }{ Name = Harry, City = NewYork, Amount = 90 }
Console.WriteLine("---------------------"); //分割线
复制代码

 

范围变量就像 foreach 循环中的迭代变量,但查询表达式中不会真正发生迭代。 当执行查询时,范围变量将充当对 customers 中每个连续的元素的引用。 由于编译器可以推断 cus 的类型,因此无需显式指定它。 可通过 let 子句引入其他范围变量。

备注:对于非泛型数据源(例如 ArrayList),必须显式键入范围变量。

1、筛选

最常见的查询操作是以布尔表达式的形式应用筛选器。 筛选器使查询仅返回表达式为 true 的元素。 将通过使用 where 子句生成结果。 筛选器实际指定要从源序列排除哪些元素。 在下列示例中,仅返回地址位于“London”的 customers。

复制代码
//一、筛选
// 1、返回地址位于“London”的 customers
var query_filter1 = from cus in customers
                    where cus.City == "London"
                    select cus;
foreach (var res in query_filter1)
    Console.WriteLine(res); //返回{ Name = Tom, City = London, Amount = 50 }{ Name = Devon, City = London, Amount = 70 }
Console.WriteLine("---------------------"); //分割线

// 2、返回来自“London”的客户 AND 该客户名称为“Devon”
var query_filter2 = from cus in customers
                    where cus.City == "London" && cus.Name == "Devon"
                    select cus;
foreach (var res in query_filter2)
    Console.WriteLine(res); //返回{ Name = Devon, City = London, Amount = 70 }
Console.WriteLine("---------------------"); //分割线

// 3、返回来自 London 或 Paris 的客户
var query_filter3 = from cus in customers
                    where cus.City == "London" || cus.City == "Paris"
                    select cus;
foreach (var res in query_filter3)
    Console.WriteLine(res); //返回{ Name = Tom, City = London, Amount = 50 }{ Name = Devon, City = London, Amount = 70 }{ Name = Dick, City = Paris, Amount = 80 }
Console.WriteLine("---------------------"); //分割线
复制代码

 

2、排序

对返回的数据进行排序通常很方便。 orderby 子句根据要排序类型的默认比较器,对返回序列中的元素排序。 例如,基于 Name 属性,可将下列查询扩展为对结果排序。 由于 Name 是字符串,默认比较器将按字母顺序从 A 到 Z 进行排序。

复制代码
//二、排序
// 1、按name升序排序 customers
var query_order1 = from cus in customers
                   where cus.City == "London"
                   orderby cus.Name ascending
                   select cus;
foreach (var res in query_order1)
    Console.WriteLine(res); 
Console.WriteLine("---------------------"); //分割线

// 2、按name降序排序 customers
var query_order2 = from cus in customers
                   where cus.City == "London"
                   orderby cus.Name descending
                   select cus;
foreach (var res in query_order2)
    Console.WriteLine(res); 
Console.WriteLine("---------------------"); //分割线
复制代码

要对结果进行从 Z 到 A 的逆序排序,使用 orderby…descending 子句。

3、分组

group 子句用于对根据您指定的键所获得的结果进行分组。 例如,可指定按 City 对结果进行分组,使来自 London 或 Paris 的所有客户位于单独的组内。 在这种情况下,cus.City 是键。

复制代码
//三、分组
var query_group1 = from cus in customers
                  group cus by cus.City;
foreach (var resGroup in query_group1)
{
    Console.WriteLine(resGroup.Key);
    foreach (var res in resGroup)
    {
        Console.WriteLine(" {0}", res.Name);
    }
}
Console.WriteLine("---------------------"); //分割线
//输出:
//London
// Tom
// Devon
//Paris
// Dick
//NewYork
// Harry
复制代码

使用 group 子句结束查询时,结果将以列表的形式列出。 列表中的每个元素都是具有 Key 成员的对象,列表中的元素根据该键被分组。 在循环访问生成组序列的查询时,必须使用嵌套 foreach 循环。 外层循环循环访问每个组,内层循环循环访问每个组的成员。

如果必须引用某个组操作的结果,可使用 into 关键字创建能被进一步查询的标识符。 下列查询仅返回包含1个以上客户的组:

复制代码
var query_group2 = from cus in customers
                group cus by cus.City into g
                where g.Count() > 1
                orderby g.Key
                select g;
foreach (var resGroup in query_group2)
{
    Console.WriteLine(resGroup.Key);
    foreach (var res in resGroup)
    {
        Console.WriteLine(" {0}", res.Name);
    }
}
Console.WriteLine("---------------------"); //分割线
复制代码

4、联接

联接操作在不同序列间创建关联,这些序列在数据源中未被显式模块化。 例如,可通过执行联接来查找所有位置相同的客户和分销商。 在 LINQ 中,join 子句始终作用于对象集合,而非直接作用于数据库表。

var query_jion = from cus in customers
                 join t in temp on cus.City equals t.City
                 select new { CustomerName = cus.Name, TempName = t.Name };

在 LINQ 中,不必像在 SQL 中那样频繁使用 join,因为 LINQ 中的外键在对象模型中表示为包含项集合的属性。 例如 Customer 对象包含 Order 对象的集合。 不必执行联接,只需使用点表示法访问订单:

from order in Customer.Orders...

5、选择(投影)
select 子句生成查询结果并指定每个返回的元素的“形状”或类型。 例如,可以指定结果包含的是整个 Customer 对象、仅一个成员、成员的子集,还是某个基于计算或新对象创建的完全不同的结果类型。 当 select 子句生成除源元素副本以外的内容时,该操作称为投影。 使用投影转换数据是 LINQ 查询表达式的一种强大功能。

复制代码
//数据源customers
var customers = new[] {
    new { Name = "Tom", City = "London", Amount = 50 },
    new { Name = "Devon", City = "London", Amount = 70 },
    new { Name = "Dick", City = "Paris" , Amount = 80 },
    new { Name = "Harry", City = "NewYork" , Amount = 90},
};
//单个字段映射
var list_single = customers.Select(x => x.Name);
foreach (var res in list_single)
    Console.WriteLine(res);
Console.WriteLine("---------------------"); //分割线

//多个字段映射
var list_multiple = customers.Select(x => new { x.Name, x.City });
foreach (var res in list_multiple)
    Console.WriteLine(res);
Console.WriteLine("---------------------"); //分割线

//全部映射
var list_all = customers.Select(x => x);
foreach (var res in list_all)
    Console.WriteLine(res);
复制代码

 实例

复制代码
//linq演示
static void Main(string[] args)
{

}

#region 测试数据
static List<Category> GetCategories()
{
    List<Category> list = new List<Category>()
    {
        new Category() { CategoryId = 1,CategoryName="服装"},
        new Category() { CategoryId = 2,CategoryName="食品"},
        new Category() { CategoryId = 3,CategoryName="办公"},
    };
    return list;
};

static List<Product> GetProducts()
{
    List<Product> list = new List<Product>()
    {
        new Product() { ProductId = 1,ProductName="羽绒服",Price = 998, Storage=1500, CategoryId = 1 },
        new Product() { ProductId = 2,ProductName="运动鞋",Price = 199, Storage=198, CategoryId = 1 },
        new Product() { ProductId = 3,ProductName="T恤",Price = 95, Storage=120, CategoryId = 1 },
        new Product() { ProductId = 4,ProductName="饼干",Price = 16.5, Storage=200, CategoryId = 2 },
        new Product() { ProductId = 5,ProductName="果汁",Price = 6, Storage=2000, CategoryId = 2 },
        new Product() { ProductId = 5,ProductName="钢笔",Price = 12.5, Storage=10, CategoryId = 3 },
    };
    return list;
};
#endregion

#region 类文件
public class Category
{
    public int CategoryId { get; set; }
    public string CategoryName { get; set; }
}
public class Product
{
    public int ProductId { get; set; }
    public string ProductName { get; set; }
    public double Price { get; set; }
    public int Storage { get; set; }
    public int CategoryId { get; set; }
}
#endregion
linq演示
复制代码

 

//查询所有Product
var productList = GetProducts();
var products = from pro in productList
               select pro;
foreach (var p in products)
    Console.WriteLine($"{p.ProductId}-{p.ProductName}-{p.Price}");

 

posted @   生之韵  阅读(186)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示