Запросы в LINQ to DataSet

Запрос представляет собой выражение, получающее данные из источника данных. Запросы обычно выражаются на специализированном языке запросов, например SQL для реляционных баз данных и XQuery для XML. Поэтому разработчикам приходится учить новый язык запросов для каждого типа источника данных и формата данных, для которых выполняется запрос. LINQ (Language-Integrated Query) реализует более простую и согласованную модель работы с данными для различных типов источников данных и различных форматов данных. В запросе LINQ работа всегда происходит с программными объектами.

Операция запроса LINQ состоит из трех действий: получение одного или нескольких источников данных, создание запроса и выполнение запроса.

К источникам данных, которые реализуют универсальный интерфейс IEnumerable<T>, могут быть выполнены запросы с помощью LINQ. Вызов метода AsEnumerable для DataTable возвращает объект, который реализует универсальный интерфейс IEnumerable<T>, который в свою очередь служит источником данных для запросов LINQ to DataSet.

В запросе указываются данные, которые необходимо получить из источника данных. В запросе можно также указать, как должна сортироваться, группироваться и оформляться возвращаемая информация. В LINQ запрос хранится в переменной. Если запрос должен возвращать последовательность значений, переменная запроса должна иметь перечислимый тип данных. Эта переменная не выполняет никаких действий и не возвращает данные. Она только хранит информацию о запросе. После создания запроса его необходимо выполнить, чтобы получить данные.

В запросе, возвращающем последовательность значений, переменная запроса никогда не содержит результаты запроса, а только хранит его команды. Выполнение запроса откладывается, пока переменная запроса используется в циклах foreach или For Each. Это называется отложенным выполнением, то есть выполнение запроса происходит спустя некоторое время после его создания. Это означает, что запрос можно выполнять настолько часто, насколько это необходимо. Такое свойство полезно, например, если имеется база данных, которая обновляется другими приложениями. В собственном приложении можно создать запрос, который регулярно выполняется, каждый раз получая последние обновленные данные.

В отличие от отложенных запросов, возвращающих последовательности значений, запросы, получающие одноэлементное значение, выполняются немедленно. Примерами одноэлементных запросов являются Count, Max, Average и First. Они выполняются немедленно, так как результаты запросов необходимы для вычисления одноэлементного результата. Например, чтобы вычислить среднее из результатов запросов, запросы должны быть выполнены, только тогда усредняющая функция получит данные для обработки. Для принудительного немедленного выполнения запроса, не создающего одноэлементное значение, можно также использовать метод ToList<TSource> или ToTSource>. Такая методика принудительного немедленного выполнения может оказаться полезной при кэшировании результатов запроса. Дополнительные сведения об отложенном и немедленном выполнении запросов см. в разделе Getting Started with LINQ.

Запросы

При подготовке запросов LINQ to DataSet можно использовать два разных синтаксиса: синтаксис выражений запросов и синтаксис запросов на основе методов.

Синтаксис выражений запросов

Выражения запроса используют декларативный синтаксис запроса. Этот синтаксис позволяет разработчику писать запросы на C# или Visual Basic в формате, похожем на SQL. С помощью синтаксиса выражения запроса можно выполнять даже сложную фильтрацию, упорядочение и группирование операций в источнике данных с помощью минимального программного кода. Дополнительные сведения см. в разделах Выражения запросов LINQ (Руководство по программированию на C#) и Основные операции запроса (Visual Basic).

Синтаксис выражения запроса появился в языках C# 3.0 и Visual Basic 2008. Тем не менее среда CLR платформы .NET Framework не может прочитать выражение запроса сама. Таким образом, во время компиляции выражения запроса преобразуются в то, что понятно 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 с передачей им лямбда-выражений в качестве параметров. Дополнительные сведения см. в разделе Лямбда-выражения (Руководство по программированию в 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"));
}

См. также

Основные понятия

Запросы к объектам DataSet (LINQ to DataSet)

Другие ресурсы

Руководство по программированию (LINQ to DataSet)

Приступая к работе с LINQ в C#

Приступая к работе с LINQ в Visual Basic