Подтвердить что ты не робот

Как поймать ошибки БД и перевести их в значимую информацию для бизнес-уровня?

Обычно мне приходится вставлять некоторые данные в БД, и он не может быть вставлен, потому что в таблице есть ограничения, препятствующие этому. В приложении, которое я разрабатываю, некоторые бизнес-правила (например, "нет двух лиц с одинаковым типом и номером" или "XXXX-продукт уже зарегистрирован" ) выполняются с помощью UNIQUE или составных клавиш и других механизмов. Хотя я знаю, что СУБД выдает сообщение об ошибке (например, ORA-6346 или), я не знаю, как поймать эти ошибки в .net 4.0 и перевести их на ошибку, которая может иметь смысл для бизнес-уровня.

В качестве примера: я видел механизм вставки, который запрашивает БД, если регистр уже существует, а затем он вставляет данные, если это не так. Я хочу сделать это только с помощью запроса и поймать ошибку ограничения ограничений базы данных, потому что первый способ кажется мне очень неэффективным (DB может предупредить вас о дублировании с ошибкой).

Как я могу реализовать что-то подобное?

Примечание. Я думаю, что можно получить исключение из базы данных и использовать его код ORA-xxxx, чтобы попытаться выяснить, что произошло. Я не помню с точностью, если сообщение об ошибке показывает, какое ограничение (имя...) было нарушено, но код бизнес-уровня может содержать константы с именами ограничений и, из них, знать, что произошло.

4b9b3361

Ответ 1

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

Многие архитекторы систем предпочитают использовать базу данных в качестве немого хранилища данных и внедрять тип обработки ошибок и проверки целостности, о которых вы говорите, в среднем/прикладном уровне. Это совершенно правильный подход и особенно подходит для систем, требующих регулярных небольших выпусков, где бизнес-логика подвержена регулярным изменениям (гораздо проще перераспределить исполняемую среднюю неделю, чем согласовать выпуск базы данных), и где данные модель довольно проста.

Другой подход заключается в том, чтобы добавить определенную полупостоянную бизнес-логику в уровень базы данных. Это особенно эффективно, когда модель данных более сложна, и у вас есть хороший администратор баз данных!;)

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

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

PROCEDURE test() AS
  b VARCHAR2;
BEGIN

  -- if the following row exists, then DUP_VAL_ON_INDEX will be thrown 
  -- (assuming there is a primary key constraint)        

  INSERT INTO table(a,b,c)
  VALUES(1,2,3);  

  -- if there is no matching record, NO_DATA_FOUND will be thrown

  SELECT a
  INTO b
  FROM TABLE
  WHERE c = 'blah';  

EXCEPTION   -- both types of exception can be caught and embellished 
  WHEN DUP_VAL_ON_INDEX THEN
    raise_application_error(-20570, 'Attempted to insert a duplicate value', TRUE);
  WHEN NO_DATA_FOUND THEN
    raise_application_error(-20571, 'No matching row in table for value:' || 'blah', TRUE);
  WHEN OTHERS THEN
  rollback
END test;

Вы можете найти более подробную информацию здесь: http://download.oracle.com/docs/cd/B19306_01/appdev.102/b14261/errors.htm

Надеюсь, что это поможет.

Ответ 2

Есть несколько подходов, здесь в общем, что я буду делать:

  • Позвольте ошибке перейти от обратного вызова БД к управляемому коду.
  • Используйте компонент для проверки сообщения об ошибке, предоставленного SQL, и укажите соответствующее сообщение "пользовательский/бизнес-уровень".

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

Для # 2 в библиотеках MS Enterprise есть блок обработки ошибок, который (я думаю) позволяет вам делать это через конфигурацию; или если это не поможет вам закрыть.

Ответ 3

Я думал об этом недавно. Я сделал метод расширения, который принимает сообщение от SqlException и переводит его на что-то более полезное для конечного пользователя, используя регулярные выражения для извлечения полезной информации из сообщения об ошибке и String.Format, чтобы поместить эту информацию в новое сообщение,

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

Это сообщение об ошибке SQL:

Violation of UNIQUE KEY constraint 'uniq_ticket'. Cannot insert duplicate key in object 'dbo.TicketHeader'. The statement has been terminated.

Возвращает этот результат:

Save to table dbo.TicketHeader failed: Ticket number must be unique.

Я бы предположил, что он может работать очень точно для исключений, отправленных Oracle.

    public static class SqlExceptionExtension
    {
        private static readonly Dictionary<string, string> Messages;
        private static readonly Dictionary<string, string> Constraints;
        static SqlExceptionExtension()
        {
            Messages = new Dictionary<string, string> {{@"Violation of UNIQUE KEY constraint '(?<Constraint>.*)'. Cannot insert duplicate key in object '(.*)'. The statement has been terminated.", "Save to table {2} failed: {0}"}};
            Constraints = new Dictionary<string, string> { { "uniq_ticket", "Ticket number must be unique." } };
        }
        public static string BusinessLayerMessage(this Exception e)
        {
            foreach(var reg in Messages.Keys)
            {
                var match = Regex.Match(e.Message, reg);
                if (match.Success)
                {
                    string friendlyConstraint = "";
                    if (match.Groups["Constraint"] != null)
                    {
                        friendlyConstraint = Constraints[match.Groups["Constraint"].Value] ??
                                             match.Groups["Constraint"].Value;
                    }
                    var groups = match.Groups.Cast<Group>().Select(x => x.Value);
                    var strings = new [] {friendlyConstraint};
                    return String.Format(Messages[reg], strings.Concat(groups).ToArray());
                }
            }
            return "Unexpected Database error.";
        }
    }
}

Я бы предположил, что вы можете сделать это на индивидуальном SqlError или OracleError, включенном в ваше исключение, для получения более надежных результатов, это было всего лишь доказательством концепции.

Ответ 4

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

(pseudo-sql)
function create-user
    @username, @password, @name, @email
as

if @username already exists return 1  --duplicate username
if @email already exists return 2   --duplicate email

insert user values (@username, @password, @name, @email)
return 0   -- success

Ответ 5

Если вы ищете вдохновение, посмотрите, как NHibernate обрабатывает это с помощью интерфейса ISQLExceptionConverter. Вы можете увидеть примерную реализацию здесь.