Книги, научные публикации Pages:     | 1 | 2 | 3 | 4 |   ...   | 6 |

том1 а л ь м а н а х программиста Тематический сборник материалов MSDN Library и MSDN Magazine Microsoft ADO.NET ...

-- [ Страница 2 ] --

Руководство по архитектуре доступа к данным на платформе.NET try { conn.OpenO;

SqlDataReader reader = cmd.ExecuteReaderf);

reader. ReadO;

// читаем единственную запись // Возвращаем выходные параметры из полученного потока данных ProductName = reader. GetString(O);

UnitPrice = reader. GetDecimal(l);

reader. Closef);

} catch { throw;

} finally { conn.CloseO;

Для чтения одной записи с помощью объекта SqlDataReader выполните следующие действия.

1. Создайте объект SqlCommand.

2. Откройте соединение.

3. Вызовите метод ExecuteReader объекта SqlCommand, чтобы получить объект SqlDataReader.

4. Считайте выходные параметры типизированными методами-аксессора ми объекта SqlDataReader (в данном случае Ч GetString и GetDecimal).

В предыдущем фрагменте кода вызывается следующая хранимая процедура, CREATE PROCEDURE DATGetProductDetailsReader &ProductID int AS SELECT ProductName, UnitPrice FROM Products WHERE ProductlD = @ProductID GO Использование ExecuteScalar для чтения одного поля Метод ExecuteScalar предназначен для запросов, возвращающих только одно значение. Если запрос возвращает несколько полей и/или записей, ExecuteScalar вернет для этого запроса лишь первое поле первой записи.

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

72 Microsoft ADG.NET void GetProductNameExecuteScalar{ int ProductID, out string ProductName ) { SqlConnection conn = new SqlConnection( "server=(local);

Integrated Security=SSPI;

database=northwind");

SqlCommand cmd = new SqlCommand("LookupProductNameScalar", conn );

cmd.CommandType = CommandType.StoredProcedure;

cmd.Parameters.Add("@ProductID", ProductID );

try { conn.0pen();

ProductName = (string)cind. ExecuteScalar();

} catch { throw;

} finally { conn.Close();

} ;

Чтобы считать одно поле через ExecuteScalar, выполните следующие действия.

1. Создайте объект SqlCommand для вызова хранимой процедуры.

2. Откройте соединение.

3. Вызовите метод ExecuteScalar. Обратите внимание, что он возвращает значение первого считанного поля как объектный тип, и вы должны привести его к нужному типу.

4. Закройте соединение.

В приведенном выше фрагменте кода используется эта хранимая процедура.

CREATE PROCEDURE LookupProductNameScalar йProductID int AS SELECT TOP 1 ProductName FROM Products WHERE ProductID = tProductID GO Руководство по архитектуре доступа к данным на платформе.NET Использование возвращаемого значения или выходного параметра хранимой процедуры для чтения одного поля Вы можете получить единственное значение через возвращаемое значение или выходной параметр хранимой процедуры. В следующем коде демон стрируется вариант с выходным параметром.

void GetProductNaraeUsingSPOutpuU int ProductID, out string ProductName ) { SqlConnection conn = new SqlConnection( "server=(local);

Integrated Security=SSPI;

database=northwind");

SqlCommand cmd = new SqlCommandC'LookupProductNameSPOutput", conn );

cmd.CommandType = CommandType.StoredProcedure;

SqlParameter paramProdID = cmd.Parameters.Add("зProductID", ProductID );

ParamProdlD.Direction = ParameterDirection.Input;

SqlParameter paramPN = cind.Parameters.Add("@>ProductName", SqlObType.VarChar, 40 );

paramPN.Direction = ParameterDirectlon.Output;

try I conn.OpenO;

cmd.ExecuteNonQue ry();

ProductName = paramPN. Value. ToStringO;

} catch { throw;

} finally { conn.Close();

} I Для чтения единственного значения из выходного параметра хранимой процедуры выполните следующее.

1. Создайте объект SqlCommand для вызова хранимой процедуры.

2. Задайте все входные параметры и единственный выходной параметр, до бавляя объекты SqlParameter в набор Parameters объекта SqlCommand.

3. Откройте соединение.

4. Вызовите метод ExecuteNonQuery объекта SqlCommand.

5. Закройте соединение.

6. Извлеките выходное значение из свойства Value объекта SqlParameter.

74 Microsoft ADO.NET В приведенном выше фрагменте кода используется следующая хранимая процедура.

CREATE PROCEDURE LookupProductNameSPOutput SProductlD int, @ProductName nvarchar(40) OUTPUT AS SELECT @ProductName = ProductName FROM Products WHERE ProductID = @ProductID GO Следующий код иллюстрирует, как воспользоваться возвращаемым значе нием хранимой процедуры, чтобы проверить наличие определенной запи си. С точки зрения программирования, это аналогично применению вы ходных параметров Ч с тем исключением, что вы должны явно присвоить объекту SqlParameter значение ParameterDirection. Return Value.

bool CheckProduct( int ProductID ) ( SqlConnectIon conn = new SqlConnection{ "server={local);

Integrated Security=SSPI;

database=northwind");

SqlCommand cmd = new SqlCommandC'CheckProductSP", conn );

cmd.CommandType = CommandType.StoredProcedure;

cmd.Parameters.Add("@ProductID", ProductID );

SqlParameter paramRet = cmd.Parameters.Add("@ProductExists", SqlDbType.Int );

paramRet.Direction = ParameterDirection.ReturnValue;

try { conn.0pen();

cmd.ExecuteNonQuery();

\ catch { throw;

} finally conn.CloseO;

} return (int)paramRet.Value == 1;

J Чтобы проверить наличие определенной записи, выполните следующие действия.

1. Создайте объект SqlCommand для вызова хранимой процедуры.

Руководство по архитектуре доступа к данным на платформе,NET 2. Задайте входной параметр, содержащий значение первичного ключа той записи, наличие которой вы хотите проверить.

3. Задайте единственный параметр для возвращаемого значения. Добавь те объект SqlParameter в набор Parameters объекта SQLCommand и ус тановите его направление как ParameterDirection.RetumValue.

4. Откройте соединение.

5. Вызовите метод ExecuteNonQuery объекта SqlCommand.

6. Закройте соединение.

7. Получите возвращаемое значение из свойства Value объекта SqlParameter.

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

CREATE PROCEDURE CheckProductSP isProductlD int AS IF EXISTSC SELECT ProductID FROM Products WHERE ProductID = @ProductID ) return ELSE return GO Использование SqIDataReader для чтения одного поля Объект SqIDataReader позволяет получать единственное выходное значе ние методом ExecuteReader объекта команды. Это требует чуть больше кодирования, чем в предыдущих случаях, поскольку вы должны вызвать метод Read объекта SqIDataReader, а затем считать требуемое значение с помощью одного из методов-аксессоров класса чтения. Использование объекта SqIDataReader демонстрирует следующий код, bool CheckProductWithReaderC int ProductID ) { SqlConnection conn = new SqlConnection( "server=(local);

Integrated Security=SSPI;

database=northwind");

SqlCominand cmd = new SqlCommand("CheckProductExistsWithCount", conn );

cmd.CommandType = CommandType.StoredProcedure;

cmd.Parameters.Add("йProductID", ProductID };

cmd. Parameters["йProductID"]. Direction = ParameterDirection.Input;

try ( conn.OpenO;

SqIDataReader reader = cmd.ExecuteReader( CommandBehavior.Singleflesult );

Microsoft ADO.NET reader.Read();

bool bRecordExists = reader.Getlnt32{0) > 0;

reader.Closet);

return bRecordExists;

catch throw;

finally conn.Close();

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

CREATE PROCEDURE CheckProductExistsWitnCount @ProductID int AS SELECT COUNT(*} FROM Products WHERE ProductID = eProductID GO Программирование ручных транзакций ADO.NET Ниже показано, как использовать поддержку транзакций в SQL Server.NET Data Provider, чтобы с помощью транзакции защитить перевод де нежных средств. Перевод средств выполняется между двумя счетами, на ходящимися в одной базе данных.

public void TransferMoney( string toAccount, string fromAccount, decimal amount ) { using ( SqlConnection conn = new SqlConnection( "server=(local);

Integrated Security=SSPI;

database=SimpleBank" ) ) { SqlCommand cmdCredit = new SqlCommand("Credit", conn );

cmdCredit.CommandType = CommandType.StoredProcedure;

cmdCredit.Parameters.Add( new SqlParameter("@AccountNo", toAccount) );

cmdCredit.Parameters.Add( new SqlParameter("йAmount", amount ));

SqlCommand cmdDebit = new SqlCommand("Debit", conn );

cmdOebit.CommandType = CommandType.StoredProcedure;

cmdDebit.Parameters.Add{ new SqlParameter("@AccountNo", fromAccount) );

cindDebit.Parameters.Add( new SqlParameter("@Amount", amount ));

conn.0pen();

// Начинаем новую транзакцию Руководство по архитектуре доступа к данным на платформе.NET using ( SqlTransaction trans = conn.BeginTransaction() ) { // Связываем с этой транзакцией два объекта команды omdCredit. Transaction = trans;

cmdDebit. Transaction = trans;

try { cmdCredit. ExecuteNonQueryt ) ;

cmdDebit. ExecuteNonQueryt ) ;

// Обе команды (для дебета и кредита) выполнены успешно trans. CommitO;

} catch( Exception ex ) { // Транзакция потерпела неудачу trans. RollbackQ;

// Протоколируем параметры исключении...

throw ex;

I Выполнение транзакций с помощью Transact-SQL Вот как выполнить транзакционную операцию по переводу денег с ис пользованием хранимой процедуры, написанной на Transact-SQL.

CREATE PROCEDURE MoneyTransfer @FromAccount char(20), taToAccount char(20), ^Amount money AS BEGIN TRANSACTION - ВЫПОЛНЯЕМ ОПЕРАЦИЮ ПО ДЕБЕТУ UPDATE Accounts SET Balance = Balance - ^Amount WHERE AccountNurober = йFromAccount IF №RowCount = BEGIN RAISERRORC 'Invalid From Account Number', 11, 1) GOTO ABORT END DECLARE ^Balance money SELECT ^Balance = Balance FROM ACCOUNTS WHERE AccountNumber = @FromAccount IF ^BALANCE < BEGIN RAISERRORC Insufficient funds', 11, 1) GOTO ABORT Microsoft ADO.MET END - ВЫПОЛНЯЕМ ОПЕРАЦИЮ ПО КРЕДИТУ UPDATE Accounts SET Balance = Balance + йAmount WHERE AccountNumber = @ToAccount IF @@RowCount = BEGIN RAISERROHCInvalid To Account Number', 11, 1) GOTO ABORT END COMMIT TRANSACTION RETURN ABORT:

ROLLBACK TRANSACTION GO В этой хранимой процедуре для управления транзакцией вручную исполь зуются операторы BEGIN TRANSACTION, COMMIT TRANSACTION и ROLLBACK TRANSACTION.

Программирование транзакционного.NET-класса В следующем примере показано три управляемых обслуживаемых.NET класса, сконфигурированных для автоматических транзакций. Каждый класс помечен атрибутом Transaction, значение которого определяет, сле дует ли создавать новый поток транзакции или объект должен участвовать в потоке транзакции вызывающего объекта. Операция по переводу денег выполняется при взаимодействии этих компонентов. Класс Transfer поме чен атрибутом транзакции RequiresNew, а классы Debit и Credit Ч атрибу том Required, В итоге при выполнении все три объекта участвуют в одной и той же транзакции.

using System;

using System.EnterpriseServices;

[Transaction(TransactionOption.RequiresNew}] public>

debit.DebitAccount( fromAccount, amount );

// Выполняем операцию по кредиту Руководство по архитектуре доступа к данным на платформе.NET Credit credit = new CreditO;

credit. CreditAccount( toAccount, amount );

} catch( SqlException sqlex ) { // Обрабатываем исключение и протоколируем его параметры.

// Помещаем исключение в оболочку и передаем дальше.

throw new TransferException( "Transfer Failure", sqlex );

[Transact ion(TransactionOption. Required)] public>

Integrated Security=SSPI";

database="SimpleBank");

SqlComtnand cmd = new SqlCommand("Credit", conn );

cmd.CommandType = CommandType.StoredProcedure;

cmd. Parameters. Add{ new SqlParameter("@AccountNo", account) );

cmd. Parameters. Add( new SqlParameter("@Amount", amount ));

try ( conn.0pen();

cmd. ExecuteNonQuery( ) ;

} catch (SqlException sqlex) { // Протоколируем параметры исключения throw;

// передави исключение дальше I [Т ransaction(TransactionOption. Required)] public>

Integrated Security=SSPI";

database="SimpleBank");

SqlCommand cmd = new SqlCommandC'Debit", conn );

cmd.CommandType = CommandType.StoredProcedure;

cmd. Parameters, Add( new SqlParameter("@AccountNo", account) );

cmd,Parameters.Add( new SqlParameter("$Amount", amount );

) try { conn.0pen();

80 Microsoft ADO.NET cmd. ExecuteNonQueryO;

catch (SqlException sqlex) // Протоколируем параметры исключения throw;

// передаем исключение обратно вызывающему Благодарности Выражаем признательность всем, кто участвовал в подготовке этой статьи:

Bill Vaughn, Mike Pizzo, Doug Rothaus, Kevin White, Blaine Dokter, David Schleifer, Graeme Malcolm (Content Master), Bernard Chen (Sapient), Matt Drucker (Turner Broadcasting) и Steve Kirk.

Вопросы? Замечания? Предложения? С авторами статьи можно связаться (на английском языке) по адресу devfdbck@microsoft.com.

Хотите освоить платформу.NET и задействовать всю ее мощь? Для обме на опытом лучше всего поработать бок о бок с экспертами из технологи ческих центров Microsoft. Дополнительную информацию можно получить по ссылке Джонни Папа ADO.NET: концепции и реализация В этой статье подробно рассматривается концепция отделения доступа к данным через ADO.NET от бизнес-логики и показывается, как добиться этого на практике. Автор реализует компонент, абстрагирующий доступ к данным, на двух.NET-языках: С# и Visual Basic.NET.

С появлением Microsoft.NET разработчики готовятся воспользоваться такими достоинствами этой платформы, как улучшенные возможности доступа к данным. Хотя и СОМ, и Visual Basic 6.0 будут широко приме няться еще долгие годы, уже сейчас заметен большой интерес к переходу на.NET-компоненты. Чаше всего меня спрашивают, чем отличается раз работка с использованием ADO.NET от ADO 2л и как выделить логику доступа к данным через ADO в отдельный компонент. Эти вопросы мы и рассмотрим.

Архитектура ADO.NET предоставляет богатые возможности манипулиро вания данными в программах на С#, Visual Basic.NET и других.NET-co вместимых языках. В предыдущей статье из этой рубрики я продемонст рировал методику инкапсуляции компонента доступа к данным с исполь зованием ADO 1.x и Visual Basic 6.0. Разумеется, концепция отделения доступа к данным от бизнес-логики относится не только к Visual Basic 6.0.

Аналогичные подходы применяются при разработке для.NET, и в этой статье мы начнем разбираться с ADO.NET и ее влиянием на программи Публиковалогь в MSDN Magazine/Русская Редакция. 2002. Спецвыпуск №1 (янпарь март). Ч Прим. изд.

Microsoft ADO.NET рование под.NET. Я покажу, как создать сервис для доступа к данным че рез ADO.NET, отделенный от бизнес-логики, реализуемой посредством ASP.NET.

В примерах, которые мы рассмотрим, три основных составляющих:

Х Web-форма, содержащая серверные элементы управления, такие как asp:DataGrid, для отображения данных;

Х CodeBehind-класс, получающий данные от сервиса доступа к данным и заполняющий ими элементы на Web-форме;

Х исходный код, в котором определяется собственное пространство имен и классы, реализующие сервис доступа к данным.

Сейчас много спорят, какой.NET-язык использовать. Хотя.NET спроек тирована так, что ни один конкретный язык не имеет существенных пре имуществ перед другими, в каждом отдельном случае неплохо определить ся с этим вопросом. Между тем, пока дискуссии насчет языков продолжа ются, я приведу примеры как на Visual Basic, так и на С#. При этом не забывайте, что сборки, созданные с использованием этих языков, могут взаимодействовать друг с другом. Иначе говоря, можно создать сборку на С# и обращаться к ней из класса, написанного на Visual Basic.NET.

Web-форма Начнем с простой Web-формы, представляющей пользовательский интер фейс (UI) приложения. Код формы WebForm.aspx содержит два серверных элемента управления DataGrid, которые будут заполнены данными из двух разных ADO.NET-объектов DataSet (рис. 1). DataGrid позволяет представлять набор данных как HTML-таблицу. (О DataGrid подробно рассказывает Дино Эспозито в рубрике Cutting Edge в апрельском, май ском и июньском номерах MSDN Magazine? за 2001 г.) На моей Web форме два элемента DataGrid: grdSql (заполняется SQL-оператором с ис пользованием моего сервиса доступа к данным) и grdProc (заполняется хранимой процедурой с применением того же сервиса).

Заметьте: Web-форма на рис. 1 содержит только директивы, HTML и сер верные элементы управления. Собственно кода здесь нет. В.NET Frame work код, реализующий Ш, можно отделить от самого интерфейса. Иначе говоря, весь HTML и серверные элементы управления могут быть внутри Web-формы, а весь код, взаимодействующий с UI, может храниться в от дельном файле класса. Это альтернатива стандартному подходу в ASP, где код и интерфейс смешаны, так что управлять проектом теперь гораздо проще.

ADQ.NET: концепции и реализация Рис. 1. Web-форма из Visual Basic a <Х@ Page Language "vtr Codebehind="WebForra.aspx.vb" a Innerlts "HyV80ataLayer.WebForerx>