ЯЗЫК ПРОГРАММИРОВАНИЯ С# 2005 И ПЛАТФОРМА .NET 2.0. 3-е издание - Эндрю Троелсен
Шрифт:
Интервал:
Закладка:
// Заполнение DataSet новыми DataTable.
DataSet myDS = new DataSet("Cars");
SqlDataAdapter dAdapt = new SqlDataAdapter("Select * From Inventory".cnStr);
dAdapt.Fill(myDS, "Inventory");
// Отображение содержимого.
PrintDataSet(myDS);
}
Обратите внимание на то, что адаптер данных создается с указанием SQL-оператора Select. Это значение будет использоваться для внутреннего построения объекта команды, которую затем можно будет получить, выбрав свойство SelectCommand. Далее, заметьте, что метод Fill() получает экземпляр типа DataSet и необязательное строковое имя, которое будет использоваться при установке свойства TableName нового объекта DataTablе (если вы не укажете имя таблицы, адаптер данных использует для таблицы имя Table).
Замечание. Метод Fill() возвращает целое число, соответствующее числу строк, затронутых SQL-запросом.
Как и следует ожидать, при передаче DataSet методу PrintDataSet() (реализованному в этой главе ранее) будет получен список всех строк таблицы Inventory базы данных Cars (рис. 22.18).
Отображение имен базы данных в понятные имена
Вы, скорее всего, знаете, что администраторы баз данных склонны создавать имена, таблиц и столбцов, которые нельзя назвать понятными для конечных пользователей. Но хорошей вестью является то, что объекты адаптера данных поддерживают внутреннюю строго типизованную коллекцию (DatаTableМаррing-Collection) типов System.Data.Common.DataTableMapping, доступную с помощью свойства TableMappings.
Рис. 22.18. Заполнение DataSet с помощью объекта адаптера данных
При желании вы можете использовать эту коллекцию для того, чтобы информировать DataTable о "дисплейных именах", которые должны использоваться при выводе содержимого. Предположим, например, что вы хотите отобразить имя Inventory, используемое для таблицы в рамках СУБД, в дисплейное имя Ассортимент. Кроме того, предположим, что вы хотите отобразить имя столбца CarID в виде Номер, а имя столбца PetName – в виде Название. Для этого в объект адаптера данных перед вызовом метода Fill() добавьте следующий программный код (и не забудьте указать using для пространства имен System.Data.Common).
static void Main(string[] args) {
…
// Отображение имен столбцов БД в имена, понятные пользователю.
DataTableMapping custMap = dAdapt.TableMappings.Add("Inventory", "Ассортимент");
custMap.ColumnMappings.Add("CarID", "Номер");
custMap.ColumnMappings.Add("PetName", "Название");
dAdapt.Fill(myDS, "Inventory");
…
}
Теперь, выполнив программу, вы обнаружите, что метод PrintDataSet() отображает "понятные" имена объектов DataTable и DataRow, а не имена, заданные структурой базы данных.
Исходный код. Проект FillDataSetWithSqIDataAdapter размещен в подкаталоге, соответствующем главе 22.
Обновление базы данных с помощью объекта адаптера данных
Адаптеры данных могут не только заполнять для вас таблицы объекта DataSet. Они могут также поддерживать набор объектов основных SQL-команд, используя их для возвращения модифицированных данных обратно в хранилище данных. При вызове метода Update() адаптера данных проверяется свойство RowState для каждой строки в DataTable и используются соответствующие SQL-команды, присвоенные свойствам DeleteCommand, InsertCommand и UpdateCommand, чтобы записать изменения данного DataTable в источник данных.
Чтобы проиллюстрировать процесс использования адаптера данных для возвращения изменении DataTable в хранилище данных, в следующем примере мы переработаем приложение CarsInventoryUpdater, созданное в этой главе ранее, чтобы на этот раз использовать DataSet и объект адаптера данных. Поскольку значительная часть приложения останется той же, сконцентрируем свое внимание на изменениях, которые необходимо сделать в методах DeleteCar(). UpdateCarPetName() и InsertNewCar() (чтобы уточнить детали, проверьте текст загружаемого программного кода для данного примера).
Первым основным изменением, которое требуется внести в приложение, является определение двух новых статических членов-переменных класса Program для представления DataSet и объекта соединения. Также, чтобы заполнить DataSet начальными данными, модифицируется метод Main().
class Program {
// Объект DataSet, доступный на уровне приложения.
public static DataSet dsСarInventory = new DataSet("CarsDatabase");
// Объект соединения, доступный на уровне приложения.
public static SqlConnection cnObj = new SqlConnection("uid-sa;pwd=;Initial Catalog=Cars;Data Source= (local)");
static void Main(string[] args) {
// Создание адаптера данных и заполнение DataSet.
SqlDataAdapter dAdapter = new SqlDataAdapter("Select * From Inventory", chObj);
dAdapter.Fill(dsCarInventory, "Inventory");
ShowInstructions();
// Программный код получения команды пользователя…
}
…
}
Обратите внимание и на то, что методы ListInventory(), DeleteCar(), UpdateCarPetName() и InsertNewCar() также были изменены с тем, чтобы они могли принять SqlDataAdapter в качестве параметра.
Установка свойства InsertCommand
При использовании адаптера данных для обновления DataSet первой задачей оказывается назначение свойствам UpdateCommand, DeleteCommand и InsertCommand действительных объектов команд (пока вы этого не сделаете, эти свойства возвращают null). Слово "действительные" для объектов команд здесь используется потому, что набор объектов команд, которые вы "подключаете" к адаптеру данных, изменяется в зависимости от таблицы, данные которой вы пытаетесь обновить. В этом примере соответствующей таблицей является таблица Inventory. И вот как выглядит измененный метод InsertNewCar().
private static void InsertNewCar(SqlDataAdapter dAdapter) {
// Сбор информации о новой машине.
…
// Формирование SQL-оператора Insert и подключение к DataAdapter.
string sql = string.Format("Insert Into Inventory" +
"(CarID, Make, Color, PetName) Values" +
"('{0}', '{1}', '{2}', '{3}')",
newCarID, newCarMake, newCarColor, newCarPetName);
dAdapter.InsertCommand = new SqlCommand(sql);
dAdapter.InsertCommand.Connection = cnObj;
// Обновление таблицы Inventory с учетом новой строки.
DataRow newCar = dsCarInventory.Tables["Inventory"].NewRow();
newCar["CarID"] = newCarID;
newCar["Make"] = newCarMake;
newCar["Color"] = newCarColor;
newCar["PetName"] = newCarPetName;
dsCarInventory.Tables["Inventory"].Rows.Add(newCar);
dAdapter.Update(dsCarInventory.Tables["Inventory"]);
}
После создания объекта команды он "подключается" к адаптеру с помощью свойства InsertCommand. Затем в DataTable таблицы Inventory добавляется новая строка, представленная объектом dsCarInventory. После добавления DataRow в DataTable адаптер выполнит SQL-команду, обнаруженную в свойстве InsertCommand, поскольку значением RowState этой новой строки является DataRowState.Added.
Установка свойства UpdateCommand
Модификации метода UpdateCarPetName() оказываются приблизительно такими же. Просто создайте новый объект команды и укажите его для свойства UpdateCommand.
private static void UpdateCarPetName(SqlDataAdapter dAdapter) {
// Сбор информации об изменяемой машине.
…
// Форматирование SQL-оператора Update и подключение к DataAdapter.
string sql = string.Format("Update Inventory Set PetName = '{0}' Where CarID = '{1}'", newPetName, carToUpdate);
SqlCommand cmd = new SqlCommand(sql, cnObj);
dAdapter.UpdateCommand = cmd;
DataRow[] carRowToUpdate = dsCarInventory.Tables["Inventory"].Select(string.Format("CarID = '{0}'", carToUpdata));
carRowToUpdate[0]["PetName"] = newPetName;
dAdapter.Update(daCarInventory.Tables["Inventory"]);
}
В данном случае, когда вы выбираете строку (с помощью метода Select()), для RowState указанной строки автоматически устанавливается значение DataRowState.Modified. Другим заслуживающим внимания моментом здесь является то, что метод Select() возвращает массив объектов DataRow, поэтому вы должны указать, какую именно строку требуется изменить.
Установка свойства DeleteCommand
Наконец, вы имеете следующую модификацию метода DeleteCar().
private static void DeleteCar(SqlDataAdaper dAdapter) {
// Получение номера удаляемой машины.
…
string sql = String.Format("Delete from Inventory where CarID = '{0}'"; carToDelete);
SqlCommand cmd = new SqlCommand(sql, cnObj);
dAdapter.DeleteCommand = cmd;
DataRow[] carRowToDelete = dsCarInventory.Tables["Inventory"].Select(string.Format("CarID = '{0}'", carToDelete));
carRowToDelete[0].Delete();
dAdapter.Update(dsCarInventory.Tables["Inventory"]);
}
В этом случае вы находите строку, которую нужно удалить (снова с помощью метода Select()), а затем устанавливаете для свойства RowState значение DataRowState.Deleted с помощью вызова Delete().
Исходный код. Проект CarslnventoryUpdaterDS размещен в подкаталоге, соответствующем главе 22.
Генерирование SQL-команд с помощью типов построителя команд
Вы должны согласиться с тем, что для работы с адаптерами данных может потребоваться ввод довольно большого объема программного кода, а также создание всех четырех объектов команд и соответствующей строки соединения (или DbConnection-объекта). Чтобы упростить дело, в .NET 2.0 каждый из поставщиков данных ADO.NET предлагает тип построителя команд. Используя этот тип, вы можете автоматически получать объекты команд, содержащие правильные типы команд Insert, Delete и Update на базе исходного оператора Select.