LINQ to DataSet主要是提供對離線數據的支持,只有在填充DataSet之後, 我們才能使用LINQ to DataSet來查詢數據。其功能主要是通過 System.Data.DataRowExtions和System.Data.DataTableExtensions兩個靜態類 中的擴展方法來公開的。LINQ to DataSet是LINQ to ADO.Net中的一部分,但這 部分所占比重非常小,內容也比較少。下面就讓我們首先來看看 DataTableExtensions中的擴展方法:
public static EnumerableRowCollection<DataRow> AsEnumerable(this DataTable source)
------------------------------------------------------------
public static DataView AsDataView(this DataTable table)
public static DataView AsDataView<T>(this EnumerableRowCollection<T> source) where T : DataRow
-------------------------------------------------------------
public static DataTable CopyToDataTable<T>(this IEnumerable<T> source) where T : DataRow
public static void CopyToDataTable<T>(this IEnumerable<T> source,DataTable table,LoadOption options) where T : DataRow
public static void CopyToDataTable<T>(this IEnumerable<T> source,DataTable table,LoadOption options,FillErrorEventHandler errorHandler)
從定義中就可 以看出這三類主要是提供DataTable、DataView和IEnumerable三者之間的轉換。 大家都知道LINQ to Object查詢主要是對IEnumerable序列進行的操作,這樣就 使得DataTable、DataView和LINQ之間建立了一個轉換橋梁。
因此,在我 們需要將DataTable應用於LINQ to DataSet查詢是要先調用AsEnumerable完成 DataTable到LINQ的轉換。如果我們需要將LINQ to DataSet的查詢的結果進行數 據綁定時我們需要調用AsDataView的泛型版來完成LINQ到DataView的轉換。當然 我們也可以使用CopyToDataTable來進行LINQ到DataTable的轉換。
注意 :如果在我們完成了DataTable到LINQ(IEnumerable)的轉換之後(也就是調用 AsEnumerable擴展方法),需要進行兩個DataRow序列的集合操作如 Distinct,Union,Except,Intersect,SequenceEqual,這些操作都需要對數據源中 的元素進行相等比較,由於缺省情況下都是調用數據源中的元素的GetHashCode 和Equals操作來判斷的,對於DataRow而言就是判斷對象的引用是否相等,這樣 可能會導致我們不期望的結果(DataRow裡面的數據內容是相同的,但不是同一 個對象),所以我們要使用Distinct,Union,Except,Intersect,SequenceEqual 帶IEqualityComparer的重載版本,使用System.Data.DataRowComparer.Default 作為參數。這個比較器類是.Net3.5專門為LINQ to DataSet新增加的,用於比較 DataRow的值的,它是通過先比較DataColumn的數量,然後使用該列中類型的 Equals方法進行比較。
不帶LoadOptions參數的CopyToDataTable方法將 自動為每一行的每一個字段創建(更新)原始版本和當前版本,帶有 LoadOptions參數的CopyToDataTable重載版本可以讓你指定是創建(更新)原始 版本或是當前版本,或者兩者都填充。LoadOptions選項有下面三個選項值可以 選擇:
OverwriteChanges: 創建(更新)每一列的當前值和原始值
PreserveChanges: 創建(更新)每一列的原始值
Upset: 創建( 更新)每一列的當前值
接下來,讓我們來看看DataRowExtensions中的擴 展方法。在這個DataRowExtensions中的擴展方法主要是從數據行中獲得字段的 值(泛型的Field方法)和設置數據行中字段的值(泛型的SetField方法)。
public static T Field<T>(this DataRow row,DataColumn column)
public static T Field<T>(this DataRow row,int columnIndex)
public static T Field<T>(this DataRow row,string columnName)
public static T Field<T> (this DataRow row,DataColumn column,DataRowVersion version)
public static T Field<T>(this DataRow row,int columnIndex,DataRowVersion version)
public static T Field<T>(this DataRow row,string columnName,DataRowVersion version)
public static void SetField<T>(this DataRow row,DataColumn column,T value)
public static void SetField<T>(this DataRow row,int columnIndex, T value)
public static void SetField<T>(this DataRow row,string columnName,T value)
其中,row: 是我們要使用的數據行對象實例
column: 指定要返回(設 置)其值的列
columnIndex: 指定要返回(設置)其值的列的索引
columnName: 指定要返回(設置)其值的列名
value: 要設置的新 值,如果是null,將自動轉換為DBNull.Value
version: 要獲取的數據行 的版本
下面,我們就來看看一個使用LINQ to DataSet的實例,這個例子 主要描述了一下上面擴展方法的用法,同時給出了部分注意的事項:
public static class LINQToDataSet
{
public class Student
{
public int Id;
public string Name;
}
public static DataTable GetDataTable(Student[] students)
{
DataTable table = new DataTable();
table.Columns.Add ("Id", typeof(Int32));
table.Columns.Add ("Name", typeof(string));
foreach (Student student in students)
{
table.Rows.Add (student.Id, student.Name);
}
return (table);
}
public static void PrintStudentTable (DataTable dt)
{
PrintStudentTable (dt.AsEnumerable());
}
public static void PrintStudentTable(IEnumerable<DataRow> dt)
{
Console.WriteLine ("=============================================================== =");
try
{
foreach (DataRow dataRow in dt.AsEnumerable())
{
Console.WriteLine("Student Id = {0} :original {1}:current {2}",
dataRow.Field<int> ("Id"),
dataRow.Field<string>("Name", DataRowVersion.Original),
dataRow.Field<string> ("Name", DataRowVersion.Current));
}
}
catch (Exception e)
{
Console.WriteLine("Exception:" + e.Message);
}
Console.WriteLine ("=============================================================== =");
}
public static void Test()
{
Student[] students = { new Student { Id = 1, Name = "Lazy Bee" },
new Student { Id = 7, Name = "Flying Wind" },
new Student { Id = 13, Name = "PiPi Zhu" },
new Student { Id = 72, Name = "Chong Chong" }};
DataTable originalTable = GetDataTable(students);
//我們試圖訪問DataTable中數據行的 Orginal版本,由於此時還沒有建立原始版本,
//所以將導致異 常
Console.WriteLine("We try to get access to original version, so we will get the exception.:");
PrintStudentTable(originalTable);
//我們使用 CopyToDataTable來建立DataTable中數據行的Orginal版本
Console.WriteLine("We will use CopyToDataTable to build original version.");
DataTable newTable = originalTable.AsEnumerable().CopyToDataTable();
PrintStudentTable(newTable);
//使用SetField來更新---www.bianceng.cn
Console.WriteLine("After call SetField to change name.");
(from s in newTable.AsEnumerable()
where s.Field<string>("Name") == "PiPi Zhu"
select s).Single<DataRow>().SetField ("Name", "George Oscar Bluth");
PrintStudentTable(newTable);
//使用SetField來設置null
Console.WriteLine("After call SetField to change name to null.");
(from s in newTable.AsEnumerable()
where s.Field<int>("Id") == 13
select s).Single<DataRow>().SetField<string> ("Name", null);
PrintStudentTable(newTable);
//使用CopyToDataTable來合並,由於我們沒有指定表的主鍵,
//所以只會簡單的追加在目標數據表的最後
Console.WriteLine("After call CopyToDataTable.We will not get our expected result because we have not set primary key.");
//首先,我們調用AcceptChanges來建立Original版本,否則,避免顯示時 拋出異常
originalTable.AcceptChanges();
newTable.AsEnumerable().CopyToDataTable(originalTable, LoadOption.OverwriteChanges);
PrintStudentTable (originalTable);
//我們使用Distinct來去掉剛才重復的記錄, 由於此時我們沒有使用DatarowComparer.Default
//所以我們將 得不到我們想要的結果
Console.WriteLine("After call Distinct.We will not get our expected result because we have not used DatarowComparer.Default comparer.");
IEnumerable<DataRow> distinctTable=originalTable.AsEnumerable ().Distinct();
PrintStudentTable(distinctTable);
//我們使用Distinct來去掉剛才重復的記錄,使用 DatarowComparer.Default比較器
//所以我們將得到我們想要的 結果
Console.WriteLine("After call Distinct.this is what we want.");
distinctTable=originalTable.AsEnumerable().Distinct (DataRowComparer.Default);
PrintStudentTable (distinctTable);
//我們先設置主鍵,然後再使用 CopyToDataTable來合並
Console.WriteLine("After call CopyToDataTable.this is what we want.");
originalTable = GetDataTable(students);
originalTable.PrimaryKey = new DataColumn[] { originalTable.Columns ["Id"] };
newTable.AsEnumerable ().CopyToDataTable(originalTable, LoadOption.OverwriteChanges);
PrintStudentTable(originalTable);
}
}
例子中有比較詳盡的注釋,相信大家看應該沒有什麼問題。
這個例子 的輸出結果為:
We try to get access to original version, so we will get the exception.:
================================================================
Exception:There is no Original data to access.
================================================================
We will use CopyToDataTable to build original version.
================================================================
Student Id = 1 :original Lazy Bee:current Lazy Bee
Student Id = 7 :original Flying Wind:current Flying Wind
Student Id = 13 :original PiPi Zhu:current PiPi Zhu
Student Id = 72 :original Chong Chong:current Chong Chong
================================================================
After call SetField to change name.
================================================================
Student Id = 1 :original Lazy Bee:current Lazy Bee
Student Id = 7 :original Flying Wind:current Flying Wind
Student Id = 13 :original PiPi Zhu:current George Oscar Bluth
Student Id = 72 :original Chong Chong:current Chong Chong
================================================================
After call SetField to change name to null.
================================================================
Student Id = 1 :original Lazy Bee:current Lazy Bee
Student Id = 7 :original Flying Wind:current Flying Wind
Student Id = 13 :original PiPi Zhu:current
Student Id = 72 :original Chong Chong:current Chong Chong
================================================================
After call CopyToDataTable.We will not get our expected result because we have not set primary key.
================================================================
Student Id = 1 :original Lazy Bee:current Lazy Bee
Student Id = 7 :original Flying Wind:current Flying Wind
Student Id = 13 :original PiPi Zhu:current PiPi Zhu
Student Id = 72 :original Chong Chong:current Chong Chong
Student Id = 1 :original Lazy Bee:current Lazy Bee
Student Id = 7 :original Flying Wind:current Flying Wind
Student Id = 13 :original :current
Student Id = 72 :original Chong Chong:current Chong Chong
================================================================
After call Distinct.We will not get our expected result because we have not used DatarowComparer.Default comparer.
================================================================
Student Id = 1 :original Lazy Bee:current Lazy Bee
Student Id = 7 :original Flying Wind:current Flying Wind
Student Id = 13 :original PiPi Zhu:current PiPi Zhu
Student Id = 72 :original Chong Chong:current Chong Chong
Student Id = 1 :original Lazy Bee:current Lazy Bee
Student Id = 7 :original Flying Wind:current Flying Wind
Student Id = 13 :original :current
Student Id = 72 :original Chong Chong:current Chong Chong
================================================================
After call Distinct.this is what we want.
================================================================
Student Id = 1 :original Lazy Bee:current Lazy Bee
Student Id = 7 :original Flying Wind:current Flying Wind
Student Id = 13 :original PiPi Zhu:current PiPi Zhu
Student Id = 72 :original Chong Chong:current Chong Chong
Student Id = 13 :original :current
================================================================
After call CopyToDataTable.this is what we want.
================================================================
Student Id = 1 :original Lazy Bee:current Lazy Bee
Student Id = 7 :original Flying Wind:current Flying Wind
Student Id = 13 :original :current
Student Id = 72 :original Chong Chong:current Chong Chong
================================================================
總結,在使用LINQ to DataSet的時候需要注意以下幾個方面:
1、在對IEnumeralbe進行數據行的集合操作如Distinct, Except, Union, Intersect, SequenceEqual時,需要使用 System.Data.DatarowComparer.Default作為比較器作為輸入參數,以保證對 DataRow是進行值比較,而不是引用比較。當然,如果GroupBy或者其他操作的 key的類型是DataRow時,也需要使用此比較器,以使我們得到我們期望的行為。
2 、SetField可以將字段值設置為null,並且SetField方法將自動將其轉 換為DBNull.Value.
3 、Field可以完成從DBNull.Value到null的轉換。 也就是說,如果該字段的值是DBNull.Value 時,Field方法將自動將其轉為null 並返回。這個方法是強類型的,不需要象通過列名或者列索引返回字段值一樣將 Object類型進行造型成需要的類型(值類型進行拆箱操作),(如果字段的值是 DBNull.Value時進行造型還將導致拋出異常)Field擴展方法自動做了這些處理 ,省去了開發人員手動進行這些處理的麻煩。
4 、缺省情況下,數據行 的Original版本中是沒有值的,試圖訪問時將導致異常發生。當然,可以在訪問 之前使用DataRow.HasVersion來進行判斷,以防止異常的發生。也可以通過調用 DataRow.AcceptChanges方法來建立Original版本來避免異常的發生。不帶 LoadOptions參數的CopyToDataTable擴展方法也會為返回的DataTable自動建立 DataRow的Original和Current版本.
5 當使用帶LoadOptions輸入參數的 CopyToDataTable擴展方法時,必須為目標DataTable指定主鍵列,否則,該函數 只是將源DataTable追加到目標DataTable的最後面。可能達不到期望更新的結果 。