C# Реализация UPDATE для SQLite. Часть 4

И немного рекламы: канцтовары - канцтовары оптом http://viland.ua/catalog/kantstovary-i-uchebniki/kantstovary.html.

Вот и заканчиваем писать шаблон класса 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%

Теги: , , ,

Если у вас возникли вопросы, вы можете оставить их в комментариях

Комментарии к статье

Один ответ на “C# Реализация UPDATE для SQLite. Часть 4”

Оставить комментарий

(обязательно)

(обязательно)