LINQ to Objects. Вид запроса. Вызов цепочек расширяющих методов. Языковые конструкции для кодирования запроса. Отложенное и неотложенное выполнение запроса.

LINQ to Objects – название для IEnumerable<T>API для стандартных операций запросов; применительно к различным объектам (списки, карты, массивы); стандартные операции запросов – это статические методы класса System.Linq.Enumerablе. Обеспечивает возможность запросов любых объектов С#, на­ходящихся в памяти, таких как массивы, списки и другие типы коллекций.

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

var query = from employee in employees

where employee.Salary > 100000

orderby employee.LastName, employee.FirstName

select new {LastName = employee.LastName, FirstName = employee .FirstName} ;

Для того чтобы использовать Linq To Object необходимо подключить System.Linq.

Каждое выражение запроса в Linq To Object начинает­ся с конструкции from, объявляющей то, что называется переменной диапазона. Конструкция from очень похожа на оператор foreach в том, что осуществляет итерацию по коллекции employees, и на каждом шаге сохраняет ка­ждый элемент коллекции в переменной (в нашем случае, employee). После конструкции from запрос состоит из последовательности инструкций, где можно использовать различные операции запроса для фильтрации данных, представленных переменной диапа­зона. В нашем примере это конструкции where и orderby. И, наконец, выражение закрывается операцией проекции. Конечный результат построения выражения запроса сосредоточен в том, что на­зывается переменной запроса, в нашем примере — query. Переменная запроса query имеет анонимный тип.

LINQ построен на использовании стандартных операций запросов, которые представляют собой методы, предназначенные для операций с последовательно­стями, подобными коллекциям, которые реализуют интерфейсы IEnumerable и IQueryable. Когда компилятор С# встречает выражение запроса, то преобразует в последовательность или цепочку запросов расширяю­щих методов, реализующих это поведение.

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

Самое большое преимущество такого подхода связано с расширяемостью LINQ. То есть мы можем определить собственный набор расширяющих методов, а компи­лятор сгенерирует их вызовы при компиляции выражения запроса LINQ.

С# 2008 представил небольшой набор новых ключевых слов для создания вы­ражений запросов LINQ: from, join, where, group, into, let, orderby и select.

Каждый запрос начинается с ключевого слова from. Оно определяет переменную диапазона, т.е. локальную переменную для каждого элемента коллекции. Потом in и указывается коллекция. Можно указывать 2 локальных переменных: from x, y in arr1. Коллекцию должен реализовывать интерфейс IEnumerable <T>.

Используются также конструкции:

Where <условие отбора> - указывается для нее предикат - любое логическое выражение.

Join – для присоединения другой таблицы

Orderby - для сортировки

Select - проектор (который генерирует конечный результат) <выражение для проектора>, оно транслируется в λ-выражение.

Group – группировка.

Let– вводит внутреннюю переменную, для того, чтобы избавится от повторений выражений в LINQ запросе.

Отложенные и не отложенные запросы.

Важно помнить, что хотя многие из стандартных операций запросов прототипированы на возврат IEnumerable<T>, и мы воспринимаем IEnumerable<T> как последовательность, на самом деле операции не возвращают последовательность в момент их вызова. Вместо этого операции возвращают объект, который при перечислении порождает (yield) очередной элемент последовательности. Во время перечисления возвращенного объекта запрос выполняется, и порожденный элемент помещается в выходную последовательность. Таким образом, выполнение запроса отложено. Отложенную операцию легко вычислить, поскольку она должна возвращать тип IEnumerable<T> или IOrderedEnumerable<T>(примеры отложенных операций: where и select).

Не отложенных операции (nondeferred) - их легко отличить по типу возврата, отличающемуся от IEnumerable<T> или IOrderedEnumerable<T>. Примеры не отложенных операций: ToArray<T> - создает массив типа Т из входной последовательности типа Т; ToList<T> - создает лист типа Т из входной последовательности типа Т. Запросы на группировку или сортировку всегда неотложенные. При их выполнении создается итератор, выполняется проход и дополнительные действия.




25. Конструкция from…select… Разделы конструкции. Условия. Проекция. Анонимные типы данных.

Данная конструкция называется проектором, потому что проектирует, или транслирует, данные внутри запроса в форму, удобную для применения. В запросе LINQ конструктор select используется для производства конечного результата запроса. Если в выражении запроса присутствуют фильтрующие конструкции, они должны предшествовать select. Фильтры состоят из ключево­го слова where, за которым следует выражение предиката. Конструкция where транслируется в вызов расширяющего метода Where как лямбда-выражение. Вызовы Enumerable .Where, которые используются, если вы выполняете запрос на типе IEnumerable, преобразуют лямбда-выражение в делегат. И наоборот, вызовы Queryable .Where, которые используются, если вы выполняете запрос к коллекции через интерфейс IQueryable, преобразуют лямбда-выражения в деревья выраже­ний. Компилятор преобразует конструкцию select в вызов распределяющего метода select. Тело конструкции select конвертируется в лямбда-выражение, которое передается в метод select, использующий его для производства каждого элемента результирующего набора. select new {Input =… Output =…}

Каждый запрос начинается со слова from. Конструкция from – это генератор, который также определяет переменную диапазона – локальную переменную, используемую для представления каждого элемента входной коллекции, по мере применения к нему выражения запроса. Конструкция from похожа на оператор foreach в стиле императивного программирования, а переменная диапазона идентична по своему предназначению переменной итерации в том же foreach. Запрос может содержать более одной конструкции from. Тогда будет более 1 переменной диапазона: Var….. = from … in…

from … in…

select new {…}

Семейства классов, не делающих больше ничего, кроме предоставления свойств, встречаются довольно часто.

Класс коллекции, способный хранить экземпляры этого класса, тогда мог бы служить представлением множества строк в этой таблице. Такое применение классов является вполне допустимым.

Анонимные типы предлагают способ для упрощения этой модели программирования. В частности, они позволяют не определять никаких простых типов для хранения данных, а использовать вместо этого компилятор С# для автоматического создания типов на базе данных, которые в них нужно хранить.

В качестве альтернативного варианта можно использовать для этого и анонимный тип,

var curry = new { MainIngredient = "Lamb", Style = "Dhansak", Spiciness = 5 };

Используется ключевое слово var. Объясняется это тем, что у анонимных типов нет идентификатора, который можно было бы использовать разработчику. На самом деле, на внутреннем уровне у них все-таки есть идентификатор, но для использования разработчиком в своем коде он не доступен. Во-вторых, никакое имя для типа после ключевого слова new не указывается. Именно благодаря этому компилятор и узнает, что требуется использовать анонимный тип. IDE-среда распознает определение анонимного типа и соответствующим образом обновляет данные IntelliSense.

26. Конструкция from…group… Разделы конструкции. Условия. Ключи группировки. Получение ключа в результате-выборке.

Конструкция from и переменные диапазона. Каждый запрос начинается со слова from. Конструкция from— это генера­тор, который также определяет переменную диапазона — локальную переменную, используемую для представления каждого элемента входной коллекции, по мере применения к нему выражения запроса. Конструкция from похожа на оператор foreach в стиле императивного программирования, а переменная диапазона иден­тична по своему предназначению переменной итерации в том же foreach. Выражение запроса может содержать более одной конструкции from. В таком случае имеется более одной переменной диапазона, что является аналогом вложенных операторов foreach. Пример:

var query = from x in Enumerable . Range(0, 10)

from у in Enumerable.Range(0, 10)

select new { X = x,Y = y,Product = x * у};

 

Конструкция group. Выражение запроса может иметь необязательную конструкцию group, кото­рая является мощным средством для разделения ввода запроса. Конструкция group— это проектор, поскольку проектирует данные на коллекцию интерфейсов IGrouping. Интерфейс IGrouping определен в пространстве имен System.Linq и наследует IEnumerable. Поэтому можно применять интерфейс IGrouping вез­де, где можно использовать интерфейс IEnumerable. IGrouping включает свойство по имени Key. которое является объектом, описывающим подмножество. Каждый результирующий набор формируется применением операции эквивалентности ме­жду Key и фрагментом входных данных либо данных, производных от входных. Пример:

// Разделение чисел на четные и нечетные.

var query = from х in numbers

group x by x % 2; // x % 2 – ключ группировки

В запросе отсутствует конструкция select. Конечным результатом запроса является последовательность из двух эк­земпляров IGrouping. Первый экземпляр— результирующая последовательность, содержащая четные числа, а вторая содержит нечетные числа. Внутренне компилятор транслирует каждую конструкцию group в вызов стандартной операции запроса GroupBy. Конструкция group может также разделять входную коллекцию, используя мно­жественные ключи, также известные, как составные ключи. Чтобы выполнить такую группировку, можно воспользоваться ано­нимным типом для представления множественных ключей в запросе.

Конструкция into и продолжение. Ключевое слово into подобно ключевому слову let в том, что оно определяет ло­кальный по отношению к контексту запроса идентификатор. Используя конструк­цию into, мы сообщаем запросу, что хотим присвоить результат операции group или join идентификатору, который может быть использован в запросе позднее. На языке запросов это называется продолжением (continuation), поскольку конструк­ция group — не финальный проектор запроса. Однако конструкция into работает как генератор, во многом подобно конструкциям from, и идентификатор, представ­ленный into, подобен переменной диапазона в конструкции from.

// Разделение чисел на четные и нечетные

var query = from х in numbers

group x by x % 2 into partition

where partition.Key = 0

select new {Key = partition.Key, Count = partition.Count(), Group = partition};

В этом запросе продолжение, т.е. часть запроса, следующая после конструкции into, фильтрует серии групп, где Key равно 0, используя конструкцию where.

27. Агрегированные операции с помощью LINQ to Objects. Метод IEnumerable<T>. SelectMany( ).

Термином "LINQ to Objects" называют использование запросов LINQ непосредственно с коллекциями IEnumerable или IEnumerable<T> без промежуточных поставщиков LINQ или API-интерфейсов, таких как LINQ to SQL или LINQ to XML. LINQ можно использовать для запроса любых перечислимых коллекций, таких как List<T>, Array или Dictionary<TKey, TValue>. Коллекция может быть определенной пользователем или возвращенной API .NET Framework.

Агрегированные операции относятся к не отложенным операциям в LINQ to Object. Они позволяют выполнять агрегатные операции над элементами входной последовательности.

Операция Count возвращает количество элементов во входной последовательности. Два прототипа: 1) public static int Count<T> (this IEnumerable<T> source);

2) public static int Count<T> ( this IEnumerable<T> source, Func<T, bool> predicate) ;

Операция LongCount возвращает количество элементов входной последовательно­сти как значение типа long. Два прототипа: 1) public static long LongCount<T>( this IEnumerable<T> source); 2) public static int LongCount<T>( this IEnumerable<T> source, Func<T, bool> predicate) ;

Операция Sum возвращает сумму числовых значений, содержащихся в элементах последовательности. Два прототипа: 1) public static NumericSum(this IEnumerable<Numeric>source); Тип Numeric должен быть одним из int, long, double или decimal, либо одним из их допускающих null эквивалентов: int?, long?, double? или decimal?. 2) public static Numeric Sum<T> ( this IEnumerable<T> source, Func<T, Numeric> selector);

Операция Min возвращает минимальное значение входной последовательности. Четыре прототипа: 1) public static Numeric Min ( this IEnumerable<Numeric> source); 2) public static Т Min<T> ( this IEnumerable<T> source); 3) public static Т Min<T>(this IEnumerable<T> source, Func<T,Numeric> selector);

4) public static S Min<T, S>(this IEnumerable<T> source, Func<T, S> selector);

Операция Max возвращает максимальное значение из входной последовательности. Четыре прототипа: 1) public static NumericМах( this IEnumerable<Wumeric> source); 2) public static T Max<T>(this IEnumerable<T> source); 3) public static Т Мах<Т>(this IEnumerable<T> source, Func<T,Numeric> selector);

4) public static S Мах<Т, S> ( this IEnumerable<T> source, Func<T, S> selector);

Операция Average возвращает среднее арифметическое числовых значений элемен­тов входной последовательности. Два прототипа: 1) public static Result Average ( this IEnumerable<Numeric> source); 2) public static Result Average<T>(this IEnumerable<T> source, Func<T, Numeric> selector);

Операция Aggregate выполняет специфицированную пользователем функцию на каждом элементе входной последовательности, передавая значение, возвращенное этой функцией для предыдущего элемента, и возвращая ее значение для последнего элемента. Два прототипа:

1) public static Т Aggregate<T>( this IEnumerable<T> source, Func<T, T, T> func) ; 2) public static U Aggregate<T, U> ( this IEnumerable<T> source, U seed, Func<U, T, U> func) ;

IEnumerable<T> - это интерфейс, реализуемый всеми обобщенными классами коллекций С#, как и массивами. Этот интерфейс представляет перечисление элементов коллекций.

Последовательность – логический термин для коллекций, реализующий интерфейс IEnumerable<T>.

Большинство стандартных операций запросов представляют собой расширяющие методы в статическом классе System.Linq.Enumerable и прототипированы IEnumerable<T> в качестве первого аргумента.

Методы стандартных операций запросов класса System.Linq.Enumerable, которые не являются расширяющими методами – это просто статические методы, которые должны быть вызваны на классе System.Linq.Enumerable.

Методы: 1) GetEnumerator - возвращает перечислитель, который осуществляет перебор элементов коллекции (унаследовано от IEnumerable); 2)GetEnumerator( ) - возвращает перечислитель, выполняющий итерацию в коллекции.

Операция SelectMany используется для создания выходной последовательности – проекции «один ко многим» из входной последовательности. В то время как операция Select возвращает один выходной элемент для каждого входного элемента, SelectMany вернет ноль или более выходных элементов для каждого входного.

Существует 2 прототипа этой операции:

1) public static IEnumerable<S> SelectMany<T, S>

{

this IEnumerable<T> source,

Func<T, IEnumerable<S>> selector

};

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

2) public static IEnumerable<S> SelectMany<T, S>

{

this IEnumerable<T> source,

Func<T, int, IEnumerable<S>> selector

};

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

28. LINQ to Objects. Расширяющие методы для преобразования типов, операций со множествами, преобразования в один элемент.

Операции множеств используются для выполнения операций с множествами на последовательностях. Операция Distinct удаляет дублированные элементы из входной последовательности. Прототип:

public static IEnumerable<T> Distinct<T>(this IEnumerable<T> source);

Операция Union возвращает последовательность объединения множеств из двух исходных последовательностей. Операция Intersect возвращает пересечение множеств двух последовательностей. Операция Except возвращает последовательность, содержащую все элементы первой последовательности, которых нет во второй последовательности. Операции преобразования представляют простой и удобный способ преобразования последовательностей в другие типы коллекций. Операция Cast используется для приведения каждого элемента входной последовательности в выходную последовательность указанного типа. Прототип:

public static IEnumerable<T> Cast<T> (this IEnumerable source);

Операция Cast пытается привести каждый элемент входной последовательности к указанному типу. Если любой из этих элементов не может быть приведён к указанному типу, будет сгенерировано исключение InvalidCastException. Если существует вероятность присутствия разнотипных элементов в исходной коллекции, нужно применять вместо Cast операцию OfType. Операция OfType используется для построения выходной последовательности, содержащей только те элементы, которые могут быть успешно преобразованы к специфицированному типу. Операция AsEnumerable просто заставляет входную последовательность типа IEnumerable<T> быть возвращённой, как тип IEnumerable<T>. Также используются операции ToArray, ToList, ToDictionary, ToLookup.

Операции элементов позволяют извлекать элементы из входной последовательности. Операция DefaultEmpty возвращает последовательность, содержащую элемент по умолчанию, если входная последовательность пуста.

Следующие операции элементов позволяют извлекать отдельные элементы из входной последовательности. Операция First возвращает первый элемент последовательности или первый элемент последовательности, соответствующий предикату – в зависимости от использованного прототипа. Операция FirstOrDefault подобна First во всём, кроме поведения, когда элемент не найден. Операция Last возвращает последний элемент последовательности или последний элемент, соответствующий предикату – в зависимости от того, какой предикат использован. Операция LastOrDefault подобна Last во всем, за исключением поведения в случае, когда элемент не найден. Операция Single возвращает единственный элемент последовательности или единственный элемент последовательности, соответствующий предикату – в зависимости от используемого прототипа. Операция SingleOrDefault подобна Single, но отличается поведением в случае, когда элемент не найден. Операция ElementAt возвращает элемент из исходной последовательности по указанному индексу. Операция ElementAtOrDefault возвращает элемент из исходной последовательности, имеющий указанный индекс местоположения.
29. Технология LINQ to SQL. Назначение технологии. Класс контекста данных и основные атрибуты для создания контекста данных.

«LINQ to SQL» это новая технология семейства технологий ADO.NET. Она работает только с Microsoft SQL Server. Основная цель LINQ to SQL является обеспечение согласованности между реляционными БД и программной логикой взаимодействия с ними. LINQ to SQL позволяет встроить доступ к данным в код программы. При программировании с помощью LINQ to SQL скрываются множество типов ADO.NET, таких как SqlConnection, SqlCommand, or SqlDataAdapter. Вместо того чтобы обрабатывать реляционную БД в виде потока записей, можно рассматривать их в виде коллекций объектов определенного класса – класса сущностей. Для работы с технологией LINQ to SQL требуется добавить к проекту ссылку (Reference) на компонент: System.Data.Linq.dll. Задать в программе используемые пространства имен: using System.Data.Linq; using System.Data.Linq.Mapping;

Классы сущностей (entity classes) это классы программы, которые представляют данные содержащиеся в реляционной БД, с которой выполняется работа. С программной точки зрения классы сущностей это описания классов, которые аннотированы с помощью специальных атрибутов технологии «LINQ to SQL» (таких, как [Table] и [Column]), которые связывают их с физическими таблицами в БД. Например, класс сущностей для таблицы Customers:

[Table(Name = "Customers")]

public class Customer {

public string CustomerID;

public string City; }

Атрибут Table. С ним можно задавать следующие параметры: 1) Name – имя таблицы, которой соответствует, описываемый класс сущностей. Например: [Table (Name = xxxxx)]. Атрибут Column. С ним можно задавать следующие параметры: 1) Name – имя соответствующей колонки в таблице; 2) DbType – тип поля записи; 3) CanBeNull – может ли быть значение null у поля записи. 4) IsPrimaryKey – указание, что поле является первичным ключом. Например: [Column (IsPrimaryKey=true)]. После того, как описан класс сущностей запросы к СУБД передаются с помощью класса DataContext. Данный класс отвечает за трансляцию LINQ запросов в соответствующие SQL запросы и передачу их конкретной БД. В некотором смысле DataContext похож на объект Connection, так как он также требует строку соединения. Однако, в отличии от класса Connection, класс DataContext имеет методы, которые связывают результаты выполнения запроса (выборку записей) с описанными классами сущностей.

Класс DataContext описывает способ получения экземпляров класса сущностей, которые могут использоваться в программе. После получения экземпляров сущностей можно менять их состояние любым желательным способом (добавлять, изменять и т.п.) и предоставлять измененный объект назад для последующей обработки. В этом смысле класс DataContext похож на класс DataAdapter.

Конструктор: DataContext ctx = new DataContext(<строка соединения>). Методы: 1) проверки соединения с базой данных DatabaseExists() – если true, то соединение выполнено успешно. 2) получение таблицу Table<имя таблицы> GetTable<имя таблицы>(). Например: Table<Inventory> invTable = ctx.GetTable<Inventory>(); 3) Метод сохранения изменений SubmitChanges(): ctx.SubmitChanges(); Класс Table<> Описывает таблицу указанного типа в базе данных. Типизированный класс, для которого задается используемый им тип данных. Table <Customers> tbc; Хранит объекты классов сущностей, того класса, который указан в угловых скобках. Предоставляет методы для работы LINQ запросов. Свойство IsReadOnly возвращает true если таблица описана только для чтения из БД. Для получения ссылки на объект класса Table<> используется метод GetTable<> класса DataContext: DataContext cnt = new DtaContext(strconn); Table<MyTable> tbl = cnt.GetTable<MyTable>; Добавления новой записи в таблицу InsertOnSubmit(): tbl.InsertOnSubmit(object); Удаление записи из таблицы DeleteOnSubmit(): tbl.DeleteOnSubmit(object);
30. Создание и подключение класса контекста данных к базе данных. Шаблон программирования при добавлении данных в объект контекста данных.

В технологии LINQ to SQL ключевым классом для объекта реляционного связывания является класс DataContext (класс контекста данных). DataContext – класс, соединяющий с БД, отслеживающий то, что мы изменяем, и обновляющий БД при вызове метода SubmitChanges. Сохраненные процедуры можно добавлять в реляционный конструктор объектов и выполнять как обычные методы DataContext. Сохраненные процедуры могут также использоваться для отмены поведения по умолчанию среды выполнения LINQ to SQL, которая выполняет операции Вставки, Обновления, Удаления, когда изменения сохраняются из классов сущностей в базу данных (например, при вызове метода SubmitChanges). По умолчанию, логика обновления базы данных (Вставки, Обновления и Удаления) с изменениями данных классов сущностей LINQ to SQL обеспечивается средой выполнения LINQ to SQL. Среда выполнения по умолчанию создает команды Вставить, Обновить и Удалить, основываясь на схеме таблицы (определения столбцов и информация о первичных ключах). Создание и конфигурирование DataContext. После добавления элемента LINQ to SQL Classes в проект и открытия Сред. Объектно-реляционный конструктор, пустая область конструктора представляет пустой DataContext, готовый к конфигурированию. DataContext конфигурируется с информацией о подключении, предоставленной первым элементом, который был перетащен в область конструктора. Поэтому DataContext конфигурируется с использованием информации о подключении из первого перемещенного в область конструктора элемента.

Вставки. Первый шаг при вставке записи в БД – создание DataContext. Второй – создание экземпляра сущностного объекта из сущностного класса. Третий – вставка этого сущностного объекта в соответствующую коллекцию таблицы. Четвертый – вызов метода SubmitChanges на объекте DataContext.

Пример вставки записи в БД посредством вставки сущностного объекта в Table<T>:

1. Создание DataContext (Northwind db = new Northwind;) 2. Создание экземпляра сущностного объекта (Customer cust = new Customer{CustomerID = “Иванов”, CompanyName = “Иванов Wranglers”, ContactName = “Иван Иванович”, City = “Гродно”, Country = “Беларусь”,};) 3. Добавление сущностного объекта в таблицу Customers (db.Customers.InsertOnSubmit (cust);) 4. Вызов метода SubmitChanges (db.SubmitChanges( );) 5. Опрос записи (Customer customer = db.Customers.Where (c=> c.CustomerID == “Иванов”).First( ); Console.WriteLine (“{0} – {1}”, customer.CompanyName, customer. ContactName);) Эта часть кода просто восстанавливает БД, чтобы можно было запустить пример снова (Console.WriteLine(“Удалить добавленного заказчика Иванов ”); db.Customers.DeleteOnSubmit (cust); db.SubmitChanges( );)

Описание: во-первых, создаем экземпляр объекта Northwind, чтобы иметь объект DataContext на БД Northwind. Во-вторых, создаем экземпляр объекта Customer и наполняем его. В-третьих, вставляем созданный экземпляр Customer в таблицу Customers типа Table< Customer>, в класс Northwind DataContext . В-четвертых, вызываем метод SubmitChanges для сохранения вновь созданного объекта Customer в БД. В-пятых, запрашиваем обратно из БД только что вставленную запись о заказчике, чтобы доказать, что она на самом деле была вставлена.

Наши рекомендации