4.
Использование Managed provider4.1.
Управление подключениями4.2.
Формат строки подключения – ADOConnection4.3.
Формат строки подключения – SQLConnection4.4.
Команды4.5.
DataReader4.6.
Использование хранимых процедур в команде4.7.
Создание простых DataSetCommand4.8.
Обновление базы данных с помощью DataSetCommand и
DataSet4.9.
Определение отображений таблиц и столбцов4.10.
Mapping/Changing отображение столбцов во время
исполнения4.11.
Использование параметров DataSetCommands4.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.
Ограничения логики механизма AutoGen4.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;
- перечисленные скрытые
функции, содержащиеся в обоих делегатах
эквивалентны.