Visual Studio 2005 и SQL Server 2005: лучше вместе

Источник: Windows IT Pro, #08/2005
Уильям Шелдон

Повторная задержка выхода SQL Server 2005 и Visual Studio 2005 предоставила администраторам баз данных (DBA) и разработчикам дополнительное время для того, чтобы поработать с бета-версиями обоих продуктов, а также осмыслить то, как обновленные инструментальные средства могут изменить сам процесс создания приложений. Я очень рекомендую начать использовать новые возможности при проектировании и планировании приложений прямо сейчас, не дожидаясь выхода официальных версий. Это позволит получить приложения, которые сразу смогут использовать все преимущества, обеспечиваемые новыми продуктами.

Теперь в распоряжении разработчиков появилась масса новых возможностей, поэтому перед клиентами может возникнуть проблема выбора: что задействовать первую очередь? Позволю себе обратить внимание читателей на три компонента расширения SQL Server 2005 и Visual Studio 2005, которые были разработаны специально для обеспечения эффективного и надежного совместного функционирования этих двух продуктов. Я имею в виду новый компонент Multiple Active Result Sets (MARS), интегрированную среду .NET Common Language Runtime (CLR), а также обновление пространства имен транзакций (transaction namespace). Каждое из этих расширений по-своему влияет на процесс разработки приложений. В частности, MARS обеспечивает улучшение масштабируемости за счет снижения количества необходимых соединений, интегрированная среда CLR предоставляет разработчикам и DBA возможности безопасного расширения функциональности их баз данных и, наконец, обновленное пространство имен транзакций превращает автоматические транзакции в действительно автоматические. Эти инструменты создавались для того, чтобы разработчики могли строить приложения с еще более гибкими возможностями, а администраторы DBA, в свою очередь, могли осуществлять поддержку приложений, используя обновленные возможности баз данных, а не просто манипулируя набором строк. Давайте рассмотрим, что дает нам каждая из этих новых возможностей для создания современных приложений.

Рассмотренные в статье примеры работают с Visual Studio 2005 Beta 2 и SQL Server 2005 Beta 3 и используют тестовую базу данных Adventure Works, которая поставляется вместе с SQL Server 2005. Для упрощения настройки строки соединения SQL Server 2005 и Visual Studio 2005 были установлены на один и тот же компьютер. Для того чтобы сделать эти примеры более функциональными, я приведу несколько хранимых процедур и некоторые дополнительные данные. Все это можно установить на стандартную базу AdventureWorks, воспользовавшись программой, код которой показан в листинге 1.

Листинг 1. Хранимая процедура для AdventureWorks

 USE AdventureWorks
GO
CREATE PROCEDURE usp_Product_UpdatePrice
(
   @ProductID int,
   @ListPrice money
)
AS
UPDATE Production.Product
SET ListPrice = @ListPrice
WHERE ProductID = @ProductID;
GO
CREATE PROCEDURE usp_Product_SelectPrice
AS
SELECT TOP 10 P.ProductID, P.ListPrice
FROM Production.Product AS P
WHERE P.ProductID > 440
ORDER BY P.ProductID

Путешествие на MARS

Одним из самых примечательных новшеств в SQL Server 2005 является MARS (это и одна из лучших аббревиатур, придуманных в Редмонде за последнее время). Основная идея MARS состоит в том, чтобы пользователи не были ограничены необходимостью последовательно вводить команды в ходе соединения с базой данных. Вместо этого соединение должно допускать одновременную обработку нескольких команд. Теперь, с появлением MARS, разработчики в рамках одного соединения могут использовать несколько команд для работы с активной базой данных или (что более важно) иметь несколько результирующих наборов данных или курсоров на стороне сервера, работающих одновременно.

Самое интересное, что эта новая функциональность не требует установки сервера (хотя необходимо изменение подхода разработчика к использованию команд для реляционных баз данных). Несколько досадно, правда, что применение этих возможностей ограничивается только средой SQL Server 2005, поэтому, если в компании разрабатывается некий программный код с использованием парадигмы MARS, следует понимать, что база данных при этом должна находиться в SQL Server 2005.

Преимущества MARS касаются не только производительности и масштабируемости: при использовании этой технологии упрощается и код разрабатываемых программных средств. На листинге 2 меткой A обозначен фрагмент кода, создающий один объект соединения (connection), который затем использует команды чтения (reader) и обновления (update). Когда выполняется операция чтения и извлекаются данные по каждой строке, для них сразу же выполняется операция обновления. Весь этот процесс происходит в пределах одного соединения. Если попытаться одновременно организовать доступ к данным и их обновление в среде Visual Studio 2003 или SQL Server 2000, важно убедиться, что этот тип простого последовательного обновления, основанный на использовании курсоров на стороне сервера, работать не будет. Необходимо также учесть, что, если вы захотите откомпилировать код листинга 2 в Visual Studio 2003, перед этим из него следует убрать транзакционную логику, свойственную Visual Studio 2005.

Листинг 2. Проект Visual Basic Command Window

'Данные примеры кода создаются в контексте проекта Visual Basic Command 'Window. Чтобы запустить приведенный образец кода, 
'нужно создать в Visual Studio новый проект Command Window и добавить в него ссылки, содержащиеся в 
'показанных ниже операторах Imports. Затем следует скопировать и вставить 
'в исходный файл Module1.vb содержащийся здесь код.
'(Учтите, что наличие символов принудительного переноса текста 
'на следующую строку в сочетании с режимом автопереноса может стать причиной ошибок в процессе копирования.)
Imports System.Transactions
Imports System.Data.SqlClient
Module Module1
    Sub Main()
        'Нужно снять символ комментария со следующей строки, чтобы
        'поставить My.User в соответствие с локально зарегистрировавшимся
         в системе пользователем
        ' My.User.InitializeWithWindowsUser
BEGIN CALLOUT A
        Dim myTran As New System.Transactions.CommittableTransaction()
        Dim myConn As New System.Data.SqlClient.SqlConnection
		("server=(local);database=AdventureWorks;Integrated Security=true")
        Try
            Dim myCommand As SqlCommand = myConn.CreateCommand()
            myCommand.CommandText = "usp_Product_SelectPrice"
            myCommand.CommandType = CommandType.StoredProcedure
            Dim updateCommand4 As SqlCommand = myConn.CreateCommand()
            myConn.Open()
            myConn.EnlistTransaction(myTran)
            Dim myReader As SqlDataReader = myCommand.ExecuteReader()
            'Loop through each item in the reader and increase the price by 20% + $1.00
            While myReader.Read()
                DoCommand(updateCommand4, myReader.GetInt32(0),  _
				(myReader.GetSqlMoney(1) * 0.2) + 1)
            End While
            myReader.Close()
            myTran.Commit()
        Catch ex As Exception
            ' An error means the transaction can't commit
            myTran.Rollback()
        Finally
            myConn.Close()
        End Try
END CALLOUT A
BEGIN CALLOUT B
        Dim myCommitableTran As New System.Transactions.CommittableTransaction()
        Dim myConnection As New 
System.Data.SqlClient.SqlConnection("server=(local);database=AdventureWorks;Integrated 
Security=true")
        Try
            myConnection.Open()
            myConnection.EnlistTransaction(myCommitableTran)
            Dim updateCommand As SqlCommand = myConnection.CreateCommand()
            DoCommand(updateCommand, 444, 0.54)
            myCommitableTran.Commit()
        Catch ex As Exception
           'Ошибка указывает на невозможность завершения транзакции
            myCommitableTran.Rollback()
        Finally
            myConnection.Close()
        End Try
END CALLOUT B
BEGIN CALLOUT C
        Using myTranScope As New System.Transactions.TransactionScope()
            Dim myConnection2 As New System.Data.SqlClient.SqlConnection
			("server=(local);database=AdventureWorks;Integrated Security=true")
            'Dim myConnection3 As New System.Data.SqlClient.SqlConnection
			("server=;database=AdventureWorks;Integrated Security=true")
            Try
                Dim updateCommand2 As SqlCommand = myConnection2.CreateCommand()
                myConnection2.Open()
                DoCommand(updateCommand2, 444, 1.05)
                'Dim updateCommand3 As SqlCommand = myConnection3.CreateCommand()
                'DoCommand(updateCommand3, 445, 1.04)
                myTranScope.Complete()
            Catch ex As Exception
           'Ошибка указывает на невозможность завершения транзакции
                myTranScope.Dispose()
            Finally
                myConnection2.Close()
                'myConnection3.Close()
            End Try
        End Using
END CALLOUT C
    End Sub
    Sub DoCommand(ByVal updateCommand As SqlCommand, ByVal productID As Integer, 
ByVal price As Double)
	  ' неэффективно, но в данном примере позволяет сократить объем повторяющегося кода
        updateCommand.CommandText = "usp_Product_UpdatePrice"
        updateCommand.CommandType = CommandType.StoredProcedure
        updateCommand.Parameters.Clear()
        Dim productIDParameter As New SqlParameter("@ProductID", SqlDbType.Int)
        updateCommand.Parameters.Add(productIDParameter)
        Dim listPriceParameter As New SqlParameter("@ListPrice", SqlDbType.Money)
        updateCommand.Parameters.Add(listPriceParameter)
        productIDParameter.Value = productID
        listPriceParameter.Value = price
        updateCommand.ExecuteNonQuery()    End Sub
End Module

CLR в SQL Server

Если MARS представляет собой расширение, по поводу которого было не слишком много шума и которое не требует настройки, то CLR - совсем другое дело. Интеграция CLR в SQL Server 2005 была, пожалуй, наиболее спорным вопросом, касающимся расширения SQL Server. Но в то же время это, наверное, одно из самых значительных усовершенствований. Основные сомнения по поводу CLR были связаны с тем, чтобы понять, когда его использовать, а когда нет.

Необходимо отметить, что CLR в SQL Server не является заменой T-SQL. И хотя из маркетинговой информации Microsoft следует, что теперь нужно использовать только языки .NET, на презентациях для технических специалистов нередко говорят о том, что в большинстве запросов применение T-SQL пока является более эффективным, чем использование CLR. С помощью этих двух, казалось бы, противоположных позиций техническая служба Microsoft хочет дать четкие рекомендации о том, как правильно использовать CLR в SQL Server 2005, а не внушить клиентам опасения по поводу CLR.

По сравнению с T-SQL, язык CLR имеет ряд специфических преимуществ. Первое из них заключается в возможности создания функций, которые производят интенсивную обработку. С помощью CLR можно создавать функции, которые инкапсулируют некоторые бизнес-вычисления внутри базы данных. Если такие функции используются именно для вычислений (а не для извлечения данных), то в этом случае использование CLR обеспечит выигрыш в производительности относительно T-SQL. Еще одним преимуществом применения CLR в SQL Server является то, что он может обрабатывать сложные конструкции XML. Кроме того, CLR позволяет создавать нестандартные типы, определяемые пользователем (User Defined Type, UDT). И последнее: использование CLR позволит отказаться от существовавших ранее и небезопасных расширенных хранимых процедур. В отличие от расширенных хранимых процедур, выполнение которых могло выходить из-под контроля SQL Server, CLR по умолчанию настроен так, что гарантируется выполнение используемых расширений T-SQL в текущем контексте безопасности. А сейчас рассмотрим, как можно создавать нестандартные UDT для дальнейшего использования в приложении.

Поскольку для нормального функционирования баз данных не требуются какие-либо механизмы CLR, по умолчанию в SQL Server 2005 компонент CLR отключен. Поэтому, для того чтобы задействовать возможности CLR, прежде всего его нужно включить. Для этого требуется открыть окно Query в SQL Management Studio и перенести туда приведенную ниже команду запуска стандартной хранимой процедуры T-SQL sp_configure.

sp_configure 'CLR Enabled', 1
GO
RECONFIGURE
EXEC sp_configure

Данная инструкция T-SQL включает механизм CLR и возвращает текущие параметры конфигурации базы данных. Более подробная информация о команде sp_configure содержится в документации Microsoft.

После включения CLR можно приступать к созданию UDT с помощью Visual Studio 2005. Соответствующий код может быть реализован как на Visual Basic, так и на C#, в данном примере я воспользовался Visual Basic. Сначала создается новый проект, для этого из списка типов проектов Visual Basic выбирается шаблон SQL Server Project. После присвоения проекту имени (я назвал его SQLServerUDT) Visual Studio попросит добавить ссылку на базу данных (соответствующее диалоговое окно приведено на экране 1). В данном диалоговом окне необходимо указать имя провайдера (в нашем примере требуется .NET Framework Data Provider for SQL Server). Затем в диалоговом окне New Database Reference (экран 2) нужно указать базу данных соответствующего проекта. Ввод значения local в текстовое поле Server name указывает на локальный SQL Server 2005, установленный на данном компьютере, который Visual Studio затем ассоциирует с соответствующим проектом.

 
Экран 1. Добавление ссылки на базу данных  
 
Экран 2. Выбор базы данных для проекта  

Итак, новый проект создан, теперь можно добавить класс, представляющий наш UDT. Следует щелкнуть на проекте правой кнопкой мыши и выбрать из контекстного меню пункт Add User Defined Type. Откроется новое диалоговое окно, содержащее несколько типов классов, которые могут быть добавлены в данный проект. Необходимо выбрать установленный по умолчанию класс User Defined Type и изменить заданное по умолчанию имя файла на myPointType.vb. После нажатия OK будет сгенерирован код, показанный в листинге 3.

Листинг 3. Сгенерированный шаблон myPointType

Imports System
Imports System.Data
Imports System.Data.Sql
Imports System.Data.SqlTypes
Imports System.Data.SqlServer
<Serializable()> _
<SqlUserDefinedType(Format.Native)> _
Public Structure myPointType
    Implements INullable
    Public Overrides Function ToString() As String
        ' Введите сюда свой код
        Return ""
    End Function
    Public ReadOnly Property IsNull() As Boolean Implements INullable.IsNull
        Get
            ' Введите сюда свой код
            Return m_Null
        End Get
    End Property
    Public Shared ReadOnly Property Null() As myPointType
        Get
            Dim h As myPointType = New myPointType
            h.m_Null = True
            Return h
        End Get
    End Property
    Public Shared Function Parse(ByVal s As SqlString) As myPointType
        If s.IsNull Then
            Return Null
        End If
        Dim u As myPointType = New myPointType
        ' Введите сюда свой код
        Return u
    End Function
    ' Это метод символа-заполнителя
    Public Function Method1() As String
        ' Введите сюда свой код
        Return "Hello"
    End Function
    ' Это статический метод символа-заполнителя
    Public Shared Function Method2() As SqlString
        ' Введите сюда свой код
        Return New SqlString("Hello")
    End Function
    ' Это значение поля символа-заполнителя
    Public var1 As Integer
    ' Приватный член
    Private m_Null As Boolean
End Structure

Здесь видно, что код, сгенерированный для данного класса используемым по умолчанию шаблоном, содержит в самом низу описания частных (private) свойств этого нового класса. Я не буду здесь подробно останавливаться на том, как реализовать указатель типа, достаточно знать, что он состоит из двух целых чисел, которые с помощью данного UDT могут быть сохранены как одно значение. В листинге 4 показано, как выглядит исходный файл myPointType.vb после того, как указателю типа было дано это простое определение. Обратите внимание, что стандартная реализация дополнена компонентом Regions, который допускает свертывание, таким образом, можно группировать связанные друг с другом части при реализации данного типа.

Листинг 4. Обновленный myPointType

Imports System
Imports System.Data
Imports System.Data.Sql
Imports System.Data.SqlTypes
Imports System.Data.SqlServer
<Serializable()> _
<SqlUserDefinedType(Format.Native)> _
Public Structure myPointType
    Implements INullable
#Region "Private Values"
    ' Это значение поля символа-заполнителя
    Private m_xAxis As Integer
    Private m_yAxis As Integer
    ' Приватный член 
    Private m_Null As Boolean
#End Region
#Region "Constructors"
    Public Sub New(ByVal xAxis As Integer, ByVal yAxis As Integer)
        m_xAxis = xAxis
        m_yAxis = yAxis
        m_Null = False
    End Sub
    Private Sub New(ByVal isnull As Boolean)
        m_Null = isnull
        m_xAxis = 0
        m_yAxis = 0
    End Sub
#End Region
#Region "Public Properties"
    ''' <summary>
    ''' Обеспечивает для точки доступ по оси x
    ''' </summary>
    ''' <value></value>
    ''' <remarks></remarks>
    Public Property xAxis() As Integer
        Get
            Return m_xAxis
        End Get
        Set(ByVal value As Integer)
            m_xAxis = value
        End Set
    End Property
    ''' <summary>
    ''' Обеспечивает для точки доступ по оси y
    ''' </summary>
    ''' <value></value>
    ''' <remarks></remarks>
    Public Property yAxis() As Integer
        Get
            Return m_yAxis
        End Get
        Set(ByVal value As Integer)
            m_yAxis = value
        End Set
    End Property
    ''' <summary>
    ''' Показывает, имеет ли текущая величина значение null.
    ''' </summary>
    ''' <value></value>
    ''' <remarks></remarks>
    Public ReadOnly Property IsNull() As Boolean Implements INullable.IsNull
        Get
            ' Введите сюда свой код
            Return m_Null
        End Get
    End Property
    ''' <summary>
    ''' Данный метод возвращает экземпляр UDT, который является null.
    ''' </summary>
    ''' <value></value>
    ''' <remarks></remarks>
    Public Shared ReadOnly Property Null() As myPointType
        Get
            Dim h As myPointType = New myPointType
            h.m_Null = True
            Return h
        End Get
    End Property
#End Region
#Region "Public Shared Functions"
    ''' <summary>
    ''' Метод пытается преобразовать величину в новую точку и возвращает корректный экземпляр
    ''' При поддержке нескольких преобразований данный способ может оказаться весьма ресурсоемким.
    ''' В настоящем примере я ограничился реализацией только для случая формата 'ToSTring()'
    ''' </summary>
    ''' <param name="s"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function Parse(ByVal s As SqlString) As myPointType
        If s.IsNull Then
            Return Null
        End If
        Dim str As String = s.ToString()
        ' Теоретически, для того чтобы быть уверенным, что строка отформатирована "(#,#)",
        ' здесь требуется более сложная обработка ошибок
        Dim intComma As Integer
        intComma = str.IndexOf(",")
        Dim u As myPointType = New myPointType
        ' Введите сюда свой код
        Return u
    End Function
#End Region
#Region "Public Instance Methods"
    ''' <summary>
    ''' Аннулирует используемое по умолчанию выполнение ToString, возвращая для данного значения точки строковое представление
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Overrides Function ToString() As String
        If m_Null Then
            Return ""
        End If
        Return "(" + m_xAxis.ToString() + "," + m_yAxis.ToString() + ")"
    End Function
    'В данной области также могли бы содержаться общие методы возврата значения точки .NET нестандартного Point UDT, 
'который имеется в используемой версии базы данных ' Это метод символа-заполнителя 'Public Function Method1() As String ' ' Введите сюда свой код ' Return "Hello" 'End Function '' Это статический метод символа-заполнителя 'Public Shared Function Method2() As SqlString ' ' Введите сюда свой код ' Return New SqlString("Hello") 'End Function #End Region End Structure

Теперь, когда у нас есть созданный обновленный класс, следующий шаг - компиляция данного класса. В результате будет создан файл c расширением .DLL, доступный для любого из проектов, в котором используется ссылка на созданный нами тип, но для этого сначала нужно разместить новый тип в SQL Server. Это можно сделать двумя способами. Для разрабатываемой базы данных (но не для промышленно эксплуатируемой) можно использовать функцию Deploy пакета Visual Studio. Для этого нужно перейти в меню Build, выбрать пункт Deploy, и Visual Studio автоматически скомпилирует ваш UDT и разместит его в той базе данных SQL Server, на которую была ссылка.

Второй метод размещения UDT пригоден как для разрабатываемых, так и для промышленных баз данных и состоит в использовании команд T-SQL. Последовательность команд T-SQL, используемая для установки созданного UDT на SQL Server, приведена в листинге 5. Здесь указан путь к каталогу, соответствующий моему локальному компьютеру, перед запуском команд на другой машине необходимо соответствующим образом изменить описание пути. Для того чтобы распознавать данный тип, SQL Server и имеющееся приложение должны ссылаться на эту сборку.

Листинг 5. Сценарий T-SQL для установки myPointType

USE AdventureWorks;
GO
-DROP TYPE myPointType
-DROP ASSEMBLY myPointType
CREATE ASSEMBLY myPointType
FROM 'C:\Documents and Settings\Bills\My Documents\Visual Studio 
2005\Projects\SqlServerUDT\SqlServerUDT\bin\SQLServerUDT.dll'
WITH permission_set = SAFE;
GO
CREATE TYPE myPointType
EXTERNAL NAME [myPointType].[SqlServerUDT.myPointType];
GO

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

' Создание параметра для типа UDT.
Dim paramUDT = 
updateCommand.Parameters.Add("@v",
 SqlDbType.Udt)
paramUDT.UdtTypeName = "myPointType";
paramUDT.Value = new myPointType(4, 22);

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

Усовершенствование механизма распределенных транзакций

При работе с .NET и подключении к SQL Server с помощьюVisual Studio более ранней версии, чем 2005, и одновременном требовании, чтобы транзакция затрагивала несколько источников данных (этот механизм также известен как автоматическая транзакция), придется сначала разместить компоненты .NET в пространстве имен Enterprise Services .NET, а затем интегрировать сборки .NET с COM+. С выпуском Visual Studio 2005 этот процесс упростился, что обусловлено появлением нового пространства имен, называемого System.Transactions. (Данное пространство имен описывается в документации Microsoft, доступной по адресу: http://msdn2.microsoft.com/library/ system.transactions.aspx или в MSDN в разделе .NET Framework Class Library.) Для управления транзакциями разработчики могут использовать три основных класса пространства System.Transactions. К ним относятся Transaction, TransactionScope и TransactionManager.

В среде .NET 1.1 для вызова метода BeginTransaction используется объект Connection. Если задействовать этот метод в случае соединения с SQL Server, то объект Connection возвратит объект System.Data.SqlClient.SqlTransaction. Этот объект затем может быть ассоциирован с каждой из команд, являющейся составной частью данной транзакции. Повторные вызовы, выполняемые SQL Server, все равно производят неявные транзакции, поэтому объект, соответствующий транзакции, нужен лишь для того, чтобы объединить в одну группу несколько команд.

Когда вызывается метод Begin Transaction, ADO.NET использует для обращения к SQL Server существующее соединение и запускает на исполнение команду T-SQL Begin Transaction. Таким образом, чтобы использовать метод BeginTransaction, нужно последовательно обращаться к одному и тому же объекту, соответствующему соединению, для каждой команды, являющейся частью данной транзакции, причем данный тип транзакций несовместим с транзакциями COM+.

Первое, что следует уяснить относительно использования пространства имен System.Transactions, - это то, что можно по-прежнему создавать в SQL Server ручные транзакции и не прибегать к методу BeginTransaction. Созданная транзакция может по-прежнему считаться ручной, поскольку клиент не только программирует код ее начала и завершения, но и ассоциирует транзакцию с каждой из составляющих ее команд.

В Visual Studio 2005 появился объект System.Transactions.Transaction, с помощью которого можно вручную включать в транзакцию необходимые команды. В листинге 2 меткой B отмечен фрагмент кода, представляющий собой пример создания вручную объекта System. Transactions. CommittableTransaction. Объект CommittableTransaction представляет собой одну из реализаций виртуального базового класса Transaction. Сначала создается объект Connection, затем устанавливается соединение. После того как соединение с базой данных установлено, объект Connection использует объект CommitableTransaction для формирования транзакции. Эта точка является началом транзакции; начиная с нее можно выполнить одну или несколько команд работы с базой данных, которые будут являться частью данной транзакции. И наконец, в момент завершения обращения к базе данных мы можем либо выполнить (commit) либо отменить (roll back) нашу транзакцию.

На первый взгляд возможность ручной привязки нового объекта транзакции к соединению мало чем отличается от существующей логики транзакций, но на самом деле здесь в корне меняется сама парадигма транзакции. Код, обозначенный меткой B, создает действительно распределенную транзакцию, для чего в Visual Studio 2003 потребовалось бы использование Enterprise Services. А в рассматриваемом нами проекте нет ссылок на пространство имен System.EnterpriseServices. В Visual Studio 2005 для создания транзакций, использующих несколько источников данных, в раздел ссылок проекта необходимо включить только библиотеку System.Transactions. А если нет нужды обращаться к Enterprise Services, это означает, что код программ упрощается и нет необходимости интегрировать свою сборку с COM+. Теперь, находясь в среде .NET, можно создавать распределенные транзакции, использующие несколько соединений. Также необходимо помнить, что до завершения транзакции объект, соответствующий соединению, должен оставаться открытым (хотя он может быть при этом в «спящем» (dormant) состоянии), что иллюстрирует последовательность команд фрагмента кода рассматриваемого примера.

Конечно, с появлением объекта System.Transactions.Transaction процедура управления распределенными транзакциями упростилась, однако разработчики Visual Studio 2005 пошли еще дальше и предоставили нам инструментальные средства для автоматизации распределенных транзакций. Объект TransactionScope дает возможность автоматически ассоциировать с текущей транзакцией каждый объект соединения в пределах установленных границ (scope). Как видно из примера с меткой C в листинге 2, код с использованием объекта TransactionScope выглядит даже несколько проще, чем код для ручного создания объекта System.Transaction.Transaction. Код, обозначенный меткой C, начинается с нового элемента языка Visual Basic - команды Using. Объект TransactionScope всегда следует создавать в контексте команды Using, это гарантирует точное определение границ транзакции. После этого можно создать и установить столько соединений, сколько требуется, и каждая команда, выполняемая в контексте границ активной транзакции, будет автоматически ассоциирована с данной транзакцией.

В ходе выполнения фрагмента, обозначенного меткой C, создается несколько транзакций, но, поскольку в рассматриваемом примере используется только один источник данных, я оставил активным лишь одно соединение. В данном примере сначала устанавливается соединение и выполняется команда Update. На следующем шаге устанавливается статус транзакции. Обратите внимание на разницу между установкой статуса и выполнением транзакции. Когда пользователь работает с объектом TransactionScope, транзакция не будет выполняться до тех пор, пока программа не дойдет до предложения End Using. Вызовы методов Complete или Dispose на самом деле не завершают и не откатывают транзакцию. Они просто указывают на то, что произойдет, когда будет достигнута граница TransactionScope. Если вместо объекта Transaction используется объект TransactionScope, то в этом случае отпадает необходимость в ассоциировании различных соединений с данной транзакцией, это автоматически сделает .NET.

Третьим основным объектом пространства имен System.Transactions является объект TransactionManager. Данный объект проектировался не столько для обработки транзакций, сколько для предоставления разработчикам интерфейса к координатору распределенных транзакций, distributed-transaction coordinator. Описание работы с этим объектом выходит за рамки данной статьи, скажу лишь, что назначение этого объекта состоит в том, чтобы предоставить разработчикам набор статических методов, которые они могли бы использовать для доступа к одному или нескольким менеджерам транзакций. Таким образом, теперь разработчики могут регистрировать в системе утилиты управления транзакциями, отличные от тех, которые в .NET имеются по умолчанию, а это в свою очередь означает, что теперь можно расширить технологию координирования транзакций за рамки COM+.

Пора за работу

В данной статье мы рассмотрели только три новые возможности, которыми можно пользоваться при работе с SQL Server 2005 и Visual Studio 2005. В этих продуктах имеется еще масса механизмов для расширения функциональности создаваемых приложений. К ним относятся возможность работы с XML, создание по-настоящему асинхронных запросов к базе данных и усовершенствованная технология обработки ошибок. Одним словом, независимого от того, кто вы - разработчик или администратор, настало время начинать работать с этими продуктами. В качестве основы для новых разработок уже можно использовать финальные версии SQL Server 2005 и Visual Studio 2005.

Уильям Шелдон - Ведущий инженер компании InterKnowlogy, имеет сертификаты MCSD и MCP+SiteBuilding

 

Сертификационные экзамены Microsoft по SQL Server 2005:

 


Страница сайта http://test.interface.ru
Оригинал находится по адресу http://test.interface.ru/home.asp?artId=4257