Обнаружение вставки с дублированием первичного ключа - PullRequest
0 голосов
/ 02 июня 2018

Мой API позволяет попытаться добавить объекты с тем же первичным ключом.Однако я хотел бы отфильтровать исключение DbUpdateException, которое возникает по этой причине, потому что они просто загрязняют мои файлы журнала.Я использую Sqlite для тестирования и Postgres in Production, поэтому мне нужен способ, который мог бы вернуть мне какое-то исключение, чтобы выяснить причину.

Ответы [ 3 ]

0 голосов
/ 02 июня 2018

Что касается SQL Server, я бы предложил фильтрацию SqlException.Number 2601 и 2627 для выявления нарушений ограничений первичного ключа / уникального ключа:

  • 2601: невозможно вставить строку дублированного ключав объекте "%. * ls" с уникальным индексом "%. * ls".Значение дублированного ключа% ls.
  • 2627: Нарушение ограничения% ls "%. * Ls".Невозможно вставить повторяющийся ключ в объект "%. * Ls".Значение ключа-дубликата:% ls.

Пример кода:

try {
    using(var db = new DatabaseContext()) {
        db.SaveChanges();
    }
}
catch(UpdateException ex) {
    var sqlException = ex.InnerException as SqlException;        
    if(sqlException != null && sqlException.Errors.OfType<SqlError>()
        .Any(se=>se.Number == 2601 || se.Number == 2627 /* primary key/unique key constraint violation */)) {            
        // handle duplicate
    }
}

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

0 голосов
/ 02 июня 2018

В итоге у меня появился некоторый класс расширения для исключения DbUpdateException, все еще хакерский:

public static class UpdateExceptionHelper
{
    public enum UpdateExceptionKind
    {
        UniqueViolation,
        ForeignKeyViolation,
        Unknown
    }

    public static UpdateExceptionKind Kind(this DbUpdateException dbUpdateException)
    {
        var inner = dbUpdateException.InnerException;
        switch (inner)
        {
            case null:
                return UpdateExceptionKind.Unknown;
            case SqliteException sqlite:
                return sqlite.Kind();
            case PostgresException postgres:
                return postgres.Kind();
            default:
                throw new Exception($"Unsupported Database Provider with Exception Type: {inner.GetType().Name}");


        }
    }

    private static UpdateExceptionKind Kind(this PostgresException e)
    {
        const string UNIQUE_VIOLATION = "23505";
        const  string FOREIGN_KEY_VIOLATION = "23503";

        switch (e.SqlState)
        {
            case UNIQUE_VIOLATION:
                return UpdateExceptionKind.UniqueViolation;
            case FOREIGN_KEY_VIOLATION:
                return UpdateExceptionKind.ForeignKeyViolation;
            default:
                return UpdateExceptionKind.Unknown;
        }
    }

    private static UpdateExceptionKind Kind(this SqliteException e)
    {
        const int SQLITE_CONSTRAINT_UNIQUE = 2067;
        const int SQLITE_CONSTRAINT_PRIMARYKEY = 1555;
        const int SQLITE_CONSTRAINT_FOREIGNKEY = 787;

        switch (e.SqliteExtendedErrorCode)
        {
            case SQLITE_CONSTRAINT_PRIMARYKEY:
            case SQLITE_CONSTRAINT_UNIQUE:
                return UpdateExceptionKind.UniqueViolation;
            case SQLITE_CONSTRAINT_FOREIGNKEY:
                return UpdateExceptionKind.ForeignKeyViolation;
            default:
                return UpdateExceptionKind.Unknown;
        }
    }
}
0 голосов
/ 02 июня 2018

Для Sql Server вы можете использовать свойство SqlException.Number внутреннего исключения DbUpdateException of, чтобы идентифицировать ошибку Нарушение PRIMARY KEY .Для Нарушение ПЕРВИЧНОГО КЛЮЧА SqlException.Number = 2627.Вы можете попробовать что-то вроде этого:

try
{
    db.SaveChanges();
}
catch (DbUpdateException ex)
{
    if (ex.InnerException is SqlException sqlEx && sqlEx.Number == 2627)
    {
    }
}

Я считаю, что для Sqlite и Postgres идея должна быть одинаковой

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...