1. 程式人生 > 程式設計 >詳解LINQ入門(下篇)

詳解LINQ入門(下篇)

前 言

終於來到下篇了,通過上篇,和中篇,我們瞭解了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));

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));

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));

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,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",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,還是希望大家給予一點期待吧。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。