Entity Framework: как правильно обрабатывать исключения, возникающие из-за ограничений SQL
Я использую Entity Framework для доступа к своим данным SQL. У меня есть некоторые ограничения в схеме базы данных, и мне интересно, как обрабатывать исключения, вызванные этими ограничениями.
В качестве примера я получаю следующее исключение в случае, когда два пользователя пытаются одновременно добавить (почти) идентичный объект в базу данных.
System.Data.UpdateException
"An error occurred while updating the entries. See the InnerException for details."
(inner exception) System.Data.SqlClient.SqlException
"Violation of UNIQUE KEY constraint 'Unique_GiftId'. Cannot insert duplicate key in object 'dbo.Donations'.\r\nThe statement has been terminated."
Как правильно поймать это конкретное исключение?
Грязное решение:
catch (UpdateException ex)
{
SqlException innerException = ex.InnerException as SqlException;
if (innerException != null && innerException.Message.StartsWith("Violation of UNIQUE KEY constraint 'Unique_GiftId'"))
{
// handle exception here..
}
else
{
throw;
}
}
Теперь, когда этот подход работает, он имеет некоторые недостатки:
- Безопасность типа: код зависит от сообщения об исключении, которое содержит имя уникального столбца.
- Зависимость от классов SqlCLient (сломанная абстракция)
Знаете ли вы лучшее решение для этого?
Спасибо за все отзывы..
Примечание: Я не хочу кодировать ограничения вручную на уровне приложения, я хочу иметь их в БД.
Ответы
Ответ 1
Вы должны уловить номер ошибки SQL (который SqlException.Number)
В этом случае это 2627, который был неизменным для SQL Server.
Если вы хотите абстракцию, у вас всегда будет какая-то зависимость от механизма базы данных, потому что каждый из них будет генерировать разные номера исключений и сообщения.
Ответ 2
Один из способов - проверить свойство Свойство Ошибок внутреннего исключения SqlException. Класс SqlError имеет Свойство Number, которое идентифицирует точную ошибку. См. Таблицу master.dbo.sysmessages для списка всех кодов ошибок.
Конечно, это все еще связывает вас с Sql Server. Я не знаю, как отвлечь это, кроме того, что вы катите свой "анализатор исключений EF".
Ответ 3
Этот сценарий не должен происходить, поскольку ключ никогда не должен назначаться явно при использовании EF; что позволяет контексту назначать соответствующий. Если это проблема с concurrency, вы должны сделать обновление в области транзакции.
Затем, если у вас есть UpdateException, вы можете повторить обновление снова. Вы можете безопасно сделать это в области транзакций и только заполнить область, когда обновление будет полным. В этом случае вероятность обновления, следующего в следующий раз, больше, чем первая.