LINQ(集成化查询)
LINQ可以对数组、集合等数据结构进行查询、筛选、排序等操作;也可以用于与数据库交互;也支持对XML的操作,使用LINQ技术可以动态创建、筛选和修改XML数据和直接操作XML文件。
一). LINQ基本语法:
(1)所有的LINQ查询表达式都是以from....in....开头;
(2)以select或group子句结束。以select子句结束表示从数据源序列筛选出元素;以group子句结束表示将从数据源序列中筛选出来的元素进行分组,并把每个分组放进查询结果集中。
例如:var result=from str in list select str;
var result from str in list group s by str[0];
二).延迟执行与强制立即执行
先看以下代码:var result=from str in list select str;当执行上述代码时,LINQ查询实际上并未执行,而是仅仅把用于查询的“命令”存放在result变量中,只到执行到遍历访问代码时才会执行查询:foreach(var a in result){。。。。。},这就是延迟执行。当然,可以调用Count,ToList,ToArray等方法来时查询立即执行,即使LINQ查询强行立即执行。
三).筛选、排序、分组
(1)在查询语句中,用where子句来设定筛选条件(一个或多个条件),如果where子句后面条件为真,则符合条件;若为假,则不符合条件。
例如:var a=from b in c where b>10 select b;
(2)对查询结果重新排序可以用orderby子句,默认情况下按升序排序,如果要进行降序排序,就必须在orderby子句后加上descending关键字。
例如:var a=from b in c orderby b select b;
(3)对数据进行分组要有分组依据,因此group一般与by关键字连用,格式如下:group <要进行分组的元素>by<分组依据>
如果要对分组的数据进行操作,可以在查询语句中储存个分组的数据,即在分组语句后面接上into关键字。如:group emp by emp.Department into eg;数据分组后会将每个分量都存入eg变量中,随后可以用select子句把存放在eg中的分组选择进行结果中。如下:group emp by emp.Department into eg select eg;这时候查询结果中的每个元素都是IGrouping<TKey,TElement>类型的对象,IGrouping<TKey,TElement>带了2个类型参数,TKey表示分组依据(Key)的类型,TElement表示每个分组中元素的类型,由于IGrouping接口继承自IEnumerable<out T>接口,因此,只要使用foreach循环就可以访问分组中的元素。
// 示例数据源 string[] arr = { "table", "use", "up", "take", "turn", "reset", "remove", "we", "wave", "work", "word" }; // 查询数据源中的所有元素 // 并以首字母作为分组依据 var res = from s in arr group s by s.ToUpper()[0]; // 输出查询结果 Console.WriteLine("共有{0}个分组。", res.Count()); foreach (IGrouping<char, string> item in res) { Console.WriteLine("--------- {0} --------", item.Key); // 取出该分组中的元素 foreach (string str in item) { Console.WriteLine(" {0}", str); } Console.WriteLine(); }
下面在看一例:
public class Student { /// <summary> /// 学员ID /// </summary> public uint StuID { get; set; } /// <summary> /// 学员姓名 /// </summary> public string StuName { get; set; } /// <summary> /// 课程 /// </summary> public string Course { get; set; } }
Student[] students = { new Student { StuID = 1, StuName = "小陈", Course = "C++基础" }, new Student { StuID = 2, StuName = "小林", Course = "VB入门" }, new Student { StuID = 3, StuName = "小邓", Course = "C++基础" }, new Student { StuID = 4, StuName = "小李", Course = "C#客户端开发" }, new Student { StuID = 5, StuName = "小唐", Course = "C++基础" }, new Student { StuID = 6, StuName = "小周", Course = "VB入门" }, new Student { StuID = 7, StuName = "小张", Course = "VB入门" }, new Student { StuID = 8, StuName = "小吴", Course = "C#客户端开发" }, new Student { StuID = 9, StuName = "小孙", Course = "C++基础" }, new Student { StuID = 10, StuName = "小孟", Course = "C#客户端开发" } }; // 筛选出学号大于5的学员 // 并按课程进行分组 // 学号按降序排列 var res = from s in students orderby s.StuID descending where s.StuID > 5 group s by s.Course into g select g; foreach (var g in res) { Console.WriteLine("----- {0} -----", g.Key); foreach (Student stu in g) { Console.WriteLine("学号:{0},姓名:{1},课程:{2}", stu.StuID, stu.StuName, stu.Course); } Console.WriteLine(); }
四).复杂查询
(1)动态创建类型
在比较复杂的查询中,查询结果通常会生成一个新的类型,以使其内部包含来自多个数据序列的属性。许多时候,这些新类型并不是固定的,不同的查询需求会产生不同的结果,如果为每种查询结果都去定义相应的类,那有可能变得很复杂,面对这种情况,使用动态类型是较理想的选择。动态类型无需事先去定义类,即可根据代码上下文的需要动态地去创建新类型,实现方法是运用new运算符,创建的新类型并没有确切的名称,是由编译器自动分配的,因此在声明量时应使用var关键字,由编译器自动去识别其类型。
例如:
var a = new { b = "cajj", c = 123 }; Console.WriteLine("{0} {1}", a.b, a.c);
也可以使用dynamic关键字来声明用于引用匿名类型的变量。使用dynamic声明的变量属于动态类型,在编译阶段不进行解析,只在运行时动态解析。
例如:
dynamic b = new { Name = "Java", Age = 18, Birth = new DateTime(1997, 07, 06) }; Console.WriteLine("Name={0} Age={1} Birth={2:yyyy-M-d}", b.Name, b.Age, b.Birth);
(2)联合查询
联合查询主要通过jion关键字来完成的。所谓的联合查询,可以理解为联合多个数据序列进行查询,并从中返回查询结果。查询结果中的数据可能来自于多个数据序列。多个数据序列要进行联合,需要提供一个联合条件才能使多个序列完成正确的联合,所以,jion关键字后面紧跟着on关键字,并通过equals关键字来判断联合条件是否成立。
代码如下:
/// <summary> /// 图书分类信息 /// </summary> public class Category { /// <summary> /// 分别ID /// </summary> public int catID { get; set; } /// <summary> /// 分类名 /// </summary> public string catName { get; set; } } /// <summary> /// 图书信息 /// </summary> public class BookInfo { /// <summary> /// 图书ID /// </summary> public int BookID { get; set; } /// <summary> /// 书名 /// </summary> public string BookName { get; set; } /// <summary> /// 图书所属分类的ID /// </summary> public int CateID { get; set; } }
// 图书分类示例数据 List<Category> bookCategs = new List<Category> { new Category { catID = 201, catName = "文学类" }, new Category { catID = 202, catName = "经济管理类" }, new Category { catID = 203, catName = "机械工程类" }, new Category { catID = 204, catName = "法律基础类" } }; // 图书信息示例数据 List<BookInfo> books = new List<BookInfo> { new BookInfo { BookID = 1, BookName = "图书01", CateID = 202 }, new BookInfo { BookID = 2, BookName = "图书02",CateID = 204 }, new BookInfo { BookID = 3, BookName = "图书03", CateID = 201 }, new BookInfo { BookID = 4, BookName = "图书04", CateID = 202 }, new BookInfo { BookID = 5, BookName = "图书05",CateID = 204 }, new BookInfo { BookID = 6, BookName = "图书06", CateID = 204 }, new BookInfo { BookID = 7, BookName = "图书07", CateID = 203 }, new BookInfo { BookID = 8, BookName = "图书08",CateID = 202 }, new BookInfo { BookID = 9, BookName = "图书09", CateID = 203 }, new BookInfo { BookID = 10, BookName = "图书10", CateID = 202 }, new BookInfo { BookID = 11, BookName = "图书11", CateID = 201 }, new BookInfo { BookID = 12, BookName = "图书12", CateID = 203 }, new BookInfo { BookID = 13, BookName = "图书13", CateID = 201 }, new BookInfo { BookID = 14, BookName = "图书14", CateID = 204 }, new BookInfo { BookID = 15, BookName = "图书15", CateID = 203 }, new BookInfo { BookID = 16, BookName = "图书16", CateID = 202 }, };
接下来,联合books和bookCategs两个数据序列,代码如下:
// 联合查询,并产生新的类型 var qryres = from b in books join c in bookCategs on b.CateID equals c.catID select new { b.BookName, c.catName }; // 输出结果 foreach (var bitem in qryres) { Console.WriteLine("图书名:{0},所属分类:{1}", bitem.BookName, bitem.catName); }
上述代码,select子句使用new运算符动态创建匿名类型,并使用BookInfo对象的BookName属性和Category对象的catName属性作为新类型的属性,也就是说,创建的匿名类型具有BookName和catName两个属性,当然,也可以为动态创建的匿名类型使用自己命名的属性名,代码如下:
var qryres2 = from bk in books join bc in bookCategs on bk.CateID equals bc.catID select new { Book_ID = bk.BookID, Book_Name = bk.BookName, Book_Cate = bc.catName }; // 输出结果 foreach (var bk in qryres2) { Console.WriteLine("图书ID:{0},图书名:{1},所属分类:{2}", bk.Book_ID, bk.Book_Name, bk.Book_Cate); }
在上面的例子中,与每个BookInfo对象实例的CateID属性相对应的Category对象都能在bookCategs列表中找到,这种的联合查询称为”内部联合“。下面,就考虑另一种情况,即当序列A与序列B进行联合查询时,在序列B中找不到与序列A的项匹配的元素。
代码如下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace My 8 { 9 10 #region 定义类型 11 /// <summary> 12 /// 专辑信息 13 /// </summary> 14 public class Album 15 { 16 /// <summary> 17 /// 专辑名称 18 /// </summary> 19 public string Title { get; set; } 20 /// <summary> 21 /// 发行年份 22 /// </summary> 23 public int Year { get; set; } 24 /// <summary> 25 /// 专辑描述 26 /// </summary> 27 public string Description { get; set; } 28 } 29 30 /// <summary> 31 /// 曲目信息 32 /// </summary> 33 public class Track 34 { 35 /// <summary> 36 /// 曲目名称 37 /// </summary> 38 public string Name { get; set; } 39 /// <summary> 40 /// 艺术家名字 41 /// </summary> 42 public string Artist { get; set; } 43 /// <summary> 44 /// 所属专辑 45 /// </summary> 46 public Album AlbumOf { get; set; } 47 } 48 #endregion 49 50 class Program 51 { 52 static void Main(string[] args) 53 { 54 // 专辑列表 55 Album album1 = new Album { Title = "专辑 1", Year = 2003, Description = "这是第一张专辑。" }; 56 Album album2 = new Album { Title = "专辑 2", Year = 2009, Description = "这是第二张专辑。" }; 57 List<Album> albums = new List<Album> { album1, album2 }; 58 59 // 曲目列表 60 Track track1 = new Track { Name = "曲目 1", Artist = "艺术家 1", AlbumOf = album1 }; 61 Track track2 = new Track { Name = "曲目 2", Artist = "艺术家 2", AlbumOf = album2 }; 62 Track track3 = new Track { Name = "曲目 3", Artist = "艺术家 3", AlbumOf = album2 }; 63 Track track4 = new Track { Name = "曲目 4", Artist = "艺术家 4", AlbumOf = album1 }; 64 Track track5 = new Track { Name = "曲目 5", Artist = "艺术家 5", AlbumOf = album2 }; 65 Track track6 = new Track { Name = "曲目 6", Artist = "艺术家 6", AlbumOf = null }; 66 List<Track> tracks = new List<Track> { track1, track2, track3, track4, track5, track6 }; 67 68 // 开始查询 69 var res1 = from t in tracks 70 join a in albums on t.AlbumOf equals a into g1 71 from a1 in g1.DefaultIfEmpty() 72 select new 73 { 74 TrackName = t.Name, 75 Artist = t.Artist, 76 AlbumName = a1 == null ? "未知专辑" : a1.Title 77 }; 78 79 // 以下代码会发生异常 80 //var res1 = from t in tracks 81 // join a in albums on t.AlbumOf equals a into g1 82 // from a1 in g1.DefaultIfEmpty() 83 // select new 84 // { 85 // TrackName = t.Name, 86 // Artist = t.Artist, 87 // AlbumName = a1.Title 88 // }; 89 90 // 输出结果 91 foreach (var item in res1) 92 { 93 Console.WriteLine("曲目:{0},艺术家:{1},专辑:{2}", item.TrackName, item.Artist, item.AlbumName); 94 } 95 96 Console.WriteLine(); 97 98 var res2 = from t in tracks 99 join a in albums on t.AlbumOf equals a into g 100 from a2 in g.DefaultIfEmpty(new Album { Title = "<未知>", Year = 0, Description = "<无>" }) 101 select new 102 { 103 TrackName = t.Name, 104 Year = a2.Year, 105 Artist = t.Artist, 106 AlbumName = a2.Title, 107 AlbumDesc = a2.Description 108 }; 109 // 输出结果 110 foreach (var item in res2) 111 { 112 Console.WriteLine("曲目:{0},年份:{1},艺术家:{2},专辑:{3},专辑描述:{4}", item.TrackName, item.Year, item.Artist, item.AlbumName, item.AlbumDesc); 113 } 114 115 Console.Read(); 116 } 117 } 118 }
(3) 嵌套查询
嵌套查询是指在一个查询内部嵌套这另一个查询,其实嵌套查询是比较简单的,就直接看个例子吧。
代码如下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace My 8 { 9 #region 定义类型 10 /// <summary> 11 /// 商品信息 12 /// </summary> 13 public class Goods 14 { 15 /// <summary> 16 /// 商品编号 17 /// </summary> 18 public string GsNo { get; set; } 19 /// <summary> 20 /// 商品名称 21 /// </summary> 22 public string GsName { get; set; } 23 /// <summary> 24 /// 商品单价 25 /// </summary> 26 public double GsPrice { get; set; } 27 } 28 29 /// <summary> 30 /// 销售单信息 31 /// </summary> 32 public class SalesOrder 33 { 34 /// <summary> 35 /// 单据ID 36 /// </summary> 37 public int OrderID { get; set; } 38 /// <summary> 39 /// 商品编号 40 /// </summary> 41 public string GoodsNo { get; set; } 42 /// <summary> 43 /// 销售时间 44 /// </summary> 45 public DateTime Time { get; set; } 46 /// <summary> 47 /// 销售数量 48 /// </summary> 49 public int Qty { get; set; } 50 } 51 #endregion 52 53 class Program 54 { 55 static void Main(string[] args) 56 { 57 // 商品信息 - 示例数据 58 Goods[] goodsArr = 59 { 60 new Goods { GsNo = "G-1", GsName = "报纸", GsPrice = 1.50d }, 61 new Goods { GsNo = "G-2", GsName = "食盐", GsPrice = 3.65d }, 62 new Goods { GsNo = "G-3", GsName = "火柴", GsPrice = 0.50d }, 63 new Goods { GsNo = "G-4", GsName = "灯泡", GsPrice = 12.30d }, 64 new Goods { GsNo = "G-5", GsName = "剪刀", GsPrice = 4.50d } 65 }; 66 67 // 销售单据 - 示例数据 68 SalesOrder[] orders = 69 { 70 new SalesOrder { OrderID = 1, GoodsNo = goodsArr[0].GsNo, Qty = 3, Time = new DateTime(2014, 1, 2) }, 71 new SalesOrder { OrderID = 2, GoodsNo = goodsArr[1].GsNo, Qty = 5, Time = new DateTime(2014, 1, 4) }, 72 new SalesOrder { OrderID = 3, GoodsNo = goodsArr[2].GsNo, Qty = 2, Time = new DateTime(2014, 1, 12) }, 73 new SalesOrder { OrderID = 4, GoodsNo = goodsArr[3].GsNo, Qty = 6, Time = new DateTime(2014, 1, 20) }, 74 new SalesOrder { OrderID = 5, GoodsNo = goodsArr[4].GsNo, Qty = 1, Time = new DateTime(2014, 2, 3) }, 75 new SalesOrder { OrderID = 6, GoodsNo = goodsArr[2].GsNo, Qty = 4, Time = new DateTime(2014, 2, 9) }, 76 new SalesOrder { OrderID = 7, GoodsNo = goodsArr[1].GsNo, Qty = 8, Time = new DateTime(2014, 3, 13) }, 77 new SalesOrder { OrderID = 8, GoodsNo = goodsArr[3].GsNo, Qty = 10, Time = new DateTime(2014, 3, 11) }, 78 new SalesOrder { OrderID = 9, GoodsNo = goodsArr[0].GsNo, Qty = 15, Time = new DateTime(2014, 3, 18) }, 79 new SalesOrder { OrderID = 10, GoodsNo = goodsArr[0].GsNo, Qty = 7, Time = new DateTime(2014, 2, 22) }, 80 new SalesOrder { OrderID = 11, GoodsNo = goodsArr[3].GsNo, Qty = 20, Time = new DateTime(2014, 3, 17) }, 81 new SalesOrder { OrderID = 12, GoodsNo = goodsArr[1].GsNo, Qty = 13, Time = new DateTime(2014, 1, 29) }, 82 new SalesOrder { OrderID = 13, GoodsNo = goodsArr[2].GsNo, Qty = 8, Time = new DateTime(2014, 2, 9) }, 83 new SalesOrder { OrderID = 14, GoodsNo = goodsArr[4].GsNo, Qty = 21, Time = new DateTime(2014, 3, 16) }, 84 new SalesOrder { OrderID = 15, GoodsNo = goodsArr[2].GsNo, Qty = 6, Time = new DateTime(2014, 2, 15) } 85 }; 86 87 /* 88 * 查询并计算出各个商品的 89 * 总销售额 90 */ 91 var res = from g in goodsArr 92 let totalQty = 93 /* 以下为嵌套查询 */ 94 (from od in orders 95 where od.GoodsNo == g.GsNo 96 select od).Sum(odr => odr.Qty) 97 select new 98 { 99 g.GsNo, 100 g.GsName, 101 /* 计算总销售额 */ 102 Total = totalQty * g.GsPrice 103 }; 104 105 // 输出查询结果 106 foreach (var item in res) 107 { 108 Console.WriteLine("编号:{0},商品:{1},总额:{2:N2}", item.GsNo, item.GsName, item.Total); 109 } 110 111 Console.Read(); 112 } 113 } 114 }
博客写到这,可以看到其实LINQ查询是比较简单的,只要仔细,就不会出错的。。。。