前 言
终于来到下篇了,通过上篇,和中篇,我们了解了linq的基本语句,对应linq我们又了解到lambda表达式,静态扩展方法,以及linq的延迟加载的特性,那么在本篇文章中我们将分享学习一下linq对于我们开发中常用到的对象的操作应用。如果没有阅读过上篇的请点击这里,如果没有阅读中篇的请点击这里
linq to DataSet
对于做.net 开发的有谁不知道DataSet,DataTable,DataRow,DataColumn这些对象,如果你真的不知道,那好吧建议你到菜市场买2块豆腐撞死算了>_<。也许你会惊讶,哇靠!linq能操作这些?答案是肯定的。那么我们来看看linq是怎么操作的。
1. 命名空间,如果需要linq操作DataSet,需要以下命名空间
using System.Data; using System.Linq;
2. 关键方法 AsEnumerable,该方法为一个静态扩展方法,他将DataTable转换为一个IEnumerable<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; dt.Rows.Add(newRow1); dt.Rows.Add(newRow2); // 重点看这里 IEnumerable<DataRow> rows = dt.AsEnumerbale(); foreach(row in rows) Console.WriteLine(row["A"].ToString());
从这段代码看,并没有什么实质意义,如果这样去遍历datarow就是脱裤子放屁,多此一举。但是这样做的目只有一个为下面的linq操作做铺垫。
3. linq 对 DataRow 操作,以下举例一些linq特有的常用datarow操作
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与rows2 var 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与rows2 var equal = rows1.SequenceEqual(row2, DataRowComparer.Default); Console.WriteLine(equal.ToString()); // 结果 // false
4. linq 对 DataColumn 操作
在了解了对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
5. CopyToDataTable<DataRow>,该方法是将datarow序列组成一个新的DataTable
// 已知一个DataTable var dt = new DataTable(); // 获取一个DataRow序列 var rows = dt.AsEnumerable(); //经过一系列操作 // .... //获取一个新的DataTable var newDt = rows.CopyToDataTable();
至此,我们对linq to DataSet 有了一个基本认识与了解,那么下面我们将了解另一个应用 linq to xml
linq to XML
在实际应用中,并不需要我们使用linq对xml对象进行操作,因为MS已经提供了封装对xml的linq操作的对象,我们一起来简单的了解下有哪些对象。
1.命名空间,linq to xml 需要如下命名空间
using System.Linq; using System.Xml.Linq;
2. linq to xml 主要类型对象
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>
2. 输出XML文档,当我们创建好一个xml文档对象时,调用该对象的方法 Save 即可,如下:
// 创建一个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");
3. 导入xml文档,如果已知一个XML文本文件,我们需要获取这个xml文本文件XDocment对象时,可以执行改对象方法 Load,该方法具有八种参数重载,参数类型分别是Stream,String,TextReader,XmlReader。下面的示例中使用的是 string 类型参数传递
XDocment xDoc = XDocument.Load("demo.xml");
4. XNode 抽象基类,表示 XML 树中节点的抽象概念(元素、注释、文档类型、处理指令或文本节点),简单理解就是我们可以把XML的内容每一个部分都视为节点,也就是说它是类型的基类,并提供了大量的操作xml方法。
摘自MSDN:
XNode 是以下类型的抽象公共基类:
XComment
XContainer
XDocumentType
XProcessingInstruction
XText
XContainer 是以下类型的抽象公共基类:
XDocument
XElement
派生自 XContainer 的类的对象可以包含子节点。
5. 常用遍历方法
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,还是希望大家给予一点期待吧。
感谢阅读,如果有说的不对的地方请指正,谢谢。