4. Использование Managed
provider
4.1. Управление
подключениями
4.2. Формат строки подключения –
ADOConnection
4.3. Формат строки подключения –
SQLConnection
4.4. Команды
4.5. DataReader
4.6. Использование
хранимых процедур в команде
4.7. Создание простых
DataSetCommand
4.8. Обновление базы данных с
помощью DataSetCommand и DataSet
4.9. Определение
отображений таблиц и столбцов
4.10.
Mapping/Changing отображение столбцов во время исполнения
4.11. Использование параметров DataSetCommands
4.12. Параметры Input/Output и возвращаемые
значения
4.13. Объединённое подключение
(Connection Pooling)
4.13.1. Определение подкласса
SQLConnection
4.13.2. Включение в код строк, которые
организуют объединенное подключение
4.13.3. Подключение
Microsoft.ComServices.dll
4.13.4. Конфигурирование
объединения компонент
4.13.5. Поддержка
транзакций
4.14. Команды генерируемые
автоматически
4.14.1. Правила организации команды
Update
4.14.2. Правила организации команды
Insert
4.14.3. Ограничения логики механизма
AutoGen
4.15. Добавление и удаление событий SQL
Managed provider
4.15.1. Обвальные исключения,
порождённые обработчиком событий<
4.15.2. Семантика
добавления и удаления событий
4.15.3. Сравнение
делегатов при добавление и удаление событий<
4. Использование Managed provider
Команды, подключение, и DataReader представляют основные элементы модели
ADO.NET.
4.1. Управление подключениями
Чтобы соединяться с источником данных, используйте любой из двух объектов
Connection, входящий в состав .NET Framework, и который поддерживается
Microsoft: SQLConnection или ADOConnection.
SQLConnection осуществляет
подключение к базам данных Microsoft SQL Server. ADOConnection позволяет
устанавливать подключение через провайдера OLE DB.
Чтобы использовать managed
providers, которые идут вместе с .NET Framework, Вы должны включить следующие
пространства имён:
- SQL Managed Provider – System.Data.SQL
- ADO Managed Provider –
System.Data.ADO
Следующий код демонстрирует создание и открытие подключения к базе данных SQL
Server:
SQLConnection
[VB]
Dim connectionString As String =
_"server=localhost;uid=sa;pwd=;database=northwind"
Dim myConnection As
SQLConnection = New SQLConnection(connectionString)
myConnection.Open()
[C#]
String connectionString =
"server=localhost;uid=sa;pwd=;database=northwind";
SQLConnection myConnection
= new SQLConnection(connectionString);
myConnection.Open();
ADOConnection
[VB]
Dim connectionString As String = "Provider= SQLOLEDB.1; Data " &
_
"Source=localhost; uid=sa; pwd=; Initial Catalog=northwind;"
Dim
myConnection As ADOConnection = New
ADOConnection(connectionString)
myConnection.Open()
[C#]
String connectionString = "Provider= SQLOLEDB.1; Data
Source=localhost;
uid=sa; pwd=; Initial Catalog=northwind;";
ADOConnection
myConnection = new ADOConnection(connectionString);
myConnection.Open();
Объекты ADO и SQL Connection обеспечивают многие из свойств, которые Вы
привыкли использовать в ADO.
4.2. Формат строки подключения – ADOConnection
Для ADO managed provider, формат строки подключения идентичен формату строки
подключения, используемому в ADO.
4.3. Формат строки подключения – SQLConnection
SQL managed provider поддерживает формат строки подключения, который подобен
формату строки подключения ADO. Для определения допустимых имён и значений, см.
свойства SQLConnection.ConnectionString в справочниках .NET Framework SDK.
4.4. Команды
Обеспечив подключение, Вы должны затем иметь возможность выполнять
инструкции, предназначенные базе данных. Самый простой и наиболее прямой способ
использовать объекты ADO и SQL Command. Следующий код демонстрирует как это
делается:
ADOCommand
[VB]
Dim SQlStmt As String = "SELECT * FROM Customers"
Dim myCommand As
ADOCommand = New ADOCommand(SQLStmt, myConnection)
[C#]
String SQLStmt = " SELECT * FROM Customers";
ADOCommand myCommand =
new ADOCommand(SQLStmt, myConnection);
SQLCommand
[VB]
Dim SQlStmt As String = "SELECT * FROM Customers"
Dim myCommand As
SQLCommand = New SQLCommand(SQLStmt, myConnection)
[C#]
String SQLStmt = " SELECT * FROM Customers";
SQLCommand myCommand =
new SQLCommand(SQLStmt, myConnection);
Дополнительный объект должен быть создан до запроса метода команды Execute с
объектом в качестве параметра.
ADO
[VB]
Dim myReader As ADODataReader = Nothing
myCommand.Execute(myReader)
[C#]
ADODataReader myReader = null;
myCommand.Execute(out myReader);
SQL
[VB]
Dim myReader As SQLDataReader = Nothing
myCommand.Execute(myReader)
[C#]
SQLDataReader myReader = null;
myCommand.Execute(out myReader);
Пример объявляет переменную, которая будет принадлежать иллюстрируемому
SQLDataReader, возвращенный как параметр метода execute последней команды.
4.5. DataReader
Когда обрабатывается большое количество данных, отвлечение на это памяти
может стать проблемой или узким местом производительности. Например, чтение
10000 строк из базы данных заставляет DataTable распределять и удерживать память
для всех 10000 строк и на весь срок жизни таблицы. Если 1000 пользователей
обратятся с таким запросом к одному серверу и в одно и то же время,
использование памяти может стать проблемой.
Также, если Вы не уверены в
правильности или достоверности результатов выполненного запроса, и Вам
необходимо предусмотреть в коде циклы, в рамках которых будут выполняться
итерации, оперирующие потоком, DataTable тоже не будет идеальным решением,
поскольку в каждой итерации будут загружаться все строки.
Чтобы решить эту
проблему, используйте DataReader, как read-only, forward-only поток,
возвращаемый из базы данных. Это позволит Вам держать в памяти только одну
строку.
Вы пройдёте поток через возвращаемый объект DataReader достаточно
простым способом:
[VB]
While myReader.Read
'
Выполняйте необходимые действия с текущей строкой
End While
[C#]
While (myReader.Read())
{
//Выполняйте необходимые действия с текущей строкой
}
DataReader также поддерживает, ряд Get методов, которые позволяют обращаться
к значениям полей, таким, как время. Например: GetDataTime, GetDouble, GetGuid,
GetInt32, GetStream.
4.6. Использование хранимых процедур в команде
Когда Вам нужно возвратить только одну строку, вероятно, самый быстрый путь
использовать хранимую процедуру с выходными параметрами. При использовании
Microsoft SQL Server, или других баз данных, имеющих процедуры, параметры могут
использоваться, чтобы отобрать единственную строку. Использование параметров
работает также как в ADO. Вы можете передавать строку в команде, или
использовать совокупность параметров.
При использовании параметров с
SQLCommand, имена параметров, добавленных к совокупности параметров команды,
должны соответствовать именам маркеров параметра в хранимой процедуре. SQL
managed provider обрабатывает их как «named» параметры и будет искать
соответствующий маркер.
[VB]
Dim myConnection As SQLConnection = New SQLConnection _
("server=delphi;uid=sa;pwd=;database=northwind")
Dim myCommand As
SQLCOmmand = New SQLCommand _
("GetCustomerListbyState 'WA'", myConnection)
myCommand.CommandType =
CommandType.StoredProcedure
Try
myConnection.Open()
myCommand.Execute(myReader)
While myReader.Read
Console.WriteLine(myReader("CompanyName").ToString))
End While
Catch e As Exception
Console.WriteLine(e.ToString)
Finally
myReader.Close()
myConnection.Close()
End Try
[C#]
SQLConnection myConnection =
new
SQLConnection("server=delphi;uid=sa;pwd=;database=northwind");
SQLCommand
myCommand = new SQLCommand("GetCustomerListbyState 'WA'",
myConnection);
myCommand.CommandType =
CommandType.StoredProcedure;
try
{
myConnection.Open();
myCommand.Execute(out myReader);
while (myReader.Read())
{
Console.WriteLine(myReader["CompanyName"].ToString());
}
}
catch(Exception e)
{
Console.WriteLine(e.ToString());
}
finally
{
myReader.Close();
myConnection.Close();
}
При использовании следующего кода, будет создана команда, которая выполняет
вызов хранимой процедуры по имени «GetCompanyName». Эта хранимая процедура
объявлена в SQL Server как:
CREATE PROCEDURE
GetCompanyName
@CustomerID nvarchar(5),
@CompanyName nvarchar(40)
output
as
Select @CompanyName = CompanyName from Customers where
CustomerID = @CustomerID
Чтобы выполнять эту хранимую процедуру, выполните следующие шаги:
(1)
Создайте команду.
(2) Установите тип команды как Stored procedure.
(3)
Создайте и установите параметры.
SQL
[VB]
Dim myConnection As SQLConnection = new
_
SQLConnection("server=delphi;uid=sa;pwd=;database=northwind")
Dim
myCommand As SQLCommand = New SQLCommand("GetCompanyName",
myConnection)
myCommand.CommandType = CommandType.StoredProcedure
Dim
workPara As SQLParameter = Nothing
workParam =
myCommand.Parameters.Add
(New SQLParameter("@CustomerID", SQLDataType.NChar,
5))
workPara.Direction = ParameterDirection.Input
workParam.Value =
"ALFKI"
workParam.myCommand.Parameters.Add
(New
SQLParameter("@CompanyName", SQLDataType.NChar, 40))
workParam.Direction =
ParameterDirection.Output
Try
myConnection.Open()
myConnection.Execute()
Console.WriteLine("CompanyName = " & _
myCommand.Parameters("@CompanyName").Value
Catch e As
Exception
Console.WriteLine(e.ToString)
Finally
myConnection.Close()
End Try
[C#]
SQLConnection myConnection =
new
SQLConnection("server=delphi;uid=sa;pwd=;database=northwind");
SQLCommand
myCommand = new SQLCommand("GetCompanyName",
myConnection);
myCommand.CommandType =
CommandType.StoredProcedure;
SQLParameter workParam =
null;
workParam = myCommand.Parameters.Add(new
SQLParameter("@CustomerID",
SQLDataType.NChar, 5));
workParam.Direction =
ParameterDirection.Input;
workParam.Value = "ALFKI";
workParam = myCommand.Parameters.Add(new
SQLParameter("@CompanyName",
SQLDataType.NChar, 40));
workParam.Direction
= ParameterDirection.Output;
try
{
myConnection.Open();
myCommand.Execute();
Console.WriteLine("CompayName= "
+
myCommand.Parameters["@CompanyName"].Value);
}
catch(Exception
e)
{
Console.WriteLine(e.ToString());
}
finally
{
myConnection.Close();
}
ADO
[VB]
Dim myCommand As ADOCommand = New ADOCommand("GetCompanyName",
myConnection)
myCommand.CommandType = CommandType.StoredProcedure
Dim
workPara As ADOParameter = Nothing
workParam =
myCommand.Parameters.Add("@CustomerID", SQLDataType.NChar,
5)
workPara.Direction = ParameterDirection.Input
workParam.Value =
"ALFKI"
workParam.myCommand.Parameters.Add("@CompanyName", SQLDataType.NChar,
40)
workParam.Direction = ParameterDirection.Output
Try
myConnection.Open()
myConnection.Execute()
Console.WriteLine("CompanyName = " &
_
myCommand.Parameters("@CompanyName").Value
Catch e As
Exception
Console.WriteLine(e.ToString)
Finally
myConnection.Close()
End Try
[C#]
ADOCommand myCommand = new ADOCommand("GetCompanyName",
myConnection);
myCommand.CommandType =
CommandType.StoredProcedure;
ADOParameter workParam =
null;
workParam = myCommand.Parameters.Add("@CustomerID", ADODBType.Char,
5);
workParam.Direction = ParameterDirection.Input;
workParam.Value =
"ALFKI"
workParam =
myCommand.Parameters.Add("@CompanyName", ADODBType.Char,
40);
workParam.Direction =
ParameterDirection.Output;
try
{
myConnection.Open();
myCommand.Execute();
Console.WriteLine("CompayName= "
+
myCommand.Parameters["@CompanyName"].Value);
}
catch(Exception
e)
{
Console.WriteLine(e.ToString());
}
finally
{
myConnection.Close();
}
Создайте команду, как уже писалось выше, для организации Вашего подключения.
Установите с помощью конструктора класса в свойстве команды commandtext - имя
хранимой процедуры.
ADOCommand myCommand = new
ADOCommand("GetCompanyName", myConnection);
myCommand.CommandType =
CommandType.StoredProcedure;
Другой вариант состоит в том, чтобы просто создать команду, затем назначать
подключение и commandtext через их вспомогательные свойства.
ADOCommand myCommand = new
ADOCommand();
myCommand.ActiveConnection =
myConnection;
myCommand.CommandText =
"GetCompanyName";
myCommand.CommandType =
CommandType.StoredProcedure;
Затем, создайте два параметра объекта, и добавьте их к совокупности
параметров команды.
SQL
SQLParameter workParam =
null;
workParam = myCommand.Parameters.Add(new
SQLParameter("@CustomerID",
SQLDataType.NChar, 5));
workParam.Direction =
ParameterDirection.Input;
workParam.Value = "ALFKI";
workParam =
myCommand.Parameters.Add(new SQLParameter("@CompanyName",
SQLDataType.NChar,
40));
workParam.Direction = ParameterDirection.Output;
ADO
workParam =
myCommand.Parameters.Add("@CustomerID", ADODBType.Char,
5);
workParam.Direction = ParameterDirection.Input;
workParam.Value =
"ALFKI";
workParam =
myCommand.Parameters.Add("@CompanyName", ADODBType.Char,
40);
workParam.Direction = ParameterDirection.Output;
Здесь, Вы делаете три вещи: создаёте и добавляете параметры, устанавливаете
назначение параметра, и устанавливаете значение для входного параметра.
Вы
обратили внимание, что есть различие в вариантах исполнении для SQLCommand и
ADOCommand. Это потому, что просто модели не были точно подобны в этот
момент.
Затем, откройте подключение, и выполните команду. После этого, просто
найдите выходной параметр, и впишите его с консоли.
[VB]
myConnection.Open()
myCommand.Execute()
Console.WriteLine("CompanyName
= " & _
myCommand.Parameters("@CompanyName").Value)
[C#]
myConnection.Open();
myCommand.Execute();
Console.WriteLine("CompanyName=
" + myCommand.Parameters["@CompanyName"].Value)
Последнее, важное замечание. Обратите внимание на последний блок. Последний
блок инструкции, это уловка, которая всегда гарантированно будет выполнена,
независимо от того, будет ли получено исключение.
[VB]
Finally
myConnection.Close
[C#]
finally
{
myConnection.Close();
}
Работая с подключением и DataReaders, Вы всегда должны использовать этот
последний, завершающий соединение блок, чтобы гарантировать, что если что – ни
будь потерпит неудачу, соединение всё равно будут закрыты.
Обратите внимание,
что в этом примере мы использовали перегрузку Execute метода команды, которая не
возвращает DataReader. Если бы DataReader возвращался, и имелись выходные
параметры, они не были бы заполнены, пока не закрыт DataReader.
4.7. Создание простых DataSetCommand
Основная функция DataSetCommand состоит в получении данных из источника и
загрузка их в DataTable из состава DataSet. Для этого, DataSetCommand необходимо
всего две вещи: организация подключения (connection) и выполнение команды
Select.
В следующем примере мы построим ADODataSetCommand, содержащую текст
команды Select и строку OLEDB подключения:
[VB]
Dim workDSCMD As ADODataSetCommand
WorkDSCMD = New ADODataSetCommand
_
("Select * from Customers", _
"Provider=SQLOLEDB.1;Initial
Catalog=Northwind;" & _
"Data Source=MyServer;User ID=sa;")
[C#]
ADODataSetCommand workDSCMD = new ADODataSetCommand("Select *
from
Customers", "Provider=SQLOLEDB.1;Initial Catalog=Northwind;
Data
Source=MyServer;User ID=sa;");
Представленный выше код выполняет следующее:
1. Создает новый объект ADOConnection, использующий строку подключения.
2. Создает новый объект ADOCommand, использующий инструкцию Select и
существующее подключение.
3. Соотносит новый объект ADOCommand с инструкцией
Select на workDSCMD.
Другая возможность получения необходимого результата могла состоять в том,
чтобы создать экземпляры каждого из объектов и затем настроить DataSetCommand
через её свойства.
[VB]
Dim workDSCMD As ADODataSetCommand
Dim workCMD As ADOCommand
Dim
workConn As ADOConnection
workConn = New ADOConnection
_
("Provider=SQLOLEDB.1;Initial Catalog=Northwind;" & _
"Data
Source=MyServer;User ID=sa;")
workCMD = New ADOCommand("SELECT * FROM
Customers", workConn)
workDSCMD = new
ADODataSetCommand()
workDSCMD.SelectCommand = workCMD
[C#]
ADODataSetCommand workDSCMD;
ADOCommand workCMD;
ADOConnection
workConn;
workConn = New ADOConnection("Provider=SQLOLEDB.1;Initial
Catalog=Northwind;
Data
Source=MyServer;User ID=sa;");
workCMD = New ADOCommand("SELECT * FROM
Customers", workConn);
workDSCMD = New
ADODataSetCommand()
workDSCMD.SelectCommand = workCMD
Эта DataSetCommand может использоваться, чтобы заполнить DataTable
принадлежащую DataSet. Как ADO, так и SQL DataSetCommand объекты используют
метод FillDataSet, имеющий два параметра: экземпляр DataSet и имя таблицы
источника. Экземпляр DataSet представляет DataSet для заполнения, а имя таблицы
источника идентифицирует таблицу внутри DataSet.
В следующем примере,
создаётся новый экземпляр DataSet, который используется в качестве аргумента при
вызове метода FillDataSet.
[VB]
Dim workDS As DataSet = New DataSet("myDataSet")
Dim workDSCMD As
ADODataSetCommand = New ADODataSetCommand _
(SELECT * FROM Customers", _
"Provider=SQLOLEDB.1;Initial
Catalog=Northwind;" & _
"Data Source=MyServer;User ID=sa;")
workDSCMD.FillDataSet(workDS,
"Customers")
[C#]
DataSet workDS = new DataSet("mydataset");
ADODataSetCommand
workDSCMD = new ADODataSetCommand("SELECT * FROM
Customers",
"Provider=SQLOLEDB.1;Initial Catalog=Northwind;
Data Source=MyServer;User
ID=sa;");
workDSCMD.FillDataSet(workDS, "Customers");
Результатом вызова метода FillDataSet станет заполнение таблицы "Customers" в
DataSet. Но Вы можете спросить: «Почему таблица Customers вошла в DataSet, хотя
она фактически не была создана до выполнения метода FillDataSet?».
Как для
SQL, так и для ADO DataSetCommand объектов, справедливо утверждение, что если Вы
не изменяете значение по умолчанию, любая схема (table/column/primary key
definitions) из источника данных, которая не существуют внутри DataSet, будет
создан автоматически. Так, в представленном выше примере, ни одна из схем
таблицы Customers предварительно не существовала.
Поэтому, если Вы создадите
таблицу Customers до выполнения метода FillDataSet, DataSetCommand просто
заполнит существующую таблицу.
Поскольку нет никакой физической связи между
DataSet и DataSetCommand, DataSetCommand может использоваться, чтобы заполнить
любое число экземпляров DataSet. Просто создайте экземпляр DataSet, и передайте
эго как аргумент в методе FillDataSet.
4.8. Обновление базы данных с помощью DataSetCommand и DataSet
ADO и SQL DataSetCommand объекты обеспечивают метод Update, позволяющий
переносить изменения в DataSet назад в базу данных. Метод Update, подобно методу
FillDataSet, использует экземпляр DataSet и имя таблицы источника. Экземпляр
DataSet – это DataSet, который содержит изменения, внесённые Вами, в то время
как исходное имя таблицы идентифицирует DataTable, в которой содержатся
изменения.
Когда вызывается метод FillDataSet, осуществляется анализ
изменений, которые были сделаны, и выполняется соответствующая команда. DataSet
принимает изменения в том порядке, в каком они представлены. Так, если Вы хотите
получить все модификации, сделанные до удаления, Вы будете должны применить для
них фильтр, причём перед применением над ними метода Update. Следующий код
показывает, как это выполнить:
[VB]
Dim workDS As DataSet = New DataSet("myDataSet")
Dim workDSCMD As
ADODataSetCommand = New ADODataSetCommand _
("SELECT * FROM Customers",
_
"Provider=SQLOLEDB.1;Initial Catalog=Northwind;" & _
"Data
Source=MyServer;User ID=sa;")
workDSCMD.FillDataSet(workDS, "Customers")
'
Make some changes to the customer data in the
DataSet.
workDSCMD.Update(workDS, "Customers")
[C#]
DataSet workDS = new DataSet("mydataset");
ADODataSetCommand
workDSCMD = new ADODataSetCommand("SELECT * FROM
Customers",
"Provider=SQLOLEDB.1;Initial Catalog=Northwind;
Data Source=MyServer;User
ID=sa;");
workDSCMD.FillDataSet(workDS, "Customers");
// Make some changes
to the customer data in the DataSet.
workDSCMD.Update(workDS, "Customers");
Пожалуйста помните, что "removing" отличается от "deleting". Если Вы
фактически использовали "remove" для строки из выборки, DataSetCommand не будет
видеть эту строку, когда Вы применяете метод Update для DataSet. Таким образом,
не будет выполняться никакая команда для этой строки. Однако, если строка
отмечена как "deleted", команда удаления будет исполнена.
4.9. Определение отображений таблиц и столбцов
Вы можете вносить изменения в таблицу с целью её заполнения или переноса
изменений и схемы в DataSet, для чего понадобится задать отображение таблицы или
столбца. Такое отображение таблиц (master mapping) обеспечивает взаимосвязь
между данными, возвращёнными из таблицы в базе данных, и DataTable внутри
DataSet. Следующий пример показывает, как создать отображение таблицы, используя
Table как исходное имя таблицы и MyAuthors как имя DataTable.
[VB]
workDSCMD.TableMappings.Add("Table", "MyAuthors")
[C#]
workDSCMD.TableMappings.Add("Table", "MyAuthors");
Это отображение таблицы создает виртуальную связь (ссылку) между таблицей в
базе данных (указанной в инструкции Select и заданной по умолчанию с именем
Table) и таблицы в DataSet, называемой MyAuthors.
Отображение таблицы дает
Вам возможность использовать имена столбцов в DataSet, которые теперь могут
отличаться от тех, которые используются в базе данных. С помощью отображений,
столбцы DataSet будут сопоставляться соответствующим столбцам базы, в то время,
когда таблица будет модифицироваться. Следующий пример показывает, как
отобразить столбцы таблицы Authors базы данных Pubs к набору более понятных и
простых имен в DataSet.
[VB]
With workDSCMD.TableMappings.Item(0).ColumnMappings
.Add("au_id", "AuthorID")
.Add("au_lname", "lastname")
.Add("au_fname", "firstname")
.Add("phone", "phone")
.Add("address", "address")
.Add("city", "city")
.Add("state", "state")
.Add("zip", "zip")
.Add("contract", "contract")
End With
[C#]
workDSCMD.TableMappings[0].ColumnMappings.Add( "au_id",
"AuthorID");
workDSCMD.TableMappings[0].ColumnMappings.Add( "au_lname",
"lastname");
workDSCMD.TableMappings[0].ColumnMappings.Add( "au_fname",
"firstname");
workDSCMD.TableMappings[0].ColumnMappings.Add( "phone",
"phone");
workDSCMD.TableMappings[0].ColumnMappings.Add( "address",
"address");
workDSCMD.TableMappings[0].ColumnMappings.Add( "city",
"city");
workDSCMD.TableMappings[0].ColumnMappings.Add( "state",
"state");
workDSCMD.TableMappings[0].ColumnMappings.Add( "zip",
"zip");
workDSCMD.TableMappings[0].ColumnMappings.Add( "contract",
"contract");
Для ADO и SQL DataSetCommand объектов, когда методы Update или FillDataSet
вызываются без указания таблицы источника, DataSetCommand ищет отображение к
таблице источнику по имени Table. В таком случае, имя таблицы из DataSet
запрашивается у отображения и далее используется это имя, причём, если таблица с
таким именем не находится из числа принадлежащих объекту DataSet, к совокупности
таблиц добавляется новая таблица, для которой используя указанное имя.
[VB]
Dim myDS As DataSet = New DataSet()
Dim myConnection As ADOConnection
= New ADOConnection _
("Provider=SQLOLEDB;Data Source=Delphi;Initial
Catalog=pubs;user id=sa")
Dim workDSCMD As ADODataSetCommand = New
ADODataSetCommand _
("SELECT * FROM Authors",
myConnection)
workDSCMD.TableMappings.Add("Table", "Authors")
With
workDSCMD.TableMappings(0).ColumnMappings
.Add("au_id", "AuthorID")
.Add("au_lname", "lastname")
.Add("au_fname", "firstname")
.Add("phone", "phone")
.Add("address", "address")
.Add("city", "city")
.Add("state", "state")
.Add("zip", "zip")
.Add("contract", "contract")
End With
workDSCMD.FillDataSet(myDS)
[C#]
DataSet myDS = new DataSet();
ADOConnection myConnection = new
ADOConnection("Provider=SQLOLEDB;Data Source=Delphi;Initial Catalog=pubs;user
id=sa");
ADODataSetCommand workDSCMD = new ADODataSetCommand("SELECT * FROM
Authors", myConnection);
workDSCMD.TableMappings.Add("Table", "Authors");
workDSCMD.TableMappings[0].ColumnMappings.Add( "au_id",
"AuthorID");
workDSCMD.TableMappings[0].ColumnMappings.Add( "au_lname",
"lastname");
workDSCMD.TableMappings[0].ColumnMappings.Add( "au_fname",
"firstname");
workDSCMD.TableMappings[0].ColumnMappings.Add( "phone",
"phone");
workDSCMD.TableMappings[0].ColumnMappings.Add( "address",
"address");
workDSCMD.TableMappings[0].ColumnMappings.Add( "city", "city");
workDSCMD.TableMappings[0].ColumnMappings.Add( "state",
"state");
workDSCMD.TableMappings[0].ColumnMappings.Add( "zip", "zip");
workDSCMD.TableMappings[0].ColumnMappings.Add( "contract",
"contract");
workDSCMD.FillDataSet(myDS);
В представленном выше примере, создаётся отображение таблицы, которая
становится заданной по умолчанию с именем Table. Когда Вы вызываете методы
Update или FillDataSet без имени таблицы источника, DataSetCommand будет
использовать это отображение.
В более широком смысле, Вам может
потребоваться, чтобы тот же самый DataSetCommand поддерживал загрузку разных
таблиц и с различными отображениями. Достигается это простым добавлением
дополнительных отображений таблицы.
В представленном ниже примере, создаётся
отображение таблицы источника с именами таблиц DataSet: bob и King.
[VB]
workDSCMD.TableMappings.Add("bob", "King")
With
workDSCMD.TableMappings(0).ColumnMappings
.Add("au_id", "AuthorID")
.Add("au_lname", "lastname")
.Add("au_fname", "firstname")
.Add("phone", "phone")
.Add("address", "address")
.Add("city", "city")
.Add("state", "state")
.Add("zip", "zip")
.Add("contract", "contract")
End With
workDSCMD.FillDataSet(myDS,
"bob")
[C#]
workDSCMD.TableMappings.Add("bob", "King");
workDSCMD.TableMappings[0].ColumnMappings.Add( "au_id",
"AuthorID");
workDSCMD.TableMappings[0].ColumnMappings.Add( "au_lname",
"lastname");
workDSCMD.TableMappings[0].ColumnMappings.Add( "au_fname",
"firstname");
workDSCMD.TableMappings[0].ColumnMappings.Add( "phone",
"phone");
workDSCMD.TableMappings[0].ColumnMappings.Add( "address",
"address");
workDSCMD.TableMappings[0].ColumnMappings.Add( "city", "city");
workDSCMD.TableMappings[0].ColumnMappings.Add( "state",
"state");
workDSCMD.TableMappings[0].ColumnMappings.Add( "zip", "zip");
workDSCMD.TableMappings[0].ColumnMappings.Add( "contract",
"contract");
workDSCMD.FillDataSet(myDS, "bob");
Вызов метода FillDataSet, применяется к экземпляру DataSet и использует
ссылки к таблице источника. DataSetCommand просматривает совокупность
TableMappings, чтобы определить, существует ли отображение к заданному имени
таблицы источника. Если это так, используется найденное отображение, чтобы
построить и заполнить таблицу. Но если исходное имя таблицы не существует в
совокупности, эта таблица сразу же будет создана в совокупности
TableMappings.
В представленном выше примере, создано отображение, которое
заставит записи, возвращаемые командой Select отображаться в таблице King.
4.10. Mapping/Changing отображение столбцов во время
исполнения
Даже притом, что TableMappings совокупность обеспечивает достаточно сильный
механизм для отображения таблиц и столбцов в базе данных в таблицы и столбцы
DataSet, может возникнуть необходимость динамического изменения этого
отображения во время исполнения. Поддержкой такой возможности занимается
SchemaMapping. Вы можете обращаться к внутренним структурам отображения
DataSetCommand и делать там необходимые изменения.
Например, хранимая
процедура, которая возвращает столбец без имени, выполняя следующую инструкцию
Select:
SELECT @@IDENTITY
Обычно, это породило бы ошибку при попытке загрузить результат в DataSet, так
как DataSet требует, чтобы все столбцы имели имена. Чтобы отобразить столбец без
имени в столбец DataTable, Вы можете использовать декларацию события
SQLSchemaMapping для объекта SQLDataSetCommand (для события ADOSchemaMapping,
это будет соответственно объект ADODataSetCommand) чтобы обойтись без имени
столбца.
Ниже приведенный пример анализирует событие SQLSchemaMapping, чтобы
заменить поступающее, пустое имя столбца на «SomeUniqueName».
[VB]
Private Sub SchemaMappingEventHolder(ByVal sender As Object, ByVal e As
_
SQLSchemaMappingEvent)
Dim
column As DataColumn = e.SchemaTable.Columns("DBCOLUMN_NAME")
Dim row As DataRow
For Each row in
e.SchemaTable.Rows.All
If
row.RowState = DataRowState.Deleted Then
row.RejectChanges()
End If
If row.IsNull(column) Or 0 =
row(column).ToString Then
column = e.SchemaTable.Columns("DataColumn")
row(column) = New
DataColumn("SomeUniqueName")
End If
Next
End Sub
[C#]
private void SchemaMappingEventHandler(object sender,
SQLSchemaMappingEvent e)
{
DataColumn column =
e.SchemaTable.Columns["DBCOLUMN_NAME"];
foreach(DataRow row in
e.SchemaTable.Rows.All)
{
if(DataRowState.Deleted ==
row.State)
{
row.RejectChanges();
}
if (row.IsNull(column) || 0 ==
row[column].ToString())
{
//
unnamed column
column = e.SchemaTable.Columns["DataColumn"];
row[column] =
new DataColumn("SomeUniqueName");
}
}
}
4.11. Использование параметров DataSetCommands
Для установки DataSetCommand, в целях использования параметризованного
запроса или хранимой процедуры для метода Update, делайте то же самое, что Вы
делали бы, чтобы использовать инструкции этого типа с Command Object.
Единственное дополнение, которое Вы должны рассмотреть, это rowversion, который
Вы будете использовать в каждом параметре.
В представленном ниже примере,
определяются параметризованные запросы, которые выполняют вставку, обновление, и
удаление для ADODataSetCommand (именно поэтому маркеры параметров обозначены
знаком «?»).
string insertSQL = "INSERT INTO
[Customers]([CustomerID], [CompanyName],
[ContactName], [ContactTitle],
[Address], [City], [Region], [PostalCode], [Country], [Phone], [Fax]) VALUES (?,
?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
string updateSQL = "UPDATE [Customers]
SET [CustomerID] = ?, [CompanyName] = ?,
[ContactName] = ?, [ContactTitle] =
?, [Address] = ?, [City] = ?,
[Region] = ?, [PostalCode] = ?, [Country] = ?,
[Phone] = ?, [Fax] = ?
WHERE [CustomerID] = ? ";
string deleteSQL =
"DELETE FROM [Customers] WHERE [CustomerID] = ? ";
Вы должны установить шесть элементов для каждого из параметров. Для трёх
первых, используйте метод Parameters.Add или конструктор параметров. Ниже
показано, как установить для имени параметра «CustomerID» из совокупности
параметров - тип данных, и их размер:
workParam =
workCommand.Parameters.Add("CustomerID", ADODBType.WChar, 5);
Для типов, определяющих свойства и выраженных целым числом, размер включить
ненужно, или можно просто установить использование заданного по умолчанию
размера.
workParam =
workCommand.Parameters.Add("FldItemID", ADODBType.Integer, 4);
Также, для элемента должно быть установлено направление (Input – Output)
параметра:
workParam.Direction =
ParameterDirection.Input;
Направления параметра допускает следующие возможные варианты параметров
настройки:
| Зарезервированное имя |
Направление параметра |
| Input |
Входной параметр |
| Output |
Выходной параметр |
| InputOutput |
Возможны оба направления |
| ReturnValue |
Параметр представлен, как возвращаемое
значение |
Последние два элемента, которые должны быть установлены - SourceColumn и
SourceVersion. SourceColumn сообщает DataSetCommand какой из столбцов в таблице
будет обеспечивать её значение. Чтобы устанавливать его, назначьте имя столбца,
как это поддерживается в таблице.
workParam.SourceColumn =
"CustomerID";
SourceVersion определяет, от какой версии DataRow - DataSetCommand должен
передать значение. Параметры для этого из DataRowVersion перечислены в
таблице:
| Зарезервировано |
Описание |
| Current |
The current values |
| Default |
The default values |
| Original |
The original values |
| Proposed |
A proposed value |
Заметьте, что в инструкции update (определенной выше) столбец CustomerID
используется в двух местах (в update и в предложении where). При использовании
этой инструкции, чтобы изменить первичное значение «ALFKI» на «AAAAA», Вы должны
иметь возможность исполнить следующее действие: «Update строки, чей первичный
ключ в настоящее время принимает значение ALFKI, а должно получится значение
первичного ключа AAAAA» (Update the row whose primary key is currently ALFKI to
have a primary key of AAAAA). Таким образом, в этом примере, Вы заставляете все
параметры в update использовать DataRowVersion.Current. Но Вы должны
устанавливать параметры предложения where, чтобы использовать
DataRowVersion.Original.
ADOParameter workParam =
null;
workParam = workCommand.Parameters.Add("CustomerID",
ADODBType.WChar, 5);
workParam.Direction =
ParameterDirection.Input;
workParam.SourceColumn =
"CustomerID";
workParam.SourceVersion =
DataRowVersion.Current;
workParam =
workCommand.Parameters.Add("CompanyName", ADODBType.VarWChar,
40);
workParam.Direction =
ParameterDirection.Input;
workParam.SourceColumn =
"CompanyName";
workParam.SourceVersion = DataRowVersion.Current;
..
all the other parameters then
workParam.SourceVersion =
DataRowVersion.Current;
workParam = workCommand.Parameters.Add("oldCustomerID", ADODBType.WChar,
5);
workParam.Direction =
ParameterDirection.Input;
workParam.SourceColumn =
"CustomerID";
workParam.SourceVersion =
DataRowVersion.Original;
4.12. Параметры Input/Output и возвращаемые значения
При использовании хранимых процедур, в дополнение к параметрам ввода и
вывода, Вы можете иметь возвращаемые значения (return values). ADO.NET
обрабатывает возвращаемые из хранимой процедуры значения точно так же, как
входные значения. Следующая хранимая процедура получает customerID как входной
параметр, возвращает companyname как выходной параметр, и имеет возвращаемое
значение.
CREATE PROCEDURE
GetCompanyName
@CustomerID nvarchar(5),
@CompanyName nvarchar(40)
output
as
Select @CompanyName = CompanyName from Customers where
CustomerID = @CustomerIDGO
Return 99
Представленный ниже пример показывает код, который вызывает хранимую
процедуру GetCompanyName:
[VB]
Imports System
Imports System.Data
Imports
System.Data.ADO
Namespace ConsoleApplication1
Module Module1
Shared
Sub Main()
Console.WriteLine("Requires a CustomerID to be passed in as a
argument")
Console.WriteLine("example: OutParamsWithACommand ALFKI")
' Create a new Connection and
DataSetCommand
Dim myConnection As
ADOConnection = New ADOConnection _
("provider=SQLOLEDB;database=northwind;data source=delphi;user
id=sa;")
Dim myCommand As
ADOCommand = New ADOCommand("GetCompanyName", myConnection)
myCommand.CommandType =
CommandType.StoredProcedure;
Dim workParam As ADOParamater = Nothing
workParam = myCommand.Parameters.Add("RETURN
VALUE", ADODBType.Integer)
workParam.Direction = ParameterDirection.ReturnValue
workParam =
myCommand.Parameters.Add("@CustomerID", ADODBType.Char, 5)
workParam.Direction =
ParameterDirection.Input
workParam.Value = args(0)
workParam = myCommand.Parameters.Add("@CompanyName", ADODBType.Char,
40)
workParam.Direction =
ParameterDirection.Output
Try
myConnection.Open()
myCommand.Execute()
Console.WriteLine("CompanyName= " & _
myCommand.Parameters("RETURN_VALUE").Value)
Console.WriteLine("CompanyName= " &
_
myCommand.Parameters("@CompanyName").Value)
Catch e As Exception
Console.WriteLine(e.ToString)
Finally
myConnection.Close
End Sub
End Module
End Namespace
[C#]
using System;
using System.Data;
using
System.Data.ADO;
class ConsoleApplication
{
public static void Main(String[]
args)
{
if (args.Length < 1 )
{
Console.WriteLine("Requires a CustomerID to be passed in as a
argument");
Console.WriteLine("example: OutParamsWithACommand ALFKI");
return 0;
}
// Create a new Connection and DataSetCommand
ADOConnection myConnection = new
ADOConnection("provider=SQLOLEDB;database=northwind;data
source=delphi;user
id=sa;");
ADOCommand
myCommand = new ADOCommand("GetCompanyName", myConnection);
myCommand.CommandType =
CommandType.StoredProcedure;
ADOParameter workParam = null;
workParam = myCommand.Parameters.Add("RETURN_VALUE",
ADODBType.Integer);
workParam.Direction = ParameterDirection.ReturnValue;
workParam =
myCommand.Parameters.Add("@CustomerID", ADODBType.Char, 5);
workParam.Direction =
ParameterDirection.Input;
workParam.Value = args[0];
workParam =
myCommand.Parameters.Add("@CompanyName", ADODBType.Char, 40);
workParam.Direction =
ParameterDirection.Output;
try
{
myConnection.Open();
myCommand.Execute();
Console.WriteLine("CompanyName= " +
myCommand.Parameters["RETURN_VALUE"].Value);
Console.WriteLine("CompanyName= " +
myCommand.Parameters["@CompanyName"].Value);
}
catch(Exception e)
{
Console.WriteLine(e.ToString());
}
finally
{
myConnection.Close();
}
}
}
Единственное требование, для получения возвращаемое из хранимой процедуры
значение необходимо указать направление первого параметра (в совокупности
параметров), которое должно обозначать вывод (Output).
4.13. Объединённое подключение (Connection Pooling)
Для разработки высокопроизводительных приложений, Вы должны использовать
объединенное подключение. При использовании ADO Managed Provider, объединенное
подключение не используется, т.к. ADO Managed Provider создаёт его
автоматически. Для организации объединенного подключения SQL Managed Provider
использует службу Windows 2000 Component Services, и Вы должны для этого
следовать указанным ниже шагами:
1. Определите подкласс SQLConnection, используя код, представленный
ниже.
2. Включите в код строки, которые организуют объединенное
подключение.
3. Подключите Microsoft.ComServices.dll.
4. Произвольно
конфигурируйте характеристики объединения компонент, используя пользовательский
интерфейс Component Services.
4.13.1. Определение подкласса SQLConnection
Следующий код определяет подкласс для класса SQLConnection. Используйте
Guidgen.exe, чтобы получить новый Guid (FD5ABffd-D026-4684-8515-2BB5184E8991 в
представленной ниже выборке).
[ConstructionEnabled(default="Server=servername …"] // required here or in
gui
[ObjectPooling(true)] //
required
[Transaction(TransactionOption.Supported)] // optional
public
class myConnection : SQLPooledConnection
{
// class is empty unless adding
SetComplete and SetAbort methods, e.g.
// public void SetComplete() {
ContextUtil.SetComplete(); }
// public void SetAbort() {
ContextUtil.SetAbort(); }
}
4.13.2. Включение в код строк, которые организуют объединенное
подключение.
Класс объединённого подключения используется очень похоже на обычный
SQLConnection, как это показано в следующем примере кода:
IReader iReader;
myConnection
conn = new myConnection();
conn.Open
SQLCommand cmd = new
SQLCommand("SELECT * FROM Orders", conn);
cmd.Execute(ref
iReader);
conn.Close();
Модель объединённого подключения та же самая, что и у не объединенных
подключений. Однако, очень важно вызывать Close, при завершении объединённого
подключения и возврате назад в объединение.
4.13.3. Подключение Microsoft.ComServices.dll
COM+ Services DLL должен быть подключён, чтобы обеспечить поддержку
объединения.
4.13.4. Конфигурирование объединения компонент
Хотя объединенный объект подключения может быть жёстко конфигурирован, через
установку атрибут во времени компиляции, часто желательно изменять поведение
объекта после того, как приложение было развернуто. Чтобы это обеспечить, такие
атрибуты, как строка подключения (также называемая строкой конструкции) и
транзакционные ограничения могут быть установлены в Component Services из
Administrative Tools.
4.13.5. Поддержка транзакций
Одно из преимуществ механизма объединения это то, что обеспечивается
поддержка управления поведением транзакционного механизма. SQL Managed provider
определяет, имеет ли текущий контекст распределенную транзакцию, связанную с
задачей, и если это так, обеспечит автоматическую организацию транзакции. Вы
можете управлять поведением транзакционным механизмом объединенного подключения
во времени компиляции с помощью TransactionAttribute или во время выполнения с
помощью Component Services из Administrative Tools.
Для получения большей
информации о распределенных транзакциях, см. раздел, Working with
Transactions.
4.14. Команды генерируемые автоматически
Для простых модификаций, SQL и ADO Managed provider обеспечивают
автоматическую генерацию команд (AutoGen). Если свойства команды delete, insert
или update не установлены пользовательским, это механизм автоматически
генерирует отсутствующие инструкции, чтобы создать полнофункциональный запрос.
Автоматическая генерация команд вызывается, если для объекта DataSetCommand
любое из указанных свойств не установлено.
Для работы автоматической
генерации, как минимум, для команды должно быть определено свойство
SelectCommand. Механизм Авто автоматической генерации (AutoGen) команд
генерирует другие типы команд (insert, update, и delete) когда они установлены в
Null. Также требуется присутствие по крайней мере одного первичного ключа или
уникального столбца. Если ничего из этого нет, будет получено исключение
InvalidOperation, и команда не будет сгенерирована. Команда Select должна
возвратить ключевой столбец, который создаётся во время авто-генерации, а не во
время исполнения метода FillDataSet.
Данные, заданные свойствами
SelectCommand, определяют форму последующих автоматически сгенерированных команд
insert, update и delete. В дополнение к использованию полученной информации о
ключевом столбце, возвращенные имена таблицы и столбцов также используются
логикой авто-генерации
4.14.1. Правила организации команды Update
Автоматически сгенерированная команда update обновляет значения всех столбцов
кроме столбцов non-updateable (например, в случае тождеств или выражений).
Команда имеет следующий формат:
UPDATE
table
SET
pkCol1=@pkCol1Alias
...
pkColN=@pkColNAlias
Col1=@Col1Alias
...
ColN=@ColNAlias
WHERE
pkCol1=@oldpkCol1Alias AND
...
pkColN=@oldpkColNAlias
Rules for Generating Delete Commands
When auto-generated, the DELETE
command takes the form:
DELETE FROM
table
WHERE
pkCol1=@oldpkCol1Alias AND
...
pkColN=@oldpkColNAlias
4.14.2. Правила организации команды Insert
При автоматической генерации, команда insert вставляет значения во всех
столбцах, которые являются updateable (исключая столбцы identifies, expressions
или timestamps). Формат команды будет следующий:
INSERT INTO
table (
Col1,
...
ColN
)
VALUES (
@Col1,
...
@ColN
)
4.14.3. Ограничения логики механизма AutoGen
Следующие ограничения относятся к автоматически генерируемым
командам:
Только одна таблица.
Автоматически генерируемые команды
не генерирует insert, update или delete команды для таблиц, которые имеют
отношения с другими таблицами в пределах одного набора данных DataSet. AutoGen
работает только для одной таблицы. Автоматически генерируемые команды полагаются
на информацию первичного ключа, чтобы генерировать предложение «where» для
различных инструкций. Этой информации будет недостаточно, когда используются
объединённые (join) таблицы. При таком подходе, пользователь будет иметь
однозначную информацию, для каких данных была исполнена инструкция
update.
Ограничения на таблицу и названия(имена) столбца
Возможны
сбои в логике AutoGen, если имена столбцов или имена таблиц будут содержать
специальные символы (пробелы, точки, кавычки или другие не алфавитно-цифровые
символы) даже если они заключены в скобки. Поддерживаются только полностью
квалифицированные названия таблицы, например, schema.owner.table.
4.15. Добавление и удаление событий SQL Managed provider
Ниже перечислены события ADODataSetCommand класса. Для дополнительной
информации, см. ссылку на ADODataSetCommand класс.
| Событие (Event) |
Описание
|
| SQLRowUpdating |
Будет начата операция update для строки (запрос к одному из
методов update) |
| SQLRowUpdated |
Завершение операции update для строки (запрос к одному из методов
update) |
| SchemaChanging |
Будут изменены установки DataSetCommand. Эти изменения могут
затрагивать возвращаемые данные или отображение этих данных. |
| SchemaChanged |
Завершение изменений установок DataSetCommand. Эти изменения
могли затронуть возвращаемые данные или отображение этих данных. |
| SchemaMapping |
Отображение таблицы источника на таблицу DataSet
закончено. |
4.15.1. Обвальные исключения, порождённые обработчиком событий
Избегите обвальных исключений (throwing exceptions) при обработке методов в
коде, вызывающем событие. Если исключение распространяется от обработчика до
кода вызова, ни один ожидающий это событие процесс не будет уведомлен
относительно этого события. Эта проблема скрыта внутри .NET Framework.
4.15.2. Семантика добавления и удаления событий
Вы в любое время можете добавлять или удалять делегата из списка делегатов,
получающих специальное событие. Это будет происходить непосредственно в течение
выполнения функции отзыва. Отзыв может быть добавлен в список любое количество
раз. Если он добавлен больше чем единожды, он будет вызываться больше чем один
раз.
Не будет считаться ошибкой, и не будет выполнено никаких действий, если
Вы попытаетесь разрегистрировать делегата, который не был добавлен в список. Всё
это определяется поведением Delegate.Remove.
Все выполняемые SQL Managed
provider события являются многоадресными (multicast).
4.15.3. Сравнение делегатов при добавление и удаление событий
Эквивалентность делегатов при добавлении и удалении событий полностью зависит
от выполнения System.Delegate. При добавлении и удалении делегатов к событиям,
нет необходимости поддержать ссылку делегата. Следующий код демонстрирует
это:
conn.InfoMessageEvent += new
InfoMessageEventHandler(myStaticFunc);
...
...
conn.InfoMessageEvent -=
new InfoMessageEventHandler(myStaticFunc);
Два делегата рассматриваются равными даже в том случае, если они представляют
два различных экземпляра. Равенство/эквивалентность двух делегатов А и B
достигается:
- если скрытая (encapsulated) функция является статической,
функции для делегатов А и B равны;
- если скрытые функции (например, методы),
имена методов и экземпляры одинаковы для делегатов А и B;
- перечисленные
скрытые функции, содержащиеся в обоих делегатах эквивалентны.