LINQ 查詢簡介 (C#)

「查詢」是指從資料來源中擷取資料的運算式。 不同的資料來源有不同的原生查詢語言,例如關聯式資料庫 SQL 和 XML 的 XQuery。 開發人員必須針對所需支援的每種資料來源類型或資料格式,學習新的查詢語言。 LINQ 為資料來源和格式類型提供一致的 C# 語言模型,以簡化這種情況。 在 LINQ 查詢中,您一律會使用 C# 物件。 您會使用相同的基本編碼模式,來查詢及轉換 XML 文件、SQL 資料庫、.NET 集合,以及當 LINQ 提供者可用時任何其他格式中的資料。

查詢作業的三個部分

所有的 LINQ 查詢作業都包含三個不同的動作:

  1. 取得資料來源。
  2. 建立查詢。
  3. 執行查詢。

下列範例示範查詢作業的三個部分在原始程式碼中的表示方式。 為了方便起見,此範例使用整數陣列作為資料來源;不過,相同的概念也適用於其他資料來源。 本文章的其他部分都會參考此範例。

// The Three Parts of a LINQ Query:
// 1. Data source.
int[] numbers = [ 0, 1, 2, 3, 4, 5, 6 ];

// 2. Query creation.
// numQuery is an IEnumerable<int>
var numQuery =
    from num in numbers
    where (num % 2) == 0
    select num;

// 3. Query execution.
foreach (int num in numQuery)
{
    Console.Write("{0,1} ", num);
}

下圖顯示完整的查詢作業。 在 LINQ 中,查詢的執行與查詢本身不同。 換句話說,您不會藉由建立查詢變數來擷取任何資料。

Diagram of the complete LINQ query operation.

資料來源

上述範例中的資料來源為陣列,可支援泛型 IEnumerable<T> 介面。 這表示可使用 LINQ 進行查詢。 查詢會在 foreach 陳述式中執行,而 foreach 則需要IEnumerableIEnumerable<T>。 支援 IEnumerable<T> 或衍生介面的類型,例如泛型 IQueryable<T> 稱為可查詢的類型

可查詢型別無須進行修改或特殊處理,即可作為 LINQ 資料來源。 若來源資料還不是記憶體中的可查詢型別,LINQ 提供者則須將其表示為可查詢型別。 例如,LINQ to XML 會將 XML 文件載入可查詢的 XElement 類型:

// Create a data source from an XML document.
// using System.Xml.Linq;
XElement contacts = XElement.Load(@"c:\myContactList.xml");

使用 EntityFramework 時,您會在 C# 類別與資料庫結構描述之間建立物件關聯式對應。 您可針對物件撰寫查詢,EntityFramework 會在執行階段處理與資料庫之間的通訊。 在下列範例中,Customers 代表資料庫中特定的資料表,而查詢結果 IQueryable<T> 的類型則衍生自 IEnumerable<T>

Northwnd db = new Northwnd(@"c:\northwnd.mdf");

// Query for customers in London.
IQueryable<Customer> custQuery =
    from cust in db.Customers
    where cust.City == "London"
    select cust;

如需如何建立特定資料來源類型的詳細資訊,請參閱各類 LINQ 提供者的文件。 但基本的規則很清楚:任何支援泛型 IEnumerable<T> 介面或所繼承介面的物件,皆可為 LINQ 資料來源,通常是 IQueryable<T>

注意

這些類型 (例如 ArrayList) 支援非泛型 IEnumerable 介面,也可作為 LINQ 資料來源。 如需詳細資訊,請參閱如何使用 LINQ 查詢 ArrayList (C#)

查詢

查詢可指定要從一或多個資料來源擷取的資訊。 查詢也可選擇性地指定該項資訊傳回之前應該如何排序、分組和成形。 查詢是儲存在查詢變數中,並以查詢運算式初始化。 您可以使用 C# 查詢語法來撰寫查詢。

上述範例中的查詢會傳回整數陣列中的所有偶數。 此查詢運算式包含三個子句︰fromwhereselect (如果您熟悉 SQL,您注意到這些子句的排序與 SQL 中的排序相反。)from 子句會指定資料來源,where 子句會套用篩選,而 select 子句會指定傳回項目的類型。 本節會詳細討論所有查詢子句。 但目前的重點是,LINQ 中的查詢變數本身不會採取任何動作,且不會傳回任何資料。 它只會儲存稍後執行查詢以產生結果時所需要的資訊。 如需如何建構查詢的詳細資訊,請參閱標準查詢運算子概觀 (C#) (英文)。

注意

查詢也可以使用方法語法來表示。 如需詳細資訊,請參閱 LINQ 中的查詢語法及方法語法

查詢執行

延後執行

查詢變數本身只會儲存查詢命令。 查詢的實際執行必須等到您逐一查看 foreach 陳述式中的查詢變數之後才會進行。 此概念稱為「延後執行」,如下列範例所示:

foreach (int num in numQuery)
{
    Console.Write("{0,1} ", num);
}

foreach 陳述式也是擷取查詢結果的位置。 例如,在上述查詢中,反覆運算變數 num 會保留傳回序列中的每個值 (一次一個)。

因為查詢變數本身絕不會保留查詢結果,所以您可以重複執行它以擷取更新的資料。 例如,您可以擁有會由個別應用程式持續更新的資料庫。 在您的應用程式中,您可以建立一個擷取最新資料的查詢,並且每隔一段時間執行一次,以擷取更新的結果。

強制立即執行

針對某個來源項目範圍執行彙總函式的查詢必須先逐一查看這些項目。 這類查詢的範例包括 CountMaxAverageFirst。 這些方法執行時並未使用明確的 foreach 陳述式,因為查詢本身必須使用 foreach 才能傳回結果。 這些查詢傳回的是單一的值,而不是 IEnumerable 集合。 下列查詢會傳回來源陣列中的偶數計數:

var evenNumQuery =
    from num in numbers
    where (num % 2) == 0
    select num;

int evenNumCount = evenNumQuery.Count();

若要立即強制執行任何查詢,並快取其結果,可以呼叫 ToListToArray方法。

List<int> numQuery2 =
    (from num in numbers
        where (num % 2) == 0
        select num).ToList();

// or like this:
// numQuery3 is still an int[]

var numQuery3 =
    (from num in numbers
        where (num % 2) == 0
        select num).ToArray();

您也可以將 foreach 迴圈放在緊接著查詢運算式後方的位置,以強制執行查詢。 不過,藉由呼叫 ToListToArray,您也可以快取單一集合物件中的所有資料。

另請參閱