Как правильно проверить ошибки каскадного сохранения?

При создании пользователя строка должна быть вставлена ​​в таблицу User и Email. Он может потерпеть неудачу в любом из них (уникальные ограничения). Как я могу узнать, что является причиной отказа? Я думал об использовании блокировки и запросов к базе данных до вставки или разбора возвращаемого SqlException (что я предпочел бы не делать).

Изменить: я должен был упомянуть, что это будет работать на нескольких машинах одновременно, и я хотел бы, чтобы он поддерживал разные базы данных.

Редактировать 2: Мое решение в конечном итоге использовать блокировку вокруг проверки на наличие дубликатов. Хранимые процедуры были опцией, но я не хотел помещать бизнес-логику в базу данных. Я прокомментировал для других, что я знал об условиях гонки на веб-ферме, но редкость обстоятельств не требовала дальнейшей работы.

4 ответа

Решение

Обработка исключений должна использоваться для захвата не простых сценариев, таких как отключение базы данных или превышение времени ожидания команды. Если у вас есть ограничения в отношении уникальности пользователя и уникальности электронной почты, вы должны действительно проверить их перед отправкой данных. Использование ограничений проверки / индексации в качестве способа обработки этих сценариев приведет к путанице в долгосрочной перспективе. Кроме того, ключевой лучший метод в обработке ошибок - никогда не сообщать конечному пользователю подробности того, почему произошла ошибка.

Используйте хранимую процедуру и проверьте известные условия, которые приведут к сбою транзакции внутри транзакции, например:

BEGIN TRANSACTION
IF EXISTS (SELECT UserID FROM User WHERE UserID = @UserID)
   BEGIN
      ROLLBACK
      SELECT 'User already exists in the User table.'
      RETURN 1
   END

IF EXISTS (SELECT UserID FROM Email WHERE UserID = @UserID)
   BEGIN
      ROLLBACK
      SELECT 'User already exists in the Email table.'
      RETURN 2
   END

INSERT INTO User ...
INSERT INTO Email ...
COMMIT
RETURN 0

Это фактически использует два механизма для возврата ошибки (код возврата и набор результатов); обычно имеет смысл использовать только один.

Я бы не стал полагаться на ограничения таблицы для проверки данных. Проверьте данные с помощью запроса перед вставкой. Исключение составляют дорогие объекты для создания. Кроме того, я предпочитаю иметь ограничения для предотвращения неверных данных, но не для проверки. Я думаю об ограничении как о ремне безопасности для стола. Он должен вызываться только в случае, если что-то не так. Бизнес-логика должна проверять все данные перед вставкой. Не полагайтесь на хранимые процедуры, если вы можете использовать базы данных, которые их не поддерживают.

Вот общий способ, которым я бы справился с этим.

        private void Form1_Load(object sender, EventArgs e)
    {
        OleDbConnection conn = null;
        OleDbTransaction t = null;
        try
        {
            conn = new OleDbConnection("a database");

            conn.Open();

            //query both tables to prevent insert fail, 
            //obviously UserID should be parameter.
            var cmd = new OleDbCommand("select count(*) from User where UserID = 1", conn);
            var count = (double)cmd.ExecuteScalar();

            cmd.CommandText = "select count(*) from Email where UserID = 1";
            count += (double)cmd.ExecuteScalar();

            if (count != 0)
            {
                MessageBox.Show("Record exists");
                return;
            }

            t = conn.BeginTransaction();

            //insert logic goes here


            t.Commit();
        }
        catch (Exception x)
        {
            //we still need catch block, someone else may have updated the 
            //data after you checked but before you insert or db open may 
            //fail

            MessageBox.Show(x.Message);
            if (t != null)
                t.Rollback();
        }
        finally
        {                
            if (conn != null)
                conn.Close();
        }
    }

Вы должны ловить подобные случаи в своей бизнес-логике, а не полагаться исключительно на базу данных, чтобы получить искомую ошибку.

Другие вопросы по тегам