LINQ to DataSet 中的查询
查询是一种从数据源检索数据的表达式。 查询通常用专用查询语言表示,如用于关系数据库的 SQL 和用于 XML 的 XQuery。 因此,开发人员对于他们查询的每种类型的数据源或数据格式,都不得不学习一种新的查询语言。 语言集成查询 (LINQ) 提供了一种较为简单的一致模型,适用于各种数据源和格式的数据。 在 LINQ 查询中,您始终使用编程对象。
一个 LINQ 查询操作包含三个操作:获取数据源、创建查询和执行查询。
实现 IEnumerable<T> 泛型接口的数据源可以通过 LINQ 进行查询。 对 DataTable 调用 AsEnumerable 将返回实现泛型 IEnumerable<T> 接口的对象,作为 LINQ to DataSet 查询的数据源。
在查询中,指定您要从数据源中检索的准确信息。 查询也可以指定返回信息之前信息的排序、分组和表现方式。 在 LINQ 中,查询存储在变量中。 如果查询旨在返回一系列值,则查询变量本身也必须为可枚举类型。 此查询变量不执行任何操作,也不返回任何数据;它只存储查询信息。 创建查询后必须执行该查询以检索任何数据。
在返回一系列值的查询中,查询变量本身从不保存查询结果,它只存储查询命令。 查询的执行将推迟到在 foreach 或 For Each 循环中循环访问查询变量之后进行。 这称为“延迟执行”;也就是说,查询将会在构造之后的某个时间执行。 这意味着您可以根据需要频繁地执行查询。 例如,当您的数据库由其他应用程序不断更新时,此功能将会很有用。 在您的应用程序中,您可以创建查询以检索最新信息并重复执行查询,每次返回更新的信息。
与返回一系列值的延迟查询相反,返回单一实例值的查询将立即执行。 Count、Max、Average 和 First 是一些单一实例查询的示例。 因为需要查询结果来计算单一实例结果,因此这些查询将会立即执行。 例如,若要计算查询结果的平均值,则必须执行查询,以便求平均值函数具有要使用的输入数据。 您也可以对查询使用 ToList<TSource> 或 ToTSource> 方法以强制立即执行不生成单一实例值的查询。 当想要缓存查询结果时,这些强制立即执行的技术可能会很有用。 有关延迟和立即执行查询的更多信息,请参见 Getting Started with LINQ。
查询
LINQ to DataSet 查询可以使用两种不同的语法进行表述:查询表达式语法和基于方法的查询语法。
查询表达式语法
查询表达式是一种声明性查询语法。 此语法使开发人员能够以类似于 SQL 的格式用 C# 或 Visual Basic 编写查询。 通过使用查询表达式语法,您可以用最少的代码对数据源执行复杂的筛选、排序和分组操作。 有关更多信息,请参见 LINQ 查询表达式(C# 编程指南)和基本查询操作 (Visual Basic)。
查询表达式语法是 C# 3.0 和 Visual Basic 2008 中的新功能。 不过,.NET Framework 公共语言运行库 (CLR) 无法读取查询表达式语法本身。 因此,在编译时,查询表达式将转换为 CLR 能理解的形式,即方法调用。 这些方法称为“标准查询运算符”。 作为开发人员,您可以选择使用方法语法而不使用查询语法直接调用这些方法。 有关更多信息,请参见LINQ 查询语法与方法语法 (C#)。 有关如何使用标准查询运算符的更多信息,请参见 LINQ 常规编程指南。
下面的示例使用 Select 返回 Product 表中的所有行并显示产品名称。
' Fill the DataSet.
Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)
Dim products As DataTable = ds.Tables("Product")
Dim query = From product In products.AsEnumerable() _
Select product
Console.WriteLine("Product Names:")
For Each p In query
Console.WriteLine(p.Field(Of String)("Name"))
Next
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
DataTable products = ds.Tables["Product"];
IEnumerable<DataRow> query =
from product in products.AsEnumerable()
select product;
Console.WriteLine("Product Names:");
foreach (DataRow p in query)
{
Console.WriteLine(p.Field<string>("Name"));
}
基于方法的查询语法
表述 LINQ to DataSet 查询的另一种方法是使用基于方法的查询。 基于方法的查询语法是对 LINQ 运算符方法的一系列直接方法调用,将 Lambda 表达式作为参数进行传递。 有关更多信息,请参见 Lambda 表达式(C# 编程指南)。
此示例使用 Select 返回 Product 中的所有行并显示产品名称。
' Fill the DataSet.
Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)
Dim products As DataTable = ds.Tables("Product")
Dim query = products.AsEnumerable() _
.Select(Function(product As DataRow) New With _
{ _
.ProductName = product.Field(Of String)("Name"), _
.ProductNumber = product.Field(Of String)("ProductNumber"), _
.Price = product.Field(Of Decimal)("ListPrice") _
})
Console.WriteLine("Product Info:")
For Each product In query
Console.Write("Product name: " & product.ProductName)
Console.Write("Product number: " & product.ProductNumber)
Console.WriteLine("List price: $ " & product.Price)
Next
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
DataTable products = ds.Tables["Product"];
var query = products.AsEnumerable().
Select(product => new
{
ProductName = product.Field<string>("Name"),
ProductNumber = product.Field<string>("ProductNumber"),
Price = product.Field<decimal>("ListPrice")
});
Console.WriteLine("Product Info:");
foreach (var productInfo in query)
{
Console.WriteLine("Product name: {0} Product number: {1} List price: ${2} ",
productInfo.ProductName, productInfo.ProductNumber, productInfo.Price);
}
编写查询
如本主题前面所述,当查询旨在返回一系列值时,查询变量本身只存储查询命令。 如果查询不包含可使查询立即执行的方法,则查询的实际执行将会推迟,直到在 foreach 或 For Each 循环中循环访问查询变量。 延迟执行可使多个查询组合在一起或使查询得到扩展。 扩展查询时,将修改查询以包括新操作,最终执行将反映这些更改。 在下面的示例中,第一个查询返回所有产品。 第二个查询通过使用 Where 扩展第一个查询,以返回大小为“L”的所有产品:
' Fill the DataSet.
Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)
Dim products As DataTable = ds.Tables("Product")
Dim productsQuery = From product In products.AsEnumerable() _
Select product
Dim largeProducts = _
productsQuery.Where(Function(p) p.Field(Of String)("Size") = "L")
Console.WriteLine("Products of size 'L':")
For Each product In largeProducts
Console.WriteLine(product.Field(Of String)("Name"))
Next
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
DataTable products = ds.Tables["Product"];
IEnumerable<DataRow> productsQuery =
from product in products.AsEnumerable()
select product;
IEnumerable<DataRow> largeProducts =
productsQuery.Where(p => p.Field<string>("Size") == "L");
Console.WriteLine("Products of size 'L':");
foreach (DataRow product in largeProducts)
{
Console.WriteLine(product.Field<string>("Name"));
}
执行一个查询后,不会再编写其他查询,并且所有后续查询都将使用驻留在内存中的 LINQ 运算符。 当在 foreach 或 For Each 语句中循环访问查询变量或通过调用可导致立即执行的 LINQ 转换运算符之一时,查询将会开始执行。 这些运算符包括:ToList<TSource>、ToTSource>、ToLookup 和 ToDictionary。
在下面的示例中,第一个查询返回按定价排序的所有产品。 ToTSource> 方法用于强制立即执行查询:
' Fill the DataSet.
Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)
Dim products As DataTable = ds.Tables("Product")
Dim query = _
From product In products.AsEnumerable() _
Order By product.Field(Of Decimal)("ListPrice") Descending _
Select product
' Force immediate execution of the query.
Dim productsArray = query.ToArray()
Console.WriteLine("Every price From highest to lowest:")
For Each prod In productsArray
Console.WriteLine(prod.Field(Of Decimal)("ListPrice"))
Next
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
DataTable products = ds.Tables["Product"];
IEnumerable<DataRow> query =
from product in products.AsEnumerable()
orderby product.Field<Decimal>("ListPrice") descending
select product;
// Force immediate execution of the query.
IEnumerable<DataRow> productsArray = query.ToArray();
Console.WriteLine("Every price from highest to lowest:");
foreach (DataRow prod in productsArray)
{
Console.WriteLine(prod.Field<Decimal>("ListPrice"));
}