Доступ к данным |Доступ к данным с помощью ADO.NET | 4. Использование Managed provider Дальше »
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;
- перечисленные скрытые функции, содержащиеся в обоих делегатах эквивалентны.

Доступ к данным |Доступ к данным с помощью ADO.NET | 4. Использование Managed provider Дальше »
Скачать электронную карту Ангарска бесплатно
Сайт управляется системой uCoz