我的api允许尝试添加具有相同主键的实体。但是我想过滤由于这个原因发生的DbUpdateException,因为那些只会污染我的日志文件。 我使用Sqlite进行测试,并在生产中使用Postgres,所以我希望有一种方法可以让我得到一些异常来获得原因。
答案 0 :(得分:0)
对于Sql Server,您可以使用DbUpdateException
内部异常的SqlException.Number
属性来识别违反PRIMARY KEY 错误。对于违反PRIMARY KEY SqlException.Number = 2627
。
你可以尝试这样的事情:
try
{
db.SaveChanges();
}
catch (DbUpdateException ex)
{
if (ex.InnerException is SqlException sqlEx && sqlEx.Number == 2627)
{
}
}
我相信Sqlite
和Postgres
这个想法应该是相同的
答案 1 :(得分:0)
关于SQL Server,我建议过滤SqlException.Number 2601和2627以捕获主键/唯一键约束违规:
示例代码:
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
}
}
另一种方法是在插入之前检查重复项 为此,请确保在原子事务中运行这两个操作以避免并发问题。否则,两个或多个事务可能会尝试插入具有相同密钥的记录。
答案 2 :(得分:0)
我最终得到了一些DbUpdateException的扩展类仍然有点hacky:
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;
}
}
}