终于来到下篇了,通过上篇,和中篇,我们了解了linq的基本语句,对应linq我们又了解到lambda表达式,静态扩展方法,以及linq的延迟加载的特性,那么在本篇文章中我们将分享学习一下linq对于我们开发中常用到的对象的操作应用。如果没有阅读过上篇的请点击这里,如果没有阅读中篇的请点击这里
对于做.net 开发的有谁不知道DataSet,DataTable,DataRow,DataColumn这些对象,如果你真的不知道,那好吧建议你到菜市场买2块豆腐撞死算了>_<。也许你会惊讶,哇靠!linq能操作这些?答案是肯定的。那么我们来看看linq是怎么操作的。
using System.Data;using System.Linq;
var dt = new DataTable();dt.Columns.Add("A", typeof(int));var newRow1 = dt.NewRow();var newRow2 = dt.NewRow();newRow1["A"] = 1;newRow2["A"] = 2;dt.Rows.Add(newRow1);dt.Rows.Add(newRow2);// 重点看这里IEnumerable<DataRow> rows = dt.AsEnumerbale();foreach(row in rows) Console.WriteLine(row["A"].ToString());
从这段代码看,并没有什么实质意义,如果这样去遍历datarow就是脱裤子放屁,多此一举。但是这样做的目只有一个为下面的linq操作做铺垫。
Distinct,顾名思义该方法返回的没有重复值的datarow序列
var dt = new DataTable();dt.Columns.Add("A", typeof(int));var newRow1 = dt.NewRow();var newRow2 = dt.NewRow();newRow1["A"] = 1;newRow2["A"] = 2;newRow3["A"] = 2;dt.Rows.Add(newRow1);dt.Rows.Add(newRow2);dt.Rows.Add(newRow3);// 重点看这里IEnumerable<DataRow> rows = dt.AsEnumerbale();
// 去重复IEnumerable<DataRow> distinctRows = rows.Distinct(DataRowComparer.Default);foreach(var row in distictRows) Console.WriteLine(row["A"].ToString());// 结果// 1// 2
注意,这里的 DataRowComparer 是一个静态类,属性 Default 表示返回单一的DataRow实例
Except, 找到DataRow序列A中在datarow序列B中没有的datarow序列
var dt1 = new DataTable();dt1.Columns.Add("A", typeof(int));var dt2 = new DataTable();dt2.Columns.Add("A", typeof(int));dt1.Rows.Add(new object[] { 1 });dt1.Rows.Add(new object[] { 2 });dt2.Rows.Add(new object[] { 2 });dt2.Rows.Add(new object[] { 3 });// 重点看这里IEnumerable<DataRow> rows1 = dt1.AsEnumerable();IEnumerable<DataRow> rows2 = dt2.AsEnumerable();// 获取rows1中在rows2里没有包含的datarow序列var rows3 = rows1.Except(rows2, DataRowComparer.Default);foreach (var row in rows3) Console.WriteLine(row["A"].ToString());
// 结果
// 1
Intersect, 两个DataRow序列的交集
var dt1 = new DataTable();dt1.Columns.Add("A", typeof(int));var dt2 = new DataTable();dt2.Columns.Add("A", typeof(int));dt1.Rows.Add(new object[]{1});dt1.Rows.Add(new object[]{2});dt2.Rows.Add(new object[]{2});dt2.Rows.Add(new object[]{3});// 重点看这里IEnumerable<DataRow> rows1 = dt1.AsEnumerbale();IEnumerable<DataRow> rows2 = dt2.AsEnumerbale();// 获取rows1与rows2共有的datarow序列rows1.Intersect(row2, DataRowComparer.Default); foreach(var row in rows1) Console.WriteLine(row["A"].ToString());// 结果// 2
Union,合并两个datarow序列
var dt1 = new DataTable();dt1.Columns.Add("A", typeof(int));var dt2 = new DataTable();dt2.Columns.Add("A", typeof(int));dt1.Rows.Add(new object[] { 1 });dt1.Rows.Add(new object[] { 2 });dt2.Rows.Add(new object[] { 2 });dt2.Rows.Add(new object[] { 3 });// 重点看这里IEnumerable<DataRow> rows1 = dt1.AsEnumerable();IEnumerable<DataRow> rows2 = dt2.AsEnumerable();// 合并rows1与rows2var row3 = rows1.Union(rows2, DataRowComparer.Default);foreach (var row in row3) Console.WriteLine(row["A"].ToString());// 结果// 1// 2// 3
SequenceEqual,判断两个dataorw序列是否相等
var dt1 = new DataTable();dt1.Columns.Add("A", typeof(int));var dt2 = new DataTable();dt2.Columns.Add("A", typeof(int));dt1.Rows.Add(new object[]{1});dt1.Rows.Add(new object[]{2});dt2.Rows.Add(new object[]{2});dt2.Rows.Add(new object[]{3});// 重点看这里IEnumerable<DataRow> rows1 = dt1.AsEnumerbale();IEnumerable<DataRow> rows2 = dt2.AsEnumerbale();// 合并rows1与rows2var equal = rows1.SequenceEqual(row2, DataRowComparer.Default); Console.WriteLine(equal.ToString());// 结果// false
在了解了对datarow的操作后,我们再来了解一下对datacolumn的操作
Field<T> , 它是一个获取当前行(datarow)的某一列值的静态扩展方法,它具有三种重载参数,类型分别是DataColumn, String, Int,在这里建议大家使用string 类型参数,明确是取哪一列,语句阅读上更流畅。
var dt = new DataTable();dt.Columns.Add("A", typeof(int));var newRow1 = dt.NewRow();var newRow2 = dt.NewRow();newRow1["A"] = 1;newRow2["A"] = 2;dt.Rows.Add(newRow1);dt.Rows.Add(newRow2);IEnumerable<DataRow> rows = dt.AsEnumerbale();// 重点看这里foreach(var val in rows.Select(e => e.Field<int>("A")) Console.WriteLine(val.ToString());
SetField<T> , 该方法刚好是对当前行某一列进行赋值操作,同样也具有三种重载参数DataColumn, String, Int,在这里建议大家使用string 类型参数,明确是取哪一列,语句阅读上更流畅。
var dt = new DataTable();dt.Columns.Add("A", typeof(int));var newRow1 = dt.NewRow();var newRow2 = dt.NewRow();newRow1["A"] = 1;newRow2["A"] = 2;dt.Rows.Add(newRow1);dt.Rows.Add(newRow2);IEnumerable<DataRow> rows = dt.AsEnumerbale();// 重点看这里foreach(var row in rows) row.SetField<int>("A", row.Field<int>("A")+10);foreach(var val in rows.Select(e => e.Field<int>("a"))) Console.WriteLine(val.ToString())// 结果// 11// 12
// 已知一个DataTablevar dt = new DataTable();// 获取一个DataRow序列var rows = dt.AsEnumerable();//经过一系列操作// ....//获取一个新的DataTablevar newDt = rows.CopyToDataTable();
至此,我们对linq to DataSet 有了一个基本认识与了解,那么下面我们将了解另一个应用 linq to xml
在实际应用中,并不需要我们使用linq对xml对象进行操作,因为MS已经提供了封装对xml的linq操作的对象,我们一起来简单的了解下有哪些对象。
using System.Linq;using System.Xml.Linq;
XDocument : 表示 XML 文档
XElement : 表示一个 XML 元素
XAttribute : 表示一个 XML 特性(节点属性)
XNamespace : 表示一个 XML 命名空间
XCData : 表示一个包含 CDATA 的文本节点(注释)
XDeclaration : 表示一个 XML 声明
// 创建一个XML文档var xDoc = new XDocument( // 定义声明 new XDeclaration("1.0", "utf-8", "yes"), // 添加根节点 new XElement("root", // 添加子节点1,并添加节点属性“name” new XElement("item1", new XAttribute("name","属性"), "子节点1"), // 添加子节点2,并添加内容注释CDATA new XElement("item2", new XCData("注释"))));// 输出结果//<?xml version="1.0" encoding="utf-8" standalone="yes"?>//<root>// <item1 name="属性">子节点1</item1>// <item2><![CDATA[注释]]></item2>//</root>
// 创建一个XML文档var xDoc = new XDocument( // 定义声明 new XDeclaration("1.0", "utf-8", "yes"), // 添加根节点 new XElement("root", // 添加子节点1,并添加节点属性“name” new XElement("item1", new XAttribute("name","属性"), "子节点1"), // 添加子节点2,并添加内容注释CDATA new XElement("item2", new XCData("注释"))));
// 输出XML文档
xDoc.Save("demo.xml");
XDocment xDoc = XDocument.Load("demo.xml");
摘自MSDN:
XNode 是以下类型的抽象公共基类:
XComment
XContainer
XDocumentType
XProcessingInstruction
XText
XContainer 是以下类型的抽象公共基类:
XDocument
XElement
派生自 XContainer 的类的对象可以包含子节点。
DescendantNodes : 按文档顺序返回此文档或元素的子代节点集合。
Elements : 按文档顺序返回此元素或文档的子元素集合
var xDoc = XDocument.Load("demo.xml");IEnumerable<XNode> nodex = xDoc.DescendantNodes();IEnumerable<XElement> elems = xDoc.Elements();
当我们获取到到节点或者元素的序列后就可以对这些对象进行常规的LINQ操作,例如运用前两篇介绍的知识。
由于篇幅关系,这里就不逐一去讲解每个LINQ TO XML的API了,感兴趣的读者可以去msdn查阅System.Xml.Linq命名空间下的操作对象。
(吐槽)当我写完这篇文章时,自我感觉很枯燥,通篇介绍的都是API封装没有体现出LINQ的新意,想删掉这篇文章的冲动都有,但是一想既然我们要学习LINQ,这些东西还是需要适当了解与接触,所以还是硬着头皮写下来了,如果你能看完整篇文章,那真的非常感谢,感谢你的支持。
linq to xml 在效率上和 xml 的 xpath 差不了多少,所以在什么情况下怎么使用任君选择,并不需要强制使用的。
linq to dataset 小数据的时候可以这么干,但是数据量大时候,我建议不要这么干,首先要执行AsEnumberable这样一个耗时的方法划不来,不如直接上foreach遍历。
最终篇将和大家分享并讨论最受大家所熟知的LINQ TO SQL,还是希望大家给予一点期待吧。
感谢阅读,如果有说的不对的地方请指正,谢谢。
联系客服