DataTable.Select()方法使用和 SQL 相似的過濾語法從 DataTable 中提取你關心的記錄,雖然 Select()可以很好的工作,但它還是有一些明顯的限制。首先,它是基於字符串的,也就是說可能的錯誤不能在編譯的時候發現。其次,它的過濾功能也很有限,它沒有提供 LINQ 操作符能夠提供的其他特性,如排序、分組以及投影。
使用 LINQ to DataSet 時,本質上和查詢對象集合相同的語法。畢竟,DataSet 只是一組 DataTable 的集合,而 DataTable 是一組 DataRow(及其他一些架構信息)的集合。但是,對 DataSet 有一個明顯的限制:它沒有顯示強類型的數據。相反,要由你把字段值強制轉換為適當的類型。
要讓這些應用成為現實,你需要使用 Field<T> 擴展方法。它由 System.Data 命名空間中的 DataRowExtensions 類提供。本質上 Field<T> 擴展所有 DataRow 對象,並讓你能夠以強類型的方式訪問字段:
string value = dataRow.Field<string>("FirstName");
LINQ 針對實現了 IEnumerable<T> 的集合工作。無論是 DataRowCollection 還是 DataTable,都沒有實現這個接口,為了彌補這一缺陷,需要使用另一個擴展方法 AsEnumerable(),它為指定的 DataTable 公開 DataRow 對象里的 IEnumerable<T> 集合:
IEnumerable<DataRow> rows = dataTable.AsEnumerable();
下面這個示例把姓以字母 D 開頭的員工記錄提取為 DataRow 對象:
IEnumerable<DataRow> matches = from employee
in ds.Tables["Employees"].AsEnumerable()
where employee.Field<string>("LastName").StartsWith("D")
select employee;
這個集合並不適合用於數據綁定(它只會顯示 DataRow 對象的公共屬性而不是字段值的集合)。問題在於綁定 ADO.NET 數據時,必須包含架構(DataTable 能綁定因為它包含了帶有列標題以及其他信息的 Columns 集合)。
有兩個辦法來解決這個問題。
方法一:通過 DataTableExtensions.AsDataView()方法獲得過濾行的 DataView:
var matches = from employee in ds.Tables["Employees"].AsEnumerable()
where employee.Field<string>("LastName").StartsWith("D")
select employee;
gridEmployees.DataSource = matches.AsDataView();
gridEmployees.DataBind();
LINQ to DataSet 表達式返回 EnumerableRowCollection<T> 類的示例(它實現 IEnumerable 接口)。AsDataView()是一個只可以在 EnumerableRowCollection<T> 對象上工作的擴展方法。因此在前一個示例里,必須使用 var 關鍵字定義匹配的變量或者把它定義為 EnumerableRowCollection<DataRow>。如果把它聲明為 IEnumerable<DataRow>,將不能訪問 AsDataView()方法。
方法二:投影
var matches = from employee in ds.Tables["Employees"].AsEnumerable()
where employee.Field<string>("LastName").StartsWith("D")
select new
{
First=employee.Field<string>("FirstName"),
Last=employee.Field<string>("LastName")
};
gridEmployees.DataSource = matches;
gridEmployees.DataBind();
兩種方法完全等效。 DataView 的方法在非連接的富客戶端場景中非常有用,因為它能夠在繼續追蹤 DataSet 變化的情況下操作數據。投影的方法能夠把字段的數目減少為只需要關心的那幾個。
強類型的 DataSet
強類型的 DataSet 為解除 DataSet 的限制提供了另一個解決方案。因為是強類型,所以不必依賴於 Field<T> 以及 AsEnumerable()方法,這使得表達式具有更好的可讀性。
var matches = from employee in ds.Employees
where employee.LastName.StartsWith("D")
select new { First = employee.FirstName, Last = employee.LastName };
空值
對於能夠以強類型的方式訪問字段值,Field<T> 方法起了非常重要的作用。它還有另一個很有用的應用:它把空值(用 DBNull.Value 表示)轉換為一個真正的空引用。因此,可以檢查值是否為空引用而不是講其與 DBNull.Value 進行比較,這使 LINQ 表達式更為整潔。
var matches = from employee in ds.Tables["Employees"].AsEnumerable()
where employee.Field<string>("LastName") != null
select employee;
