C# Реализация UPDATE для SQLite. Часть 4
Вот и заканчиваем писать шаблон класса bdFacade. Подошла очередь и для операции UPDATE. Кто не читал предыдущие статьи из этой серии, рекомендую, для начала, прочитать первые три части — часть 1, часть 2 и часть 3. Когда я только начинал писать шаблон для операции обновления данных в различных своих программах, у меня было много идей, как требуется реализовать метод, который будет универсальным. Хотя, то что вы увидите сегодня, не является универсальным на 100%. После этой статьи у нас будет готовый класс, который позволит легко работать с базой данных в наших программах.
Реализация
Для изменения данных будет только один метод, но навороченный. Назовем его нетривиально Update. Можно, конечно писать кучу перегружаемых братьев, но я не вижу в этом смысла. Если запрос к базе данных будет сложным, то для него требуется писать отдельный метод.
Перед тем как писать основной метод, предлагаю рассмотреть как в C# реализовуются исключения, определенный пользователем. Так вы научитесь еще и писать свои обработчики исключений.
class ExceptionWarning : Exception
{
private string _messageText;
public string MessageText
{
get { return _messageText; }
}
public ExceptionWarning(string messagetext)
: base()
{
_messageText = messagetext;
}
}
Как видите, это обычный класс, который наследуется от системного класса Exception. Мы задали переменную _messageText, в которой будет храниться текст сообщения. Аксессор MessageText задает свойство и позволяет использовать переменную только для чтения. Далее конструктор задает значение нашей переменной. Класс написан. Можете добавить еще переменных и перегруженных конструкторов.
Займемся методом UPDATE. Смотрим код:
public void Update(string tablename, ParametersCollection collection, object[] whereparams, string whereseparator)
{
ConnectionState previousConnectionState = ConnectionState.Closed;
using (SQLiteConnection connect = new SQLiteConnection(ConnectionString))
{
try
{
//проверяем переданные аргументы
if (whereparams.Length == 0) throw (new ExceptionWarning("Ошибка! Не указано ни одно условие"));
if (whereparams.Length > 0 && whereseparator.Trim().Length == 0) throw (new ExceptionWarning("При использовании нескольких условий, требуется указать разделитель OR или AND"));
previousConnectionState = connect.State;
if (connect.State == ConnectionState.Closed)
{
connect.Open();
}
int i = 0;
//готовим переменную для сбора полей и их значений
string sql_params = string.Empty;
bool ifFirst = true;
SQLiteCommand command = new SQLiteCommand(connect);
//в цикле создаем строку запроса
foreach (Parameter param in collection)
{
if (ifFirst)
{
sql_params = param.ColumnName + " = @param" + i;
ifFirst = false;
}
else
{
sql_params += "," + param.ColumnName + " = @param" + i;
}
//и добавляем параметры с таким же названием
command.Parameters.Add("@param" + i, param.DbType).Value = param.Value;
i++;
}
//условия для запроса
string sql_where = string.Empty;
ifFirst = true;
//собираем строку с условиями
foreach (object item in whereparams)
{
if (ifFirst)
{
sql_where = item.ToString();
ifFirst = false;
}
else
{
sql_where += " " + whereseparator + " " + item;
}
}
sql_where = "WHERE " + sql_where;
//собираем запрос воедино
command.CommandText = string.Format("UPDATE {0} SET {1} {2}", tablename, sql_params, sql_where);
//выполняем запрос
command.ExecuteNonQuery();
}
catch (ExceptionWarning message)
{
System.Windows.Forms.MessageBox.Show(message.MessageText, "Ошибка при обновлении данных в таблице " + tablename, MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
catch (Exception error)
{
System.Windows.Forms.MessageBox.Show(error.Message, "Ошибка при обновлении данных в таблице " + tablename, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
if (previousConnectionState == ConnectionState.Closed)
{
connect.Close();
}
}
}
}
Метод принимает 4 аргумента: 1) имя таблицы, с которой будем работать; 2) коллекцию параметров, с которой мы уже работали, когда писали метод INSERT; 3) массив условий; 4) разделитель между условиями, если их (условий) больше одного.
Строки
if (whereparams.Length == 0) throw (new ExceptionWarning("Ошибка! Не указано ни одно условие"));
if (whereparams.Length > 0 && whereseparator.Trim().Length == 0) throw (new ExceptionWarning("При использовании нескольких условий, требуется указать разделитель OR или AND"));
...
catch (ExceptionWarning message)
{
System.Windows.Forms.MessageBox.Show(message.MessageText, "Ошибка при обновлении данных в таблице " + tablename, MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
проверяют переданные аргументы и вызывают наш класс исключений, передавая в него строку с каким-то текстом.
Далее наша стандартная проверка состояния подключения к базе данных. Следующий цикл foreach готовит строку для замены данных в таблице. Сначала создаем текстовую переменную sql_params и там же в объект SQLiteCommand добавляем данные из коллекции параметров. Также мы делали и в методе вставки данных. Второй foreach генерирует строку для условий. Т.к. теоретически условий для запроса может быть несколько и они будут разделяться операторами AND или OR. Поэтому, если вы передаете несколько условий — задавайте переменную whereseparator. Иначе, будет вызвано исключение.
В итоге строка
command.CommandText = string.Format("UPDATE {0} SET {1} {2}", tablename, sql_params, sql_where);
соберет все переменные в одну строку запроса.
Можно было бы возвращать данные об успешности выполнения нашей операции, как это мы делали, когда писали DELETE, но я не хочу усложнять код. Этот метод можно условно разбить на три части для более легкого понимания. Часть в начале и в самом конце отвечают за подключение к базе данных. Часть, которая проверяет переданные аргументы и циклы для сбора и подготовки строки запроса. Ну и пару строк, которые собственно отправляют запрос.
Заключение
Теперь можно сказать что это первая полная версия класса dbFacade. Мы написали все основные операции. Я думаю, что класс будет кому-то полезен и вы можете помочь мне его совершенствовать и дорабатывать.
Хочу еще отметить тот факт, что если поле проиндексировано, его нельзя использовать в запросе, где условия разделены оператором OR. Чтобы вам было понятно приведу пример.
В базе данных есть проиндексированное поле testdate. Код при создании базы следующий:
CREATE UNIQUE INDEX indx ON Test (testdate)
Если выполнить запрос
UPDATE text SET title = 'простой текст' WHERE id = 1 OR id = 2
будет вызвано исключение с ошибкой Abort due constraint violation column testdate is not unique. Если вместо OR записать AND, все отработает чисто. Правда таких записей не будет найдено, если id это поле с autoincrement.
Код проекта к статье
Код класса dbFacade
Популярность: 20%
Если у вас возникли вопросы, вы можете оставить их в комментариях


Здравствуйте!
А где описание класса Select и как с ним работать?