В реальной программе была бы предусмотрена логика обработки такой ошибки, но в рассматриваемом примере просто генерируется новое исключение. Добавьте в класс
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 (хорошо известная проблема безопасности доступа к данным).
Для поддержки параметризированных запросов объекты команд 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
(возвращаемое значение, например, из хранимой процедуры).