实体框架:如何捕获任何错误

时间:2019-02-18 06:50:35

标签: c# entity-framework error-handling .net-core .net-core-2.0

我正在尝试将数据插入具有很多not null约束的SQL Server表中:

CREATE TABLE [dbo].[Customer]
(
    [CustomerId] [int] IDENTITY(1,1) NOT NULL,
    [FirstName] [varchar](255) NOT NULL,
    [LastName] [varchar](255) NOT NULL,
    [AddressLine] [varchar](255) NOT NULL
    CONSTRAINT [PK_Customer] PRIMARY KEY CLUSTERED ([CustomerId] ASC)
 )

EF代码:

public virtual DbSet<Customer> Customer { get; set; }
modelBuilder.Entity<Customer>(entity =>
{
    entity.Property(e => e.FirstName)
        .HasMaxLength(255)
        .IsRequired()
        .IsUnicode(false);

    entity.Property(e => e.LastName)
            .HasMaxLength(255)
            .IsRequired()
            .IsUnicode(false);

    entity.Property(e => e.AddressLine)
            .HasMaxLength(255)
            .IsRequired()
            .IsUnicode(false);
  });

尝试向表中添加数据时,代码缺少列,因此无法插入数据库。对此我一无所知,也没有收到“ NOT NULL”错误,就像我在SQL数据库中看到的那样。

var source = new Customer();

source.FirstName = "Joe";  // missing Last Name and Address
_context.Customer.Add(source);

所以我添加了以下代码。这就解决了问题,但是如果出现任何数据库错误,并发,错误的数据类型等,我将如何使它失败?

try
{
   _context.SaveChanges();
}
catch (DbUpdateException e)
{
}

以下操作无效: 方法1和2:实施这些方法后,不再出现我们想要的not null错误。

try
{
   _context.SaveChanges();
}
catch (Exception e)
{
}


try
{
   _context.SaveChanges();
}
catch
{
}

4 个答案:

答案 0 :(得分:1)

try {
   var source = new Customer();
   source.FirstName = "Joe";

   _context.Customer.Add(source);
   _context.SaveChanges();
}
catch (Exception ex) {
   // ...
}

答案 1 :(得分:1)

您可以在catch块中检查异常的类型,以获取确切的异常消息,例如

try
{
    //Your code here
}
catch (Exception ex)
{
    if (ex.GetType() == typeof(DbEntityValidationException))
    {
        //Exception thrown from System.Data.Entity.DbContext.SaveChanges when validating entities fails.
    }
    else
    if (ex.GetType() == typeof(DbUnexpectedValidationException))
    {
        //Exception thrown from System.Data.Entity.DbContext.GetValidationErrors when an
        //exception is thrown from the validation code.
    }
    else
    {
        //All remaining exception here 
    }
}

或者您可以为每种异常类型使用不同的catch块

try
{
    //Your code here
}
catch (DbEntityValidationException de_ex)
{
    //Exception thrown from System.Data.Entity.DbContext.SaveChanges when validating entities fails.
}
catch (DbUnexpectedValidationException du_ex)
{
    //Exception thrown from System.Data.Entity.DbContext.GetValidationErrors when an
    //exception is thrown from the validation code.
}
catch (Exception ex)
{
    //All remaining exception here 
}

答案 2 :(得分:1)

DbContext.SaveChanges描述了您可能期望的异常。确定要捕获的异常,而不要捕获的异常。

如果您不确定在什么情况下会遇到哪些异常,请使用调试器和一些测试代码来找出您实际上可能期望的异常:

// TODO: create one of your error conditions
try
{
   _context.SaveChanges();
}
catch (Exception e)
{
    Console.WriteLine(e.GetType()); // what is the real exception?
}

当您知道a可以期待哪些异常以及您真正可以处理哪些异常时,请编写最终代码:

try
{
   _context.SaveChanges();
}
catch (DbUpdateException e)
{
    // handle the update exception
}
catch (DbEntityValidationException e)
{
    // handle the entity validation exception
}
catch (...)

您可能不会捕获System.NotSupportedException,因此您的代码应仅使用受支持的LINQ语句。

优化

请记住,DbSets中的DbContext代表数据库中的表。 DbSets中的类表示表中的一行:非虚拟属性表示表中的列,表之间的关系表示为虚拟属性。

之所以设计这些数据库表,是因为您想解决问题。显然,在您的解决方案中,确保FirstName / LastName等不为空非常重要。

您可能会将DbContext的用法包装到一个类中,该类隐藏了您使用实体框架保留数据的方法,而不是例如Dapper或任何较低级别的方法来查询和更新数据的方法。

这个包装器类经常被称为Repository类:您的Repository的用户不知道,也不是真的不在乎,如何以及在哪里保存数据:SQL?蒙哥甚至是CSV文件?

拥有Repository类的好处是,如果您决定更改表布局,或者是否决定将查询之一更改为存储过程,或者是否决定存储数据在CSV中,更改很小,用户甚至不会注意到更改

在您的存储库中,您将具有查询人员,添加/删除/更新人员等功能。您早先决定解决方案不应接受具有空名称的人员。

您的解决方案不取决于数据的保存方式。因此,您的解决方案不应取决于存储库是否检查您的名称是否为null或nt。

请考虑在调用SaveChanges之前检查数据有效性。在这种情况下:检查名字,姓氏等是否确实不为空。您的代码将

  • 外观更简洁:您的范围之外的各方抛出的异常处理更少
  • 易于阅读:如果数据为空,读者将不必猜测会发生什么情况
  • 更易于测试:您的测试代码可以使用简单的模拟存储库而无需进行空检查
  • 更易于维护:如果您决定允许添加不带名字的人,则数据库模型不会更改,只会更改您的存储库类

答案 3 :(得分:1)

通过使用以下代码,您可以为DbEntityValidationException创建更用户友好的消息:

try
{
   _context.SaveChanges();
}
catch (DbEntityValidationException dbEx)
{
    var sb = new StringBuilder();
    foreach (var validationErrors in dbEx.EntityValidationErrors)
    {
        foreach (var validationError in validationErrors.ValidationErrors)
        {
           sb.AppendLine(string.Format("Property: {0} Error: {1}",
           validationError.PropertyName,validationError.ErrorMessage));
        }
     }
     throw new Exception(sb.ToString(), dbEx);
}

然后,您可以在更高级别上捕获此新Exception;要捕获其他异常,可以使用单独的catch块。