Главная » Microsoft SQL Server, Базы данных » Поставщики данных

0

Программный код, использованный в следующем примере, можно найти на странице files.htmсайта книги.

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

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

Обычно файлы конфигурации загружаются только один раз, либо при запуске приложения, либо в момент первого доступа к данным. Однако иногда возникает необходимость коррекции режима работы приложения уже в процессе его работы, без перезапуска. В такой ситуации проблему может решить загрузчик параметров, который осуществляет мониторинг файла конфигурации. Класс PropertyLoader. vb может стать отличной стартовой точкой, и его можно найти на сайте книги. Для примера рассмотрим Web-приложение, работающее под управлением службы IIS, режим работы которого необходимо динамически корректировать. Данное приложение работает в среде высокой доступности, и его перезапуск для перезагрузки файла конфигурации недопустимо. Реализуя загрузчик параметров, регистрирующий изменения файла конфигурации и выполняющий в нужный момент его перезагрузку, полностью отпадает необходимость в использовании перезагрузки приложения. Все измененные значения заново загружаются и становятся доступными приложению в процессе его выполнения.

Поставщик данных представляет собой задействуемый клиентом класс, который абстрагирует реализацию источника данных. в своей реализации используют шаблон моста и полиморфизм. На рис. 54.3 показана модель поставщика данных с точки зрения абстрактного класса. На рис. 54.4 приведен контекстный пример использования поставщика данных Address (DpAddress) и необходимых классов поддержки.

Шаблон моста отделяет интерфейс от изменяющейся реализации. На практике На заметку это означает, что задействуемый клиентом класс не изменяет свой интерфейс.

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

I Конкретный класс DpDeterminer, SqlOrAccessDeterminer, реализует бизнес-логику для оп- ! ределения того, с какой базой данных следует взаимодействовать. Так как эта логика инкап- ] сулирована с параметрами конфигурации, ее идентифицирующими, ее можно вставить в любой класс, наследуемый от DpDeterminer и изменяющий бизнес-логику без перекомпиляции.

Абстрактный базовый класс DataProvider представляет собой ядро динамической идентификации и создания специфичной для источника данных реализации.

Рис. 54.4. В примере поставщика данных Address клиентский код ссылается только на класс DpAddress

Рис. 54.3. Этот пример поставщика данных демонстрирует базовые классы и требуемые файлы конфигурации

Абстрактный базовый класс Dp implement at ion реализует первый уровень иерархии реализации. Он содержит некоторые общие переменные и методы. От этого класса наследуются конкретные реализации.

Как работает поставщик данных

Поставщик данных формирует запрос к ассоциированному определителю и динамически создает в процессе работы специфичную для источника данных реализацию. Различные файлы конфигурации (см. рис. 54.3) обеспечивают гибкость поставщика и позволяют во время работы изменять информацию, необходимую для создания экземпляра класса, определяющего конкретную реализацию.

Класс Determiner играет критичную роль в модели поставщика данных, так как он отделяет бизнес-логику, определяющую, какой источник данных должен поддерживаться и как к нему подключаться. Это является основным отличием от моделей фабрики и DAO. Определитель существует сам по себе и может заменяться другим по мере необходимости. Схема, показанная на рис. 54.5, иллюстрирует фактический поток задач. Обратите внимание на то, что объекты SqlOrAccessDeterminer и Address создаются на основе базового класса DataProvider. Информация, необходимая этим объектам, содержится в файлах конфигурации. Также заметим, что при вызове функции Load (id) специфичный для базы данных объект Address запрашивает информацию о подключении к базе данных у объекта SqlOrAccessDeterminer. И, что более важно, клиентский код взаимодействует только с объектом DpAddress для загрузки и доступа к свойствам адреса. Таким образом, с точки

Puc. 54.5. Эта схема иллюстрирует создание поставщика данных Address, за чем следует загрузка соответствующих данных адреса и доступ к конкретным полям

зрения пользователя, DpAddress является не более чем обычным классом— с методами и свойствами. Сам доступ к базе данных при этом выполняется “за кулисами”.

В следующем программном коде и приведенном за ним примере реализации рассмотрим базовый класс DpDeterminer.

Базовый класс определителя

Public Mustlnherit Class DpDeterminer

Private Const BINDING_INFO_TAG As String = "_BindingInfo"

Private Const DESCRIPTION_TAG As String = "_Description" i i i_

111 Конструктор.

I I I___________________________________________________________

Public Sub New()

‘ Часть инициализации опущена. Важен конфигурационный файл,

‘ загружаемый для доступа к свойству…

1 Конфигурационный файл существует — загружаем его.

PropertyLoader.Instance.LoadProperties(strPropertyFile)

End Sub

i i i___________________________________________________

111 Информация о подключении и связывании для 11‘ текущего источника данных.

I I I____________________________________________________

Public Readonly Property Bindinglnfо() As String Get

Return PropertyLoader.Instance.GetProperty( _

Me.GetType.Name &  & Me.DataSource &

ВINDING_INFO_TAG)

End Get End Property

111 Источник данных для этого определителя.

I I I___________________________________________________________

Public MustOverride Readonly Property DataSource() As String i i i_______

111 Описание текущего источника данных.

I I I___________________________________________________________

Public Readonly Property Description() As String Get

Return PropertyLoader.Instance.GetProperty( _

Me.GetType.Name &  & Me.DataSource &

DESCRIPTION_TAG)

End Get End Property

Основная часть этого базового класса возвращает различные значения файла конфигурации в зависимости от типа определителя. Наибольший интерес представляет свойство MustOverride DataSource (). Этот метод, определенный в классе-наследнике, содержит бизнес-логику, определяющую тип источника данных, к которому осуществляется доступ. Рассмотрим пример:

SqlOrAccessDeterminer.vb

Public Class SqlOrAccessDeterminer Inherits DpDeterminer i i i_________

11‘ Внутренняя бизнес-логика, определяющая, какой тип ”1 источника данных будет использоваться.

I I I_________________________________________________

Public Overrides Readonly Property DataSourceO As String Get

With PropertyLoader.Instance If CBool(.GetProperty(Me.GetType.Name & "_UseSQL")) Then Return .GetProperty(Me.GetType.Name & "_SqlTag")

Else

Return .GetProperty (Me .GetType .Name & _

"_AccessTag")

End If End With End Get End Property End Class

Просматривая бизнес-логику, заложенную в классе SqlOrAccessDetermoner, обратите внимание на то, что если "_UseSQL" имеет значение True, то определитель возвращает строку "SqlTag", указывающую на использование базы данных SQL Server. В противном случае возвращается строка "AccessTag". Ключевая концепция заключается в том, что вся бизнес-логика была полностью отделена от создания объекта данных и теперь существует сама по себе. Подобная слабая связность повышает гибкость модели.

Именами файлов конфигурации, в соответствии с соглашением корпорации Назаметку Microsoft, являются <имя_сборки>.dll .conf ig. Поэтому при просмотре программного кода имейте в виду, что файлы конфигурации должны носить именно такие имена, например Providers.dll .conf ig или Provider Implementations .dll.config.

Файл конфигурации, на который ссылается SqlOrAccessDeterminer, выглядит следующим образом:

ProviderDeterminers.dll.config

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

<appSettings>

<!– Настройки определителя. –>

<add key="SqlOrAccessDeterminer_UseSQL" value="True" />

<1– The ‘tag’ values below are included in building a –>

<!– class instance namespace, i.e –>

<!– Providers.Implementations.Sql.Address or –>

<!– Providers.Implementations.Access.Address –>

<add key="SqlOrAccessDeterminer_SqlTag" value="Sql" />

<add key="SqlOrAccessDeterminer_AccessTag" value="Access" />

<!– Остальные настройки опущены… –>

</appSettings>

</configuration>

При изменении значения True ключа SqlOrAccessDeterminer_UseSQL на False изменяется база данных и все поставщики данных, использующие данный определитель, без необходимости перекомпиляции приложения. Это является существенным преимуществом, особенно в среде, в которой простои недопустимы.

Когда возникает необходимость создания загрузчика параметров для управле- Совет      ния настройками приложения, включайте в него функции мониторинга измене

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

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

файл конфигурации Providers. dll. conf ig

<?xml version="l. 0" encoding= "utf -8[3] ?>

<configuration>

<appSettings>

<!– Настройки реализации поставщика данных. –>

<!– Поставщик данных Address –>

<add key="DpAddress_Determiner"

value="Providers .Determiners . SqlOrAccessDeterminer11 />

<add key="DpAddress_Determiner_Assembly"

Value="ProviderDeterminers" />

<add key="DpAddress_Instance" value="Address" />

<add key="DpAddress_Instance_Assembly" value=11 Provider Implementations " / >

<add key="DpAddress_Instance_RootNameSpace" value="Providers.Implementations" />

</appSettings>

</conf igurat ion>

Строка DpAddress_Determiner указывает, какой определитель будет использовать данный поставщик данных при создании реализации источника данных. По этой причине любые изменения, которые влияют на логику определителя, будут воздействовать на все поставщики данных, зависящие от него. Также в раздел конфигурации включены значения, определяющие, в каких сборках следует искать определитель, а также значения, необходимые для создания экземпляра поставщика данных методом отражения. Теперь, когда вы увидели, как конфигурируется поставщик данных, взгляните на программный код. Начнем мы с базового класса DataProvider.

Класс DataProvider. vb

Public Mustlnherit Class DataProvider 1 Константы и локальные определения опущены

I I I_____________________________________________________

1 Используя новый определитель, получаем реализацию.

strAssembly = .GetProperty(Me.GetType.Name & _ INSTANCE_ASSEMBLY_TAG) strClass = .GetProperty(Me.GetType.Name & _ INSTANCEJROOT_NAME_SPACE) &

11.              [4] & _ Determiner .DataSource & 11. " & _ .GetProperty (Me.GetType.Name & INSTANCE_TAG)

1 Установка реализации для поставщика данных.

Implementation = СТуре(Activate(strAssembly,strClass),_ Dplmplementation) Implementation.Determiner = Determiner End With End Sub

i i i_____________________________________________________

1 1 1 Создание экземпляра затребованного класса 1 1 ‘ из заданной сборки.

I I I____________________________________________________

Private Function Activate(ByVal strAssembly As String, _

ByVal strClassName As String) As Object ‘ код проверки…

1 создание экземпляра с помощью отражения и его возвращение.

Dim instanceType As Type = _

System.Ref lection. Assembly .Load (strAssembly) .GetType ( _                 strClassName)

Return Activator.CreateInstance(instanceType)

End Function

1 Остальные общие методы поддержки опущены.

‘ Найти их можно на сайте книги.

End Class

Конструктор пытается загрузить файл конфигурации, а затем начинает динамически создавать экземпляры класса. Вначале создается определитель, связанный с поставщиком данных (определенный в файле конфигурации). Затем создается конкретная реализация источника данных (Access, SQL и т.п.), идентифицированная данным определителем. Код автоматически выполняется, когда поставщик данных идентифицируется. На этапе проектирования это освобождает разработчика от создания классов подцержки, и он может сосредоточить свое внимание на деталях поставщика данных. Поставщик данных Address приведен ниже.

Поставщик данных DpAddress. vb

Public Class DpAddress Inherits DataProvider Implements Iaddress 1 Этот класс только представляет реализацию свойств,

‘ определенных в интерфейсе IAddress. Помните, что этот 1 интерфейс также реализован в классах, специфичных 1 для источника данных.

I I I_________________________________________________________

1 1 1 Вспомогательный метод преобразует реализацию в ‘11 подходящий тип. Implementation — это свойство,

1 1‘ определенное в базовом классе DataProvider.

I I I_________________________________________________________

Protected Function Mylmplementation() As Iaddress Return CType(Implementation, IAddress)

End Function ‘ Вот пример свойства.

I I i_________________________________________________________

End Get

Set(ByVal Value As String)

MyImplementation.City = Value End Set End Property

‘ Все остальные свойства интерфейса представляются аналогичным 1 образом.

End Class

Являясь наследником класса DataProvider, класс DpAddress принимает всю функциональность создания. Реализуя интерфейс IAddress, класс DpAddress соглашается с общим контрактом, определяющим его возможности. Единственным дополнительным методом этого класса является My Implement at ion (), и он был введен для удобства разработчика и активизации функции IntelliSense программы Visual Studio. Теперь рассмотрим средства, обеспечивающие реализацию источника данных. Перед рассмотрением специфических особенностей реализации взглянем на базовый класс Dp Implement at ion. vb.

Базовый класс Dp Implementation, vb

i i i____________________________________________________________

11 Определитель i i i________________________________________________

Public Property Determiner() As DpDeterminer

1                                  Реализация крайне проста, поэтому она опускается.

End Property

I I I___________________________________________________________

11‘ Конструктор.

I I I___________________________________________________________

Public Sub New()

‘ Загрузка соответствующего файла конфигурации.

Dim strFileName As String

strFileName = My.Application.CurrentDirectory & "\" &

Me.GetType.Module.ScopeName & ".config"

‘ Загрузка файла свойств.

PropertyLoader.Instance.LoadProperties(strFileName)

End Sub End Class

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

В языке VB.NET 2005 было введено ключевое слово Му, обеспечивающее Новинка ^ удобный доступ к общим значениям запущенной программы и платформы, в ко-

2005  торой она установлена. Благодаря использованию ключевых слов му, Appli

cation, user и WebServices доступ к значениям становится намного легче.

В предыдущих версиях VB.NET, когда разработчику нужно было определить теку- / На заметку щий каталог, использовался метод AppDomain. CurrentDomain. BaseDirectory. –   Этот метод возвращал разделитель Path.DirectorySeparatorChar О , добав

ленный в конец строки. Новое свойство My.Application.CurrentDirectory() возвращает текущий каталог без добавления разделителя. Это позволяет сократить программный код, который ранее предполагал существование символа каталога в конце строки.

Ниже приведен базовый класс реализации, служащий основой всех классов, специфичных для баз данных.

Базовый класс ImplAddress. vb

Public Mustlnherit Class ImplAddress Inherits DpImplementation Implements Iaddress

Private m_CurrentValues As doAddress Private m_OriginalValues As doAddress 1 Этот класс реализует интерфейс Iaddress,

1 общий для всех реализаций, специфичных для 1 источников данных.

‘ Отдельно от объявлений объектов данных, приведенных выше,

1 в данном классе нет ничего особенного. При желании вы можете ‘ просмотреть полный программный код на сайте книги.

I                                                                                                                                                                                                                                                                                                                                                                                                          I I  

II                                   ‘ Загрузка определения интерфейса. Она должна применяться в 1 11 классах-наследниках.

I                                                                                                                                                                                                     I I  

Public MustOverride Sub Load(ByVal strld As String)

End Class

Класс ImplAddress обеспечивает общую функциональность всех реализаций, оставляя специфичные для источников данных функции конкретным реализациям. Все это можно увидеть в объявлении метода Load (…). Также отметьте для себя, что метод Load (…) был объявлен с атрибутом MustOverride. Таким образом, все дочерние классы должны реализовать собственную интерпретацию этого метода. Еще следует обратить внимание на объект данных doAddres. Этот объект хранит состояние класса Address, обращаясь к нему как к напоминанию (memento).

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

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

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

Рассмотрим специфичный для источника данных класс Address.

Address .vb — реализация SQL Server

11                                                                               

1 1 Это реализация SQL Server.

! I_________________________________________________________________

Public Class Address Inherits ImplAddress i i i_________________________

111             Фактическая функция загрузки реализации SQL Server

I i i_______________________________________________________________

Public Overrides Sub Load(ByVal strld As String)

Dim cmd As SqlCommand = Nothing Dim conn As SqlConnection = Nothing Dim dataReader As SqlDataReader = Nothing Try

1 установка подключения к базе данных с помощью 1 информации определителя.

conn = New SqlConnection(Me.Determiner.Bindinglnfо) conn.Open()

1 Настройка команды.

cmd = New SqlCommand( PropertyLoader.Instance.GetProperty( _ "Sql_Address_Select" ) .Replace ("%111, _ strld.ToString) , conn) dataReader = cmd.ExecuteReader( _

CommandBehavior.CloseConnection)

If dataReader.Read Then

Me.Streetl = MySqlHelper.GetStringValue( _ dataReader, 11 Street 111, "<no value>")

Me.Street2 = MySqlHelper.GetStringValue( _ dataReader, 11 Street2", "<no value>")

1 Аналогичный программный код используется для инициализации 1 города, штата, индекса и т.д.

End If

Me.SetOriginalValues()

Catch ex As Exception 1 Здесь выполняется обработка.

Throw(ex)

Finally 1 Очистка

If cmd IsNot Nothing Then cmd.Dispose If conn IsNot Nothing Then conn.Close()

If dataReader IsNot Nothing Then dataReader.Close()

End Try End Sub End Class

Поскольку класс Address является дочерним для класса ImplAddress, он наследует полный интерфейс и не обязан беспокоиться об определении методов установки и проверки свойств отдельных полей. Вместо этого данный класс фокусирует свое внимание на специфике взаимодействия с базой данных SQL Server и наполнении соответствующего объекта данных. То же справедливо и для класса Address, предназначенного для доступа к базам данным Access.

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

Клиентский программный код в следующем примере использует именно такой шаблон. Сравните его с программным кодом фабрики и DAO, представленными ранее.

Поставщик данных MainTester

Public Class MainTester

‘ ‘ ‘ Этот метод тестирует поставщика данных.

1 I 1__________________________________________________________

Sub Main()

Try

Dim objAddr As New Address objAddr.Load("1" )

Call PrintAddress(objAddr)

Catch ex As Exception

Console.WriteLine(ex.Message)

End Try End Sub End Class

Этот клиентский тестовый программный код даже меньше по размеру, чем используемый для работы с фабрикой, к тому же в данном случае нет никакой необходимости в преобразовании объекта. На самом деле данному клиентскому коду даже не нужно знать, что для доступа к источникам данных используются разные классы. Если в решении имеет значение компактность клиентского программного кода, то следует использовать подход с применением поставщика данных. Если сравнить его с подходом DAO, то в данном клиентском коде существуют три строки вместо семи, к тому же в нем более интуитивно понятна работа с объектом данных Address, поскольку в нем нет отличий от стандартного кода, в частности, используется команда Dim strSomeString As String.

Достоинства шаблона поставщика данных

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

Недостатки шаблона поставщика данных

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

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

Резюме

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

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

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

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

Источник: Нильсен, Пол. Microsoft SQL Server 2005. Библия пользователя. : Пер. с англ. — М. : ООО “И.Д. Вильямс”, 2008. — 1232 с. : ил. — Парал. тит. англ.

По теме:

  • Комментарии