我试图捕获当我将具有给定用户名的现有用户插入数据库时引发的异常。正如标题所说,我正在使用 EF。当我尝试将用户插入数据库时引发的唯一异常是“UpdateException” - 如何提取此异常以识别其是否是重复异常或其他异常?
catch (UpdateException ex)
{
SqlException innerException = ex.InnerException as SqlException;
if (innerException != null && innerException.Number == ??????)
{
// handle exception here..
}
else
{
throw;
}
}
在
??????
处输入与违反唯一约束相对应的正确数字(我从头到尾都不知道)。
因为我将 EntityFramework 与 C# 一起使用,所以我必须对此进行一些小的更改 - 希望它对任何人都有帮助...
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateException ex)
{
SqlException innerException = ex.InnerException.InnerException as SqlException;
if (innerException != null && (innerException.Number == 2627 || innerException.Number == 2601))
{
//your handling stuff
}
else
{
throw;
}
}
我的问题出现是因为我需要 DbUpdateException 而不是 UpdateException,并且我的 InnerException 对象有一个额外的 InnerException 对象,其中包含我需要的 Number...
现在在 C# 6.0 中你应该能够做这样的事情:
catch (UpdateException ex) when ((ex.InnerException as SqlException)?.Number == ??????)
{
// Handle exception here
}
现在在 C# 7 中您可以使用 is 运算符
// 2627 is unique constraint (includes primary key), 2601 is unique index
catch (UpdateException ex) when (ex.InnerException is SqlException sqlException && (sqlException.Number == 2627 || sqlException.Number == 2601))
{
}
如果您需要在 Entity Framework Core 中执行相同的操作,您可以使用我构建的库,该库提供强类型异常,包括
UniqueConstraintException
: EntityFramework.Exceptions
它允许您捕获这样的类型异常:
using (var demoContext = new DemoContext())
{
demoContext.Products.Add(new Product
{
Name = "a",
Price = 1
});
demoContext.Products.Add(new Product
{
Name = "a",
Price = 10
});
try
{
demoContext.SaveChanges();
}
catch (UniqueConstraintException e)
{
//Handle exception here
}
}
您所要做的就是从 Nuget 安装它并在您的
OnConfiguring
方法中调用它:
class DemoContext : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<ProductSale> ProductSale { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseExceptionProcessor();
}
}
关注@peteski 和@Sam
or
关键字自 C# 9 起可用:
catch (UpdateException ex) when (ex.InnerException is SqlException { Number: 2627 or 2601 })
{
}
我认为如果你阻止异常的发生会更好。如果您的代码可行,我会执行以下操作:
使用实体框架时,最好的办法是首先尝试使用LINQ的SingleOrDefault来获取会给你带来麻烦的条目。现在,您可以使用要插入的实例更新获取的实体,如果您使用它,可以为您提供自动递增的 ID 号。如果 SingleOrDefault 为 null,您可以安全地添加您的实体。
代码示例:
public override void AddOrUpdate(CustomCaseSearchCriteria entity)
{
var duplicateEntityCheck = GetSingleByUniqueConstraint(entity.UserCode, entity.FilterName);
if (duplicateEntityCheck != null)
{
duplicateEntityCheck.Overwrite(entity);
base.Update(duplicateEntityCheck);
}
else
base.Add(entity);
}
public virtual CustomCaseSearchCriteria GetSingleByUniqueConstraint(string userCode, string filterName)
{
return GetAllInternal().SingleOrDefault(sc => sc.UserCode == userCode && sc.FilterName == filterName);
}
在 EF 核心中,UpdateException 变成了 DbUpdateException。不能保证内部异常一定是 SQLException,因此,您需要循环遍历内部异常来查找 SqlException,例如:
try
{
return await context.SaveChangesAsync();
}
catch (DbUpdateException ex)
{
var inner = ex.InnerException;
while(inner != null)
{
if (inner is SqlException sqlException)
{
if (sqlException.Number is 2627 or 2601)
throw/return/log here
break;
}
inner = inner.InnerException;
}
// the error was not due a duplicate
}
对于任何数据库类型,您可以使用反射和相应的错误号(对于 MySql 1062):
try
{
var stateEntries = base.SaveChanges();
}
catch (Exception e)
{
if(e is DbUpdateException)
{
var number = (int)e.InnerException.GetType().GetProperty("Number").GetValue(e.InnerException);
if (e.InnerException!= null && (number == 1062))
{
//your handling stuff
}
else
{
messages.Add(e.InnerException.Source, e.InnerException.Message);
}
}
else if (e is NotSupportedException || e is ObjectDisposedException || e is InvalidOperationException)
{
messages.Add(e.InnerException.Source, e.InnerException.Message);
}
}