Написание универсального кода доступа к данным в ASP.NET 2.0 и ADO.NET 2.0
Д-р Шахрам Хосрави
Информационные решения
Апрель 2005 г.
Применимо к:
Microsoft ADO.NET 2.0
Microsoft ASP.NET 2.0
Microsoft .NET Framework 2.0
Бета-версия Microsoft Visual Web Developer 2005 Express Edition
Язык программирования C#
Сводка. Используйте пошаговый подход, чтобы узнать, как использовать разные ASP.NET 2.0 и ADO.NET 2.0 средства и методы для написания универсального кода доступа к данным. (18 печатных страниц)
Скачайте связанный пример кода: GenericDataAccessSample.exe.
Знакомство
Большинство веб-приложений содержат код доступа к данным для доступа к базовому хранилищу данных для выполнения базовых операций с данными, таких как Select, Update, Deleteи Insert. В этой статье используется пошаговый подход, чтобы показать, как разработчики страниц могут воспользоваться разными ASP.NET 2.0 и ADO.NET 2.0 средствами и методами для написания универсального кода доступа к данным, который можно использовать для доступа к различным типам хранилищ данных. Написание универсального кода доступа к данным особенно важно в веб-приложениях, управляемых данными, так как данные поступают из множества различных источников, включая Microsoft SQL Server, Oracle, XML-документы, неструктурированные файлы и веб-службы, просто назовите несколько.
В этой статье используется простое веб-приложение в качестве тестовой кровати для всего кода, представленного здесь. Приложение состоит из двух частей: первая часть позволяет системным администратору отправлять информационные бюллетени всем подписчикам списка рассылки. Вторая часть позволяет пользователям подписываться или отменять подписку из списка рассылки. Первая часть статьи начинается с реализации простого кода доступа к данным (см. рис. 1) для доступа к Microsoft SQL Server и извлечения списка подписчиков. Код изменяется и делается более универсальным в ходе статьи.
Рис. 1. Метод GetSubscribers извлекает список всех подписчиков.
public IEnumerable GetSubscribers()
{
SqlConnection con = new SqlConnection();
con.ConnectionString = @"Data Source=.\SQLExpress;Integrated Security=True;AttachDBFilename=D:\Application\Data\Database.mdf";
SqlCommand com = new SqlCommand();
com.Connection = con;
com.CommandText = "Select * From Subscribers";
com.CommandType = CommandType.Text;
DataSet ds = new DataSet();
SqlDataAdapter ad = new SqlDataAdapter();
ad.SelectCommand = com;
con.Open();
ad.Fill(ds);
con.Close();
return ds.Tables[0].DefaultView;
}
Так как код доступа к данным на рис. 1 содержит код для создания объектов ADO.NET, таких какsqlConnection
Шаблон поставщика в ADO.NET 2.0
Основная проблема с методом GetSubscribers заключается в том, что он содержит код для создания объектов ADO.NET. Согласно шаблону поставщика, код доступа к данным должен делегировать ответственность за предоставление кода для создания объектов ADO.NET в другой класс. Я ссылаюсь на этот класс как класс поставщика кода, так как он предоставляет код для создания объектов ADO.NET. Класс поставщика кода предоставляет такие методы, как CreateConnection, CreateCommandи CreateDataAdapter, где каждый метод предоставляет код для создания соответствующего объекта ADO.NET.
Так как класс поставщика кода содержит фактический код, один и тот же класс нельзя использовать для доступа к разным хранилищам данных. Поэтому код доступа к данным (метод GetSubscribers) необходимо изменить и перенастроить, чтобы делегировать предоставление кода новому классу поставщика кода каждый раз, когда он используется для доступа к новому хранилищу данных. Метод GetSubscribers по-прежнему привязан к коду, даже если он не содержит код.
Шаблон поставщика предлагает решение этой проблемы и состоит из следующих шагов:
Проектирование и реализация абстрактного базового класса поставщика.
Наследует все классы поставщика кода из абстрактного базового класса поставщика.
Код доступа к данным (метод GetSubscribers) использует абстрактный базовый класс вместо отдельных классов поставщика кода.
Абстрактный базовый класс делегирует ответственность за предоставление кода для создания объектов ADO.NET в соответствующий подкласс. Абстрактный базовый класс называется DbProviderFactory. Ниже приведены некоторые методы этого класса:
public abstract class DbProviderFactory { public virtual DbConnection CreateConnection(); public virtual DbCommand CreateCommand(); public virtual DbDataAdapter CreateDataAdapter(); }
Каждый подкласс предоставляет код для создания соответствующих ADO.NET объектов для определенного хранилища данных. Например, подкласс sqlClientFactory
предоставляет код для создания объектов ADO.NET для доступа к Microsoft SQL Server, как показано на рис. 2. Рис. 2. Класс SqlClientFactory и некоторые из его методов
public class SqlClientFactory : DbProviderFactory { public override DbConnection CreateConnection() { return new SqlConnection(); } public override DbCommand CreateCommand() { return new SqlCommand(); } public override DbDataAdapter CreateDataAdapter() { return new SqlDataAdapter(); } }
Шаблон поставщика позволяет коду доступа к данным обрабатывать все подклассы одинаково, так как они являются подклассами одного базового класса. Что касается кода доступа к данным, все подклассы имеют тип DbProviderFactory. Код доступа к данным не имеет способа знать конкретный тип используемого подкласса. Это представляет новую проблему. Если код доступа к данным (метод GetSubscribers) не знает тип подкласса, как создать экземпляр подкласса?
Решение шаблона поставщика для этой проблемы состоит из следующих трех частей:
- Для идентификации каждого подкласса используется уникальная строка. ADO.NET 2.0 использует пространство имен подкласса в качестве уникального идентификатора строки. Например, идентификатор уникальной строки System.Data.SqlClient и System.Data.OracleClient определить SqlClientFactory и подклассы OracleClientFactory соответственно.
- Текстовый файл (обычно XML-файл) используется для хранения сведений обо всех подклассах. ADO.NET 2.0 использует файлы machine.config и web.config для хранения необходимых сведений. Сведения о подклассе содержат, помимо прочего, уникальный идентификатор строки и имя типа подкласса. Например, сведения о подклассе SqlClientFactory включают уникальный идентификатор строки System.Data.SqlClient и имя типа подкласса, т. е. System.Data.SqlClient.SqlClientFactory.
- Статический метод разработан и реализован. Метод может быть частью абстрактного базового класса или частью отдельного класса. ADO.NET 2.0 использует отдельный класс с именем DbProviderFactories, который предоставляет статический метод GetFactor y. Метод принимает уникальный идентификатор строки требуемого подкласса в качестве единственного аргумента и выполняет поиск по файлу machine.config для подкласса с заданным уникальным идентификатором строки. Метод извлекает имя типа требуемого подкласса и использует отражение для динамического создания экземпляра подкласса.
Код доступа к данным (метод GetSubscribers) вызывает статический метод GetFactory и передает соответствующий уникальный идентификатор строки для доступа к экземпляру соответствующего подкласса. После доступа к экземпляру метод GetSubscribers вызывает соответствующие методы создания, такие как CreateConnection(), CreateCommand() и т. д., чтобы создать экземпляр соответствующих объектов ADO.NET, как показано на рис. 3.
Рис. 3. Версия метода GetSubscribers, использующего новый шаблон поставщика ADO.NET
public IEnumerable GetSubscribers()
{
DbProviderFactory provider = DbProviderFactories.GetFactory("System.Data.SqlClient");
DbConnection con = provider.CreateConnection();
con.ConnectionString = @"Data Source=.\SQLExpress;Integrated Security=True;AttachDBFilename=D:\Application\Data\Database.mdf";
DbCommand com = provider.CreateCommand();
com.Connection = con;
com.CommandText = "Select * From Subscribers";
com.CommandType = CommandType.Text;
DataSet ds = new DataSet();
DbDataAdapter ad = provider.CreateDataAdapter();
ad.SelectCommand = com;
con.Open();
ad.Fill(ds);
con.Close();
return ds.Tables[0].DefaultView;
}
Код доступа к данным (метод GetSubscribers) делегирует предоставление кода для создания объектов ADO.NET экземпляру класса поставщика кода, который создается и возвращает метод GetFactory. Таким образом, один и тот же код доступа к данным можно использовать для доступа к разным хранилищам данных, таким как Microsoft SQL Server и Oracle.
Код для создания правильных ADO.NET объектов — это хранилище данных. Шаблон поставщика в ADO.NET 2.0 удаляет эти части хранилища данных из кода доступа к данным (метод GetSubscribers), чтобы сделать его более универсальным. Однако шаблон поставщика не удаляет все части хранилища данных. Более тщательная проверка метода GetSubscribers показывает следующие оставшиеся части хранилища данных:
- Строка подключения
- Уникальный идентификатор строки, определяющий базовый класс поставщика кода
- Текст команды
- Тип команды
Если в указанных выше частях что-то не сделано, код доступа к данным по-прежнему привязан к конкретному типу хранилища данных. Шаблон поставщика в ADO.NET 2.0 не помогает решить эту проблему. Однако ADO.NET 2.0 предоставляет нам другие средства и методы для удаления первых двух частей хранилища данных, таких как строка подключения и уникальный идентификатор строки из метода GetSubscribers.
Строки подключения
Строки подключения являются одними из наиболее ценных ресурсов в веб-приложении. Они настолько важны, что платформа .NET Framework 2.0 рассматривает их как "граждан первого класса". Файл web.config теперь поддерживает новый раздел с именем <connectionStrings>, содержащий все строки подключения, используемые в приложении. Поэтому мы переместим строку подключения из метода GetSubscribers в этот раздел:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add
name="MySqlConnectionString"
connectionString="Data Source=.\SQLExpress;Integrated Security=True;AttachDBFilename=D:\Application\Data\Database.mdf"
providerName="System.Data.SqlClient"/>
</connectionStrings>
</configuration>
<добавить> подэлемент элемента <connectionStrings> предоставляет следующие три важных атрибута:
- имя— понятное имя строки подключения
- connectionString— фактическая строка подключения
- providerName— уникальный идентификатор строки или инвариант класса поставщика кода
NET Framework 2.0 предоставляет код доступа к данным (метод GetSubscribers) с правильными средствами для универсального извлечения значения строки подключения из файла web.config, как описано ниже. Пространство имен System.Configuration
Сам класс предоставляет статический метод с именем
Configuration configuration = Configuration.GetWebConfiguration("~/");
Класс, наследующий от класса ConfigurationSection ConfigurationSection, представляет каждый раздел файла web.config. Имя класса состоит из имени раздела и раздела ключевого слова. Например, класс ConnectionStringsSection
ConnectionStringsSection section = (ConnectionStringsSection)configuration.Sections["connectionStrings"];
Так как Sections представляет собой коллекцию объектов ConfigurationSection, код доступа к данным должен ввести возвращаемый экземпляр. Когда метод GetSubscribers обращается к объекту ConnectionStringsSection и использует его для доступа к значению строки подключения:
string connectionString = section.ConnectionStrings["MySqlConnectionString"].ConnectionString;
На рисунке 4 показана новая версия метода GetSubscribers, содержащего необходимый код для извлечения строки подключения в универсальном режиме.
Рис. 4. Версия метода GetSubscribers, извлекающего строку подключения из файла web.config
public IEnumerable GetSubscribers()
{
DbProviderFactory provider = DbProviderFactories.GetFactory("System.Data.SqlClient");
DbConnection con = provider.CreateConnection();
Configuration configuration = Configuration.GetWebConfiguration("~/");
ConnectionStringsSection section = (ConnectionStringsSection)configuration.Sections["connectionStrings"];
con.ConnectionString = section.ConnectionStrings["MySqlConnectionString"].ConnectionString;
DbCommand com = provider.CreateCommand();
com.Connection = con;
com.CommandText = "Select * From Subscribers";
com.CommandType = CommandType.Text;
DataSet ds = new DataSet();
DbDataAdapter ad = provider.CreateDataAdapter();
ad.SelectCommand = com;
con.Open();
ad.Fill(ds);
con.Close();
return ds.Tables[0].DefaultView;
}
Метод GetSubscribers теперь включает в себя строку MySqlConnectionString, поэтому нам по-прежнему необходимо изменить метод GetSubscribers, чтобы добавить поддержку другого хранилища данных, например базы данных Oracle. Казалось бы, мы вернулись к квадрату. Не так. Мы получили несколько важных преимуществ путем перемещения строки подключения из кода доступа к данным в файл web.config:
Строка подключения теперь является значением атрибута connectionString <добавить> вложенный элемент элемента connectionStrings файла web.config, который является XML-документом. Отличное дело в XML-документе заключается в том, что мы можем зашифровать один элемент в документе. Мы не должны шифровать весь документ, если необходимо защитить небольшую часть документа. Платформа .NET Framework 2.0 поставляется с инструментом, который позволяет шифровать <connectionStrings> раздел для защиты наших наиболее важных ресурсов, строк подключения. Представьте, сколько ущерба хакеру может сделать наша ценная база данных, если он получает руку на наших строках подключения. Помните, что строки подключения — это все хакеры, которые должны получить доступ к нашей базе данных.
Может показаться, что все, что мы сделали, заключается в замене следующей строки
"Data Source=.\SQLExpress;Integrated Security=True;AttachDBFilename=D:\Application\Data\Database.mdf"
с новой строкой MySqlConnectionString. Однако есть одна большая разница. Бывшая строка содержит сведения, относящиеся к базе данных SQL Server, которые не применяются к другой базе данных, например Oracle, но последняя строка — это просто понятное имя.
Однако понятное имя по-прежнему может вызвать проблемы, так как оно ссылается на определенную строку подключения в разделе <connectionStrings> файла web.config. В нашем примере он ссылается на строку подключения, используемую для доступа к Microsoft SQL Server. Это означает, что метод GetSubscribers (код доступа к данным) необходимо изменить, чтобы использовать другое понятное имя для доступа к другому хранилищу данных, например Oracle.
Чтобы избежать изменения кода доступа к данным, можно переместить понятное имя из кода доступа к данным в раздел appSettings <appSettings> файла web.config и динамически извлечь код доступа к данным в среде выполнения следующим образом:
string connectionStringName = ConfigurationSettings.AppSettings["ConnectionStringName"];
Мы также переместим имя поставщика в раздел <appSettings>:
string providerName = ConfigurationSettings.AppSettings["ProviderName"];
Разработчики страниц просто изменяют значение атрибута <добавления> подэлемента элемента <appSettings> на тот же код доступа к данным, чтобы получить доступ к другому хранилищу данных без внесения изменений в код доступа к данным.
На рис. 5 представлена версия кода доступа к данным (метод GetSubscribers), который содержит последние изменения.
Рис. 5. Версия метода GetSubscribers для извлечения имени поставщика и понятного имени строки подключения из файла web.config
public IEnumerable GetSubscribers()
{
string connectionStringName = ConfigurationSettings.AppSettings["ConnectionStringName"];
string providerName = ConfigurationSettings.AppSettings["ProviderName"];
Configuration configuration = Configuration.GetWebConfiguration("~/");
ConnectionStringsSection section = (ConnectionStringsSection)configuration.Sections["connectionStrings"];
DbProviderFactory provider = DbProviderFactories.GetFactory(providerName);
DbConnection con = provider.CreateConnection();
con.ConnectionString = section.ConnectionStrings[connectionStringName].ConnectionString;
DbCommand com = provider.CreateCommand();
com.Connection = con;
com.CommandText = "Select * From Subscribers";
com.CommandType = CommandType.Text;
DataSet ds = new DataSet();
DbDataAdapter ad = provider.CreateDataAdapter();
ad.SelectCommand = com;
con.Open();
ad.Fill(ds);
con.Close();
return ds.Tables[0].DefaultView;
}
Элементы управления источниками данных
Последняя версия метода GetSubscribers, как показано на рис. 5, по-прежнему не является универсальной из-за следующих проблем:
- Метод содержит хранилище данных— конкретные сведения, т. е. текст команды и тип команды. Поэтому разработчикам страниц по-прежнему нужно изменить метод, прежде чем они смогут использовать его для доступа к другой базе данных.
- Метод возвращает экземпляр класса DataView вызывающим элементам, чтобы метод передает табличные данные вызывающих объектов. Мы можем подумать об этом как о контракте между методом GetSubscribers и его вызывающими. Вызывающие ожидают, что метод GetSubscribers будет учитывать контракт во всех обстоятельствах, даже если базовое хранилище данных не таблично. Так как метод GetSubscribers использует объекты ADO.NET для доступа к базовому хранилищу данных, он не может получить доступ к иерархическему хранилищу данных, например XML-файлу, где экземпляры классов в System.Xml и его подпространстве имен должны использоваться вместо ADO.NET объектов.
Основная проблема с кодом доступа к данным, используемым в методе GetSubscribers, заключается в том, что он непосредственно содержит фактический код, извлекающий данные из базового хранилища данных. Это именно та проблема, которую шаблон поставщика .NET Framework 2.0 специально предназначен для решения. В соответствии с шаблоном поставщика метод GetSubscribers должен делегировать ответственность за предоставление кода для доступа к хранилищу данных к другому классу. Он называется классом поставщика кода. Тип класса поставщика кода зависит от типа доступного к хранилищу данных. Эти классы поставщика кода коллективно называются элементами управления источниками данных. ASP.NET 2.0 поставляется с несколькими различными типами элементов управления источниками данных, включая SqlDataSource, AccessDataSource, ObjectDataSource, XmlDataSourceи элементы управления SiteMapDataSource.
Элемент управления SqlDataSource специально предназначен для обновления, удаления, вставки и извлечения данных из реляционных хранилищ данных, таких как Microsoft SQL Server и Oracle. Элемент управления AccessDataSource — это подкласс элемента управления SqlDataSource, который знает, как работать с базами данных Microsoft Access. С другой стороны, элемент управления ObjectDataSource использует бизнес-объекты в памяти в качестве хранилища данных. Элемент управления XmlDataSource специально предназначен для извлечения данных из XML-документов. Однако элемент управления XmlDataSource не предоставляет доступ на запись к базовому XML-документу.
Каждый элемент управления источником данных предоставляет одно или несколько представлений базового хранилища данных. Каждое представление — это экземпляр соответствующего класса. Например, элементы управления SqlDataSource, AccessDataSource и ObjectDataSource предоставляют представления, которые являются экземплярами SqlDataSourceView, AccessDataSourceViewи классах ObjectDataSourceView соответственно. Представления скрывают реальный тип базового хранилища данных и делают его таким же, как тип, который ожидает код доступа к данным. Например, метод GetSubscribers ожидает табличный тип хранилища данных, так как он передает табличные данные своих клиентов. Табличные представления позволяют методу GetSubscribers извлекать табличные данные из базового хранилища данных, даже если само хранилище данных является иерархическим источником данных, например XML-документом. Это позволяет методу GetSubscribers обрабатывать как табличные, так и иерархические хранилища данных как табличные хранилища данных.
Элементы управления источниками данных могут предоставлять своим клиентам два типа представлений: табличные и иерархические. ASP.NET 2.0 поставляется с двумя элементами управления источниками данных, которые предоставляют оба типа представлений, XmlDataSource и SiteMapDataSource. Остальные элементы управления источниками данных — SqlDataSource, AccessDataSource и ObjectDataSource — доступны только табличные представления. Однако их можно расширить для предоставления табличных и иерархических представлений.
Элементы управления табличными источниками данных
Табличный элемент управления источником данных делает его базовым хранилищем данных действовать как табличное хранилище данных, независимо от того, является ли хранилище табличным. Табличное хранилище данных состоит из таблиц строк и столбцов, где каждая строка представляет элемент данных. Имя таблицы однозначно идентифицирует и находит таблицу среди других таблиц в табличном хранилище данных. Табличное представление действует как таблица, что означает, что представления именуются.
Как описано ранее, каждый класс управления источником данных и связанный с ним класс представления (например, класс SqlDataSource и связанный с ним класс SqlDataSourceView) предоставляют фактический код для обновления, удаления, вставки и извлечения данных из базового хранилища данных. Очевидно, что код для каждого элемента управления источниками данных и связанного класса представления специально предназначен для работы с определенным типом хранилища данных. Таким образом, каждый класс управления источником данных и связанный с ним класс представления зависят от хранилища данных. Это представляет серьезную проблему для метода GetSubscribers, который использует элементы управления источником данных и их табличные представления для доступа к базовому хранилищу данных, так как он связывает метод с определенным типом хранилища данных, что означает, что один и тот же метод нельзя использовать для доступа к разным типам хранилищ данных.
ASP.NET 2.0 предлагает решение, использующее шаблон поставщика для:
- Введите интерфейс
IDataSource и абстрактный класс DataSourceViewDataSourceView - Наследование всех элементов управления табличными источниками данных из интерфейса IDataSource
- Получение всех табличных представлений из абстрактного класса DataSourceView
Интерфейс IDataSource и абстрактный класс DataSourceView делегируют ответственность за предоставление фактического кода для обновления, удаления, вставки и извлечения данных из хранилища данных в соответствующие подклассы. Код доступа к данным, такой как метод GetSubscribers, должен использовать методы и свойства интерфейса IDataSource и абстрактного класса DataSourceView. Они не должны использовать какой-либо метод или свойство, относящиеся к определенному классу управления версиями данных, например SqlDataSource или определенному классу представления источников данных, например SqlDataSourceView. Шаблон поставщика позволяет коду доступа к данным обрабатывать все элементы управления источниками данных и их соответствующие представления источников данных универсальным образом. Что касается кода доступа к данным, все элементы управления источниками данных имеют тип IDataSource, а все представления источников данных имеют тип DataSourceView. Код доступа к данным не имеет способа знать фактический тип используемого объекта управления версиями данных и представления источника данных. Это приводит к новой проблеме. Если код доступа к данным (метод GetSubscribers) не знает тип элемента управления версиями данных, как создать экземпляр его?
Как описано ранее, шаблон поставщика предоставляет решение, состоящее из следующих шагов:
- Уникальный идентификатор строки используется для идентификации каждого класса управления версиями данных.
- Текстовый файл (обычно XML-файл) используется для хранения сведений обо всех классах управления версиями данных.
- Механизм разработан и реализован, который выполняет поиск XML-файла для подкласса с заданным идентификатором строки.
Теперь давайте посмотрим, как ASP.NET 2.0 реализует перечисленные выше три задачи шаблона поставщика. ASP.NET 2.0 наследует все элементы управления источником данных из класса
- Их можно создать декларативно.
- Они сохраняют и восстанавливают значения свойств в обратной части.
- Они добавляются в дерево элементов управления содержащей страницы.
Первая функция позволяет разработчикам страниц декларативно создавать экземпляры элементов управления источниками данных в соответствующих .aspx файлах. Поэтому файл .aspx выступает в качестве текстового или XML-файла, который требуется второму шагу шаблона поставщика. Архитектура элемента управления ASP.NET 2.0 динамически создает экземпляр объявленного элемента управления источником данных и назначает экземпляр переменной, имя которой является значением свойства идентификатора объявленного элемента управления версиями данных. При этом необходимо выполнить указанные выше и третие шаги, необходимые шаблону поставщика.
На рисунке 6 показана версия метода GetSubscribers, использующего методы и свойства интерфейса IDataSource и абстрактного класса DataSourceView для доступа к базовому хранилищу данных:
Рис. 6. Версия метода GetSubscribers, использующего методы и свойства интерфейса IDataSource и абстрактного класса DataSourceView
void GetSubscribers()
{
IDataSource ds = (IDataSource)MySource;
DataSourceView dv = ds.GetView(String.Empty);
DataSourceSelectArguments args = new DataSourceSelectArguments();
if (dv.CanSort)
args.SortExpression = "Email";
DataSourceViewSelectCallback callback = new DataSourceViewSelectCallback(SendMail);
dv.Select(args, callback);
}
Первая строка метода GetSubscribers четко показывает, что метод обрабатывает элемент управления источником данных как объект типа IDataSource. Метод не заботится о реальном типе элемента управления источниками данных, например о том, является ли он элементом управления sqlDataSource, AccessDataSource, ObjectDataSource или XmlDataSource. Это позволит разработчикам страниц переключаться с одного элемента управления источниками данных на другой, не изменяя код доступа к данным (метод GetSubscribers). В следующем разделе рассматривается эта важная проблема в более подробной информации.
Метод GetSubscribers вызывает метод GetView объекта IDataSource для доступа к объекту табличного представления по умолчанию. Обратите внимание, что метод GetView возвращает объект типа DataSourceView. Метод GetSubscribers не учитывает и не должен заботиться о реальном типе объекта представления, например о том, является ли объект SqlDataSourceView, AccessDataSourceView, ObjectDataSourceView или XmlDataSourceView.
Затем метод GetSubscribers создает экземпляр класса DataSourceSelectArguments для запроса дополнительных операций, таких как вставка, разбиение по страницам или получение общего количества строк данных , возвращаемых операцией выбора. Сначала метод должен проверить значение свойства CanInsert, CanPageили CanRetrieveTotalRowCount класса DataSourceView, чтобы убедиться, что объект представления поддерживает соответствующую операцию перед выполнением запроса.
Так как операция Select является асинхронной, метод GetSubscribers регистрирует метод SendMail в качестве обратного вызова. Метод Select автоматически вызывает метод SendMail после запроса данных и передает данные в качестве аргумента, как показано на рис. 7.
Рис. 7. Метод SendMail перечисляет данные и извлекает необходимые сведения.
void SendMail(IEnumerable data)
{
string firstName = String.Empty;
string lastName = String.Empty;
IEnumerator iter = data.GetEnumerator();
while (iter.MoveNext())
{
MailMessage message = new MailMessage();
message.From = "admin@greatnews.com";
message.To = DataBinder.Eval(iter.Current, "Email").ToString();
message.Subject = "NewsLetter";
firstName = DataBinder.Eval(iter.Current, "FirstName").ToString();
lastName = DataBinder.Eval(iter.Current, "LastName").ToString();
string mes = "Dear " + firstName + " " + lastName + ",<br/>";
mes += MessageBody.Text;
message.Body = mes;
message.BodyFormat = MailFormat.Html;
SmtpMail.SmtpServer = "<myserver>";
SmtpMail.Send(message);
}
}
Метод SendMail вызывает метод GetEnumerator объекта, переданного в качестве первого аргумента для доступа к объекту перечислителя, и использует перечислитель для перечисления данных. Метод SendMail использует метод Eval класса DataBinder для извлечения адреса электронной почты, имени и фамилии каждого подписчика и отправки новостной буквы каждому подписчику.
Переход с одного элемента управления источниками данных на другой
Как упоминалось ранее, архитектура управления ASP.NET динамически создает экземпляр элемента управления источником данных, объявленного на соответствующей странице .aspx, и назначает его переменной, имя которой — значение свойства идентификатора объявленного элемента управления версиями данных. Такое динамическое создание экземпляра объявленного элемента управления источниками данных изолирует метод GetSubscribers от фактического типа элемента управления источником данных и позволяет методу обрабатывать все элементы управления источниками данных как объекты типа IDataSource. Это позволяет разработчикам страниц переключаться с одного типа управления версиями данных на другой, не изменяя код доступа к данным (метод GetSubscribers). В этом разделе представлен пример такого случая.
Предположим, что веб-приложение использует метод GetSubscribers в сочетании с элементом управления ObjectDataSource для получения списка подписчиков из реляционного хранилища данных, например Microsoft SQL Server:
<asp:SqlDataSource ID="MySource" Runat="Server"
ConnectionString="<%$ ConnectionStrings:MySqlConnectionString %>"
SelectCommand="Select * From Subscribers" />
Предположим, что веб-приложение работает в среде, где список подписчиков также может поступать из XML-документа. XML-документ может быть локальным XML-файлом или удаленным ресурсом, доступ к который осуществляется по URL-адресу. Поэтому наше приложение должно иметь возможность получить список подписчиков из XML-документов. Очевидно, что элемент управления ObjectDataSource не предназначен для получения табличных данных из XML-документов, что означает, что необходимо использовать элемент управления источником данных, который может извлекать табличные данные из XML-документов, таких как элемент управления XmlDataSource.
Метод GetSubscribers не использует никаких свойств или методов, относящихся к классам ObjectDataSource и ObjectDataSourceView, и использует только методы и свойства интерфейса IDataSource и абстрактного класса DataSourceView для работы с элементами управления источниками данных. Можно легко переключаться с ObjectDataSource на элемент управления XmlDataSource и использовать тот же код доступа к данным, что и метод GetSubscribers для получения списка подписчиков:
<asp:XmlDataSource ID="MySource" Runat="Server"
DataFile="data.xml" XPath="/Subscribers/Subscriber" />
Значение атрибута XPath элемента управления XmlDataSource имеет значение /Подписчик или подписчик, чтобы выбрать всех подписчиков.
Операция вставки и удаления
Напомним, что веб-приложение состоит из двух частей. Вторая часть приложения позволяет пользователям подписываться или отменять подписку из списка рассылки. Метод
Рис. 8. Метод подписки вызывается при нажатии кнопки "Подписка".
void Subscribe(Object sender, EventArgs e)
{
IDataSource ds = (IDataSource)MySource;
DataSourceView dv = ds.GetView(String.Empty);
KeyedList values = new KeyedList();
values.Add("Email", Email.Text);
values.Add("FirstName", FirstName.Text);
values.Add("LastName", LastName.Text);
DataSourceViewOperationCallback callback = new DataSourceViewOperationCallback(OperationCallback);
if (dv.CanInsert)
dv.Insert(values, callback);
}
Первая строка метода Subscribe показывает, что метод не заботится о реальном типе элемента управления версиями данных. Поэтому можно переключиться на другой элемент управления источниками данных, чтобы поддерживать новое хранилище данных, не изменяя код в методе Подписки.
Метод использует экземпляр класса KeyedList для сбора электронной почты, имени и фамилии подписчика. Нам не нужно использовать класс KeyedList. Мы можем использовать любой класс, реализующий интерфейс IDictionary, включая ArrayList, KeyedList и т. д.
Метод Subscribe проверяет значение свойства CanInsert объекта представления источника данных, чтобы убедиться, что объект представления поддерживает операцию Insert перед вызовом метода insert Insert. Метод Subscribe передает экземпляр KeyedList в качестве первого аргумента методу Insert.
Метод unsubscribe
Рис. 9. Метод отмены подписки вызывается при нажатии кнопки отмены подписки.
void Unsubscribe(Object sender, EventArgs e)
{
IDataSource ds = (IDataSource)MySource;
DataSourceView dv = ds.GetView(String.Empty);
KeyedList keys = new KeyedList();
keys.Add("Email", Email.Text);
KeyedList oldValues = new KeyedList();
oldValues.Add("Email", Email.Text);
DataSourceViewOperationCallback callback = new DataSourceViewOperationCallback(OperationCallback);
if (dv.CanDelete)
dv.Delete(keys, oldValues, callback);
}
Элементы управления иерархическими источниками данных
Метод GetSubscribers (код доступа к данным) передает табличные данные вызывающей стороны. Однако иногда код доступа к данным должен возвращать иерархические данные вызывающим абонентам. В этом разделе представлена реализация версии метода GetSubscribers, возвращающего иерархические данные. Мы можем подумать об этом как контракт между методом и его вызывающими. Вызывающие ожидают, что метод возвращает иерархические данные как из иерархических, так и табличных хранилищ данных.
ASP.NET 2.0 использует шаблон поставщика для изоляции метода GetSubscribers от фактического типа базового хранилища данных и представляет метод с иерархическими представлениями хранилища данных. Это позволяет методу обрабатывать как иерархические, так и табличные хранилища данных как иерархические хранилища данных.
Каждый иерархический элемент управления источником данных специально предназначен для работы с определенным хранилищем данных. Однако, так как все иерархические элементы управления источником данных реализуют интерфейс IHierarchicalDataSource и все иерархические представления источников данных, производные от класса HierarchyDataSourceView, метод GetSubscribers не должен иметь дело с конкретными особенностями каждого элемента управления источниками данных и может обрабатывать их в универсальной форме.
IHierarchicalEnumerable GetSubscribers()
{
IHierarchicalDataSource ds = (IHierarchicalDataSource)MySource;
HierarchicalDataSourceView dv = ds.GetHierarchicalView("/Subscribers");
return dv.Select();
}
Первая строка метода GetSubscribers показывает, что метод обрабатывает элемент управления источником данных как объект типа IHierarchicalDataSource и не заботится о реальном типе элемента управления версиями данных. Это позволит разработчикам страниц переключиться на новый иерархический элемент управления источником данных, чтобы добавить поддержку нового хранилища данных, не изменив код в методе GetSubscribers.
Затем метод GetSubscribers вызывает метод GetHierarchicalView класса HierarchyDataSourceView для доступа к иерархическим представлениям с заданным путем, таким как "/подписчики". Обратите внимание, что метод Select не асинхронен. Приложение передает данные, возвращаемые методом GetSubscribers, методу SendMail (см. рис. 15). Обратите внимание, что данные типы IHierarchicalEnumerable.
IHierarchicalEnumerable реализует IEnumerable, что означает, что он предоставляет метод GetEnumerator. Метод SendMail вызывает метод GetEnumerator для доступа к соответствующему объекту IEnumerator, который впоследствии используется для перечисления данных. IHierarchicalEnumerable также предоставляет метод с именем GetHierarchyData, который принимает перечисленный объект и возвращает объект IHierarchyData, связанный с ним.
Интерфейс IHierarchyData предоставляет важное свойство с именем Item, которое не является элементом данных. Метод SendMail использует метод Eval класса XPathBinder для оценки выражений XPath по объекту Item.
Рис. 10. Метод SendMail перечисляет данные, извлекает необходимые сведения и отправляет информационный бюллетень каждому подписчику.
void SendMail(IHierarchicalEnumerable data)
{
string firstName = String.Empty;
string lastName = String.Empty;
IEnumerator iter = data.GetEnumerator();
while (iter.MoveNext())
{
IHierarchyData ihdata = data.GetHierarchyData(iter.Current);
MailMessage message = new MailMessage();
message.From = "admin@subscribers.com";
message.To = XPathBinder.Eval(ihdata.Item, "@Email").ToString();
message.Subject = "NewsLetter";
firstName = XPathBinder.Eval(ihdata.Item, "@FirstName").ToString();
lastName = XPathBinder.Eval(ihdata.Item, "@LastName").ToString();
string mes = "Hi " + firstName + " " + lastName + ",<br/>";
mes += MessageBody.Text;
message.Body = mes;
message.BodyFormat = MailFormat.Html;
SmtpMail.SmtpServer = "MyServer";
SmtpMail.Send(message);
}
}
Заключение
Используя пошаговый подход, показывающий различные ASP.NET 2.0 и ADO.NET 2.0 инструменты и методы, в этой статье показано, как разработчики страниц могут писать универсальный код доступа к данным, который можно использовать для доступа к различным типам хранилищ данных.
д-р Шахрам Хосравиявляется старшим инженером по программному обеспечению с schlumberger Information Solutions (SIS). Шахрам специализируется на ASP.NET, веб-службах XML, технологиях .NET, технологиях XML, трехмерной компьютерной графике, hi/usability, шаблонах проектирования и разработке элементов управления сервера ASP.NET. У него более 10 лет опыта в объектно-ориентированном программировании. Он использует различные средства и технологии Майкрософт, такие как SQL Server и ADO.NET. Шахрам написал статьи о технологиях .NET и ASP.NET для журнала asp.netPRO.