Отправляет email-рассылки с помощью сервиса Sendsay

.NET: Записки программиста

  Все выпуски  

На дальнем рубеже ...


Информационный Канал Subscribe.Ru



.NET: Записки программиста или хлопок одной ладони

 

Выпуск одинадцатый: На дальнем рубеже ...


Доброй ночи!

Один вопрос, преред тем как начать этот выпуск ...
Как оказалось в прошлый раз, минимум трое моих друзей не получили последний, 10-й выпуск этой рассылки. Причину я так и не понял, к примеру мне письмо пришло, а вот девушке, сидящей за стенкой и прячущейся за тем же самым Firewall - нет. Так что, если вы вдруг не читали предыдущий выпуск "Быстрее, проще, надежней ..." - он ждет вас в архиве рассылки.

Ну а теперь - о деле. Как я и обещал, сегодняшний выпуск будет чисто практическим - "подключил, настроил - работает". А разговор у нас пойдет о работе с базой данных, или, если быть конкретнее, как при помощи Microsoft Data Access application block значительно упростить себе жизнь при получении и сохранении даных.

Прим:
Application blocks в целом, а так же библиотеку Microsoft Enterprise Library я описал в предыдущем выпуске рассылки "Быстрее, проще, надежней ...".

Почему выпуск называется "На дальнем рубеже ..."? Мысленно представим себе схему многоуровневого (n-tier) приложения. Представили? Вот прямо перед нами нагромождение стендов, прилавков, все увешано рекламой, постоянно слышны сообщения "Уважаемые посетители, если вы пройдете на третий этаж, то в отделе парфюмерии ..." - это Presentation Layer. Выходим оттуда и попадаем на улицу, переполненную деловито снующими людьми, передающим друг другу дипломаты с пачками денег - Business Object Layer. О! видели? Вот какой-то мужчина просто исчез в момент, когда он уже почти передал битком набитый дипломат - не иначе как транзакция откатилась. Осторожно идем дальше и попадаем на территорию порта. Он кипит жизнью, торопливо снуют туда-сюда доверху загруженные грузовики, огромные трюмы кораблей извергают сотни тонн груза, чтобы тут же заполнить освободившееся место новыми контейнерами - это Data Access Component Layer. А если пройти по волнорезу вон туда, к самому краю, то можно увидеть самый краешек загадочных глубин океана - сервисов, служб и компонент Операционной Системы. А где-то, далеко на горизонте, мрачными скалами вздымается защита хранилища данных - Database Layer.

А, что? Да-да, мы же о программировании ... Вобщем, я всего лишь хотел сказать, что тот волнорез, на котором мы стоим и который защищает нас от океана, обычно называют Data Access Helper Layer - уровень, находящийся между DACL и Database и упрощающий их взаимодействие. Самый дальняя граница нашего мира, который проектировали лично мы, рубеж, за которым начинается Система.

Прим:
Конечно эта анология скорее красивая, чем верная :) Зануды вспомнят, что Database так же может содержать целый уровень, который спроектирован нами - логику, реализованную в хранимых процедурах. Мало того, с приходом MS SQL 2005 у нас появилась возможность использовать в базе код, написанный уже на C#. Но! Где это видано, чтобы вы смотрели в "открытый океан" и видели "такой же порт, шумный и суетливый"? Так что прийдется мириться с законами жанра - за нашим рубежом будут идти "загадочные и мрачные глубины ядра системы, написанного о боже! на неуправляемом (unmanaged) коде", а вовсе не наглядная и дружественная .NET Framework :)

Итак, что нам дает Microsoft Data Access application block и стоит ли тратить время на его инсталляцию и изучение описания?

  • Обращение к базе данных (вызов хранимой процедуры, прямое выполнение SQL запроса, сохранение DataSet) выполняется в две строчки (а если потратить чуть больше времени, то и в одну). Это мы разберем подробнее, когда дойдем до примера.

  • Повышается уровень абстракции - как правило, вы будете работать только с контейнерами данных, такими как DataReader или DataSet. Объекты Connection, DataAdapter, Command надежно инкапсулированны внутри Data Access application block, который сам занимается управлением пулом Connections, преобразованием типов данных, созданием и кешированием параметров - вобщем всеми остальными задачами, которые необходимо выполнять для эффективной работы с базой. Вам остается только сказать, в каком виде вы хотите получить результат - как скалярный объект (число, строка, логическое значение), как одну строку данных (DataRow, реально используется DataReader, оптимизированный под возвращение одной строки), DataReader или DataSet.

  • Data Access application блок включает в себя два провайдера для работы c базами данных: MS SQL и Oracle. Они скрывают в себе весь код, специфичный для конкретного сервера базы данных, что упрощает работу по переходу от одного типа базы к другому. Причем тип провайдера, строка соединения и уникальные для конкретной базы параметры храняться в конфигурационном файле, что позволяет переключаться с базы на базу вобще без перекомпиляции кода.
Прим:
Конечно, это только в сказках достаточно одного взмаха волшебной палочки (читай, запуска Enterprise Library Configuration) чтобы приложение, работавшее с MS SQL заработало под Oracle. Но! Если весь SQL был заключен в хранимых поцедурах - действительно вполне реальна ситуация, когда переход на другой тип базы не приведет к переписыванию вашего кода (саму базу естественно прийдется переделывать, разве что вы обходились скромными командами basic SQL спецификации года эдак 1970 :).

Я перечислил основные моменты, полезность которых чуствуется сразу. В документации к Enterprise Library вы найдете подробное изложение 1001 причины, почему ей действительно стоит пользоваться, но и этого пожалуй достаточно, чтобы продолжить читать дальше, не так ли?

Теперь посмотрим, как это выглядит на практике. Сразу определимся с версиями продуктов. Я использовал последние доступные версии:

  • Visual Studio 2005 (version 8.0.50727.42) - финальный release Visual Studio 2005.

  • Enterprise Library for .NET Framework 2.0 (November 2005 Community Technology Preview). Как раз сегодня (хотя, когда я допишу этот выпуск, скорее всего уже наступит завтра :) был выложен December interim Community Drop - сырые исходники, без всякой инсталляции и документации, но это для фанатов, осталось подождать еще немного и должен появиться нормальный release этой библиотеки (обещан в декабре).

Если у вас не установлен release VisualStudio 2005 - лучше не терять время и подключить версию Enterprise Library for .NET Framework 1.1. Дело в том, что для компиляции Enterprise Library for .NET Framework 2.0 необходима release версия .Net Framework 2.0 (version 2.0.50727). Даже если у вас установлена Visual Studio 2005 Beta - не надейтесь обойтись установкой всего лишь run-time .NET Framework release. Библиотека у вас может и скомпилируется, а вот VS 2005 Beta запускаться уже не будет - я убедился в этом на собственном печальном опыте, думаете откуда бы у меня  иначе нашлось время и желание доставать VS 2005 release и инсталлировать на своем компьютере, начисто снося все, что имело хоть какое-то отношение к слову Beta? :)

Enterprise Library for .NET Framework 1.1 не так уж сильно отличается от своей следующей версии (под .NET 2.0), так что много вы не потеряете. Смотрите сами, если не считать изменений во внутренней реализации, в новой версии добавилось:

  • возможность программно указывать строки соединения (connection strings), а не ограничиваться только теми, что хранятся в конфигурационном файле. Это полезно, если строка соединения формируется динамически, например в зависимости от пользователя, залогиненного в систему.

  • Для хранения строк соединений взамен собственного конфигурационного файла используется стандартная конфигурационная секция ADO.NET <connection strings>. Это позволяет обращаться к этим строкам другим .NET классам вашего приложения.
  • Используя Generic Database класс, вы можете работать с любым провайдером данных, поддерживаемым ADO.NET 2.0. В предыдущей версии вам были доступны только входящие в Enterprise Library провайдеры для MS SQL и Oracle (конечно, если вам не доставляло удовольствие написать собственный провайдер, унаследовав его от класса Database из Enterprise Library).

Вобщем, я буду рассматривать работу с Enterprise Library for .NET Framework 2.0, но с небольшими изменениями вы можете проделать то же самое со старой версией под .NET 1.1.

Итак, поехали:

  • Создаем новое Web приложение.

  • Для того чтобы хоть что-то увидеть, добавим на созданную страницу Default.aspx элемент управления DataGrid, куда и будем выводить полученные данные.

  • Подключим к проекту Enterprise Library. Для этого как reference подключаем к проекту Data Access application block (файл Microsoft.Practices.EnterpriseLibrary.Data.dll). Его можно найти в каталоге, в который вы проинсталлировали Enterprise Library, у меня это -  "C:\Program Files\Microsoft Enterprise Library November 2005\bin". Обратите внимание, что к проекту автоматически подключились еще две библиотеки: Common и Configuration, на которые ссылается библиотека Data Access.

  • Добавляем в наш Default.aspx.cs строку
using Microsoft.Practices.EnterpriseLibrary.Data;
  • Далее нужно указать строку соединения с источником данных в конфигурационном файле приложения. Для того чтобы упростить себе жизнь, обратимся к старой доброй базе Northwind из стандартной поставки MS SQL Server.
Прим:
Тут мы столкнемся с небольшой проблемой - дело в том, что в November 2005 Community Technology Preview не входит новая версия замечательной утилиты Enterprise Library Configuration для визуального редактирования конфигурационных файлов. В примере по работе с Data Access application block параметры для соединения были прописаны прямо в web.config. Когда я попытался работать со старой версией EntLib Configuration (для VS 2003), все сохранилось без ошибок, но при запуске приложение никак не могло найти в config файле описание базы данных - так что формат все таки изменился. Так что если мы хотим посмотреть, как это должно выглядеть по настоящему, нам прийдется потратить немного времени на добывание новой версии утилиты. Вариантов у на два:
1. Загрузить к себе на машину вышеупомянутый December interim Community Drop - по слухам он включает в себя долгожданную новую версию нужной нам утилиты. Но, во первых, я это лично не проверял, во вторых Community Drop содержит только исходники, а значит эту утилиту вам прийдется компилировать самому. Поэтому я советую сделать шаг конем, зато он окажеться проще, а именно
2. Новая версия утилиты входит в состав инсталляции Net Tiers 0.9.2 Caribert - набора шаблонов для генерирования объектов Data Access Component Layer для распределенных приложений. Если вам ничего не говорит это название, возможно вы слышали о NGibernate для .NET (или не дай бог о его прообразе под Java), CSLA .NET framework или Wilson ORMapper? Если да - суть у них одна и та же и это даст вам какое-то представление о Net Tiers, если нет - в будущих выпусках я затрону эту тему, так как сейчас активно экспериментирую с этими архитектурными решениями. Ну а пока, мы вполне можем воспользоваться EntLib Configuration из инсталляции Net Tiers не имея ни малейшего представления ни о самой Tiers ни о прочих сложных аббревиатурах.
Так что загружаем инсталляцию Net Tiers (она занимает чуть больше 3Mb - даже меньше чем December interim Community Drop) и - вуа ля, у нас в  меню появляется пункт "Enterprise Library Configuration for VS2005".
  • Итак, запускаем Enterprise Library Configuration, выбираем опцию меню "Open Application" и открываем конфигурационный файл web.config нашего приложения. В tree view слева отображается пустой узел "Application", так как наш web.config еще не содержит каких-либо настроек для application blocks.

  • Из контекстного меню узла "Application" выбираем "New - > Data Access Application Block". Tree view сразу заполняется, приобретая вид, показанный на рис 1.

Дело в том, что в конфигурационный файл автоматичски добавляются настройки и для других application blocks, на которые ссылается наш Data Access application block. К счастью, единственное, что нам прийдется указать - так это параметры строки соединения. Для этого:

  • В качестве значения для узла "database" указываем название нашей базы "Northwind" (см. рис 1).

  • В качестве значения для узла "server" указываем название SQL Server, у меня это "localhost".
  • Если SQL Server использует SQL authorization (а не интегрированную Windows авторизацию), вам прийдется установить значение параметра "Integrated Security" в False и задать два дополнительных параметра: "User ID" и "Password".

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

Теперь перейдем к написанию самого кода. Поставим перед собой простую, но типичную задачу: вызвать хранимую процедуру, принимающую на вход несколько параметров, получить возвращаемый результат в виде DataSet и отобразить его на странице при помощи элемента управления DataGrid. Для примера возьмем процедуру "Employee Sales by Country", которая принимает на вход два параметра: начало и окончание периода и возвращает набор записей, описывающих продажи, совершенные в указанный промежуток времени.

Для сравнения, сначала рассмотрим вариант без использования Data Access application block. Он может выглядеть примерно так:

//  Получаем connection string, сохраненную в конфигурационной секции ADO.NET <connection strings>.
//  Это нововведение .NET 2.0, в .NET 1.1 разработчики как правило хранили строки соединения в секции <appSettings>.
//  Для простоты, мы будем предполагать, что у нас только одна строка соединения, поэтому обратимся к ней по индексу 1
//  (индекс 0 судя по всему зарезервирован для доступа к строке соединения с источником, хранящим информацию
//  для провайдеров ASP.NET 2.0, которые обеспечивают защиту приложения, поддерживают ролевую систему и т.д.?).
string connectionString = WebConfigurationManager.ConnectionStrings[1].ConnectionString;

//  Создаем объект ADO.NET Connection
using (SqlConnection connection = new SqlConnection(connectionString))
{
    // Создаем и настраиваем объект ADO.NET Command
    SqlCommand salesCmd = new SqlCommand();
    salesCmd.Connection = connection;
    salesCmd.CommandType = CommandType.StoredProcedure;
    salesCmd.CommandText = "Employee Sales by Country";

    // Задаем параметры для вызова хранимой процедуры
    salesCmd.Parameters.Add("@Beginning_Date", SqlDbType.DateTime).Value = new DateTime(1996, 01, 01);
    salesCmd.Parameters.Add("@Ending_Date", SqlDbType.DateTime).Value = new DateTime(1996, 12, 31);

    // Заполняем DataSet при помощи объекта ADO.NET DataAdapter
    DataSet sales = new DataSet();
      SqlDataAdapter adapter = new SqlDataAdapter(salesCmd);
    adapter.Fill(sales);
    
    // Связываем полученный DataSet с элкментом управления DataGrid и выводим полученные данные
    this.GridView1.DataSource = sales;
    this.GridView1.DataBind();
}

Теперь посмотрим на новый вариант с использованием Data Access application block:

// Создаем объект Database, все свои настройки он получит из конфигурационного файла
Database database = DatabaseFactory.CreateDatabase();

// Получаем DataSet, обращаясь к методу ExecuteDataSet созданного экземпляра класса Database
DataSet sales = database.ExecuteDataSet("Employee Sales by Country", new DateTime(1996, 01, 01), new DateTime(1996, 12, 31));

// Связываем полученный DataSet с элкментом управления DataGrid и выводим полученные данные
this.GridView1.DataSource = sales;
this.GridView1.DataBind();

Думаю, результат комментировать не нужно. Мало того, это решение можно упростить еще больше. Если вы использовали старую, 1-ю версию Data Access application block, возможно вы помните, что там все вызовы реализовывались всего одной строкой кода, например:

DataSet sales = SqlHelper.ExecuteDataSet("Employee Sales by Country", new DateTime(1996, 01, 01), new DateTime(1996, 12, 31));

Зачем же в новой версии понадобилось усложнять работу и вводить дополнительный объект Database?

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

В старой же версии для выполнения операций с базой данных мы использовали статические методы класса SqlHelper, которые нельзя перегрузить и поэтому для смены типа бызы нам пришлось бы править код, заменяя вызовы статических методов SqlHelper (умеющего работать с MS SQL Server) скажем на вызовы OracleHelper, умеющего делать то же самое, но для Oracle.

Но! Если мы знаем что наш проект не будет постоянно перенастраиваться с одного источника данных на другой (для реального проекта это достаточно серьезная работа, что служит хорошим поводом семь раз подумать, а уж потом принять такое решение), мы можем достаточно легко вернуть простоту использования первой версии. Для этого можно написать класс-оболочку со статическими методами (аналог SqlHelper), которые будут принимать на вход параметры, аналогичные параметрам методов класса Database, создавать этот объект и вызывать его соответствующий метод. Например:

public static class SqlDatabase
{
    public static DataSet ExecuteDataSet(string storedProcedureName, params object[] parameterValues)
    {
        Database database = DatabaseFactory.CreateDatabase();
        return database.ExecuteDataSet(storedProcedureName, parameterValues);
    }
}

Тогда наш пример превращается в:

this.GridView1.DataSource = SqlDatabase.ExecuteDataSet("Employee Sales by Country", new DateTime(1996, 01, 01), new DateTime(1996, 12, 31));
this.GridView1.DataBind();

А теперь сравните его с нашим первоначальным вариантом ...

Еще пара моментов о реализации Data Access application block ... Как видите, вы передаете параметры для вызова хранимой процедуры просто перечислив их через запятую. Для того чтобы правильно сформировать запрос, application block получит метаданные о хранимой процедуре, сформирует массив объектов SqlParameter и, при необходимости, приведет тип переданных вами параметров к типам параметров, которые требуются хранимой процедуре. Для того, чтобы производительность программы не снижалась, полученные метаданные кешируются и в следующий раз берутся уже из кеша.

Кроме метода ExecuteDataSet, класс Database содержит следующие методы для возвращения других типов данных:

  • ExecuteNonQuery - выполняет операцию, не возвращающую значения (например SQL UPDATE statement)
  • ExecuteReader - возвращает данные в виде объекта ADO.NET DataReader
  • ExecuteScalar - возвращает скалярное значение (число, строка, логическое значание)

Конечно, возможности Data Access application block не исчерпываются перечисленными выше примерами. Но ... уже давно наступила ночь и Шахерезаде пора заканчивать дозволенные речи ... Я думаю, что я рассказал достаточно, чтобы вы решили, стоит ли вам использовать этот блок в вашей работе.

И конечно, как всегда я буду рад услышать ваши вопросы и пожелания, пишите на мой адрес.

Удачи вам, приятной увлекательной работы, и - до следующего выпуска!



Subscribe.Ru
Поддержка подписчиков
Другие рассылки этой тематики
Другие рассылки этого автора
Подписан адрес:
Код этой рассылки: comp.soft.prog.prgnotes
Архив рассылки
Отписаться
Вспомнить пароль

В избранное