Шрифт:
Интервал:
Закладка:
В реальной программе была бы предусмотрена логика обработки такой ошибки, но в рассматриваемом примере просто генерируется новое исключение. Добавьте в класс InventoryDal следующий метод:
public void DeleteCar(int id)
{
OpenConnection();
// Получить идентификатор автомобиля, подлежащего удалению,
// и удалить запись о нем.
string sql = $"Delete from Inventory where Id = '{id}'";
using (SqlCommand command = new SqlCommand(sql, _sqlConnection))
{
try
{
command.CommandType = CommandType.Text;
command.ExecuteNonQuery();
}
catch (SqlException ex)
{
Exception error = new Exception("Sorry! That car is on order!", ex);
throw error;
}
}
CloseConnection();
}
Добавление логики обновления
Когда речь идет об обновлении существующей записи в таблице Inventory, первым делом потребуется решить, какие характеристики будет позволено изменять вызывающему коду: цвет автомобиля, его дружественное имя, модель или все перечисленное? Один из способов предоставления вызывающему коду полной гибкости заключается в определении метода, принимающего параметр типа string, который представляет любой оператор SQL, но в лучшем случае это сопряжено с риском.
В идеале лучше иметь набор методов, которые позволяют вызывающему коду обновлять запись разнообразными способами. Тем не менее, определите для такой простой библиотеки доступа к данным единственный метод, который дает вызывающему коду возможность обновить дружественное имя указанного автомобиля:
public void UpdateCarPetName(int id, string newPetName)
{
OpenConnection();
// Получить идентификатор автомобиля для модификации дружественного имени.
string sql = $"Update Inventory Set PetName = '{newPetName}'
Where Id = '{id}'";
using (SqlCommand command = new SqlCommand(sql, _sqlConnection))
{
command.ExecuteNonQuery();
}
CloseConnection();
}
Работа с параметризированным и объектами команд
В настоящий момент внутри логики вставки, обновления и удаления для типа InventoryDal используются жестко закодированные строковые литералы, представляющие каждый запрос SQL. В параметризированных запросах параметры SQL являются объектами, а не простыми порциями текста. Трактовка запросов SQL в более объектно-ориентированной манере помогает сократить количество опечаток (учитывая, что свойства строго типизированы). Вдобавок параметризированные запросы обычно выполняются значительно быстрее запросов в виде строковых литералов, т.к. они подвергаются разбору только однажды (а не каждый раз, когда строка с запросом SQL присваивается свойству CommandText). Параметризированные запросы также содействуют в защите против атак внедрением в SQL (хорошо известная проблема безопасности доступа к данным).
(window.adrunTag = window.adrunTag || []).push({v: 1, el: 'adrun-4-390', c: 4, b: 390})Для поддержки параметризированных запросов объекты команд ADO.NET содержат коллекцию индивидуальных объектов параметров. По умолчанию коллекция пуста, но в нее можно вставить любое количество объектов параметров, которые отображаются на параметры-заполнители в запросе SQL. Чтобы ассоциировать параметр внутри запроса SQL с членом коллекции параметров в объекте команды, параметр запроса SQL необходимо снабдить префиксом в виде символа @ (во всяком случае, когда применяется Microsoft SQL Server; не все СУБД поддерживают такую систему обозначений).
Указание параметров с использованием типа DbParameter
Перед построением параметризированного запроса вы должны ознакомиться с типом DbParameter (который является базовым классом для объекта параметра поставщика). Класс DbParameter поддерживает несколько свойств, которые позволяют конфигурировать имя, размер и тип параметра, а также другие характеристики, включая направление движения параметра. Некоторые основные свойства типа DbParameter описаны в табл. 21.6.
Давайте теперь посмотрим, как заполнять коллекцию совместимых с DBParameter объектов, содержащуюся в объекте команды, для чего переделаем методы InventoryDal для использования параметров.
Обновление метода GetCar()
В исходной реализации метода GetCar() при построении строки SQL для извлечения данных об автомобиле применяется интерполяция строк С#. Чтобы обновить метод GetCar(), создайте экземпляр SqlParameter с соответствующими значениями:
SqlParameter param = new SqlParameter
{
ParameterName = "@carId",
Value = id,
SqlDbType = SqlDbType.Int,
Direction = ParameterDirection.Input
}
Значение ParameterName должно совпадать с именем, используемым в запросе SQL (который будет модифицирован следующим), тип обязан соответствовать типу столбца базы данных, а направление зависит от того, применяется параметр для отправки данных в запрос (ParameterDirection.Input) или он предназначен для возвращения данных из запроса (ParameterDirection.Output). Параметры также могут определяться как InputOutput или ReturnValue (возвращаемое значение, например, из хранимой процедуры).
- Понимание SQL - Мартин Грубер - Базы данных