【问题标题】:Entity Framework : how to catch any error实体框架:如何捕获任何错误
【发布时间】:2019-07-11 12:22:00
【问题描述】:

我正在尝试将数据插入到具有大量 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
{
}

【问题讨论】:

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


    【解决方案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 块。

    【讨论】:

      【解决方案2】:

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

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

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

      当您知道可以预期哪些异常以及您真正可以处理哪些异常后,编写您的最终代码:

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

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

      优化

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

      您设计这些数据库表是因为您想解决一个问题。显然,在您的解决方案中,FirstName / LastName 等不能为空是很重要的。

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

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

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

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

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

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

      • 看起来更干净:对超出范围的各方抛出的异常处理更少
      • 更易于阅读:读者不必猜测如果数据为空会发生什么,
      • 更容易测试:您的测试代码可以使用简单的模拟存储库,无需空检查
      • 更好的可维护性:如果您决定允许添加没有名字的人员,您的数据库模型不会改变,只会改变您的存储库类

      【讨论】:

        【解决方案3】:

        您可以在 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 
        }
        

        【讨论】:

        • 我想一次性捕获所有异常而不列出任何异常,有吗?谢谢
        • 意味着您想在单个 catch 块中获取任何类型的异常并且没有 if else?那么父 Excpetion 类会为你做所有这些事情
        【解决方案4】:
        try {
           var source = new Customer();
           source.FirstName = "Joe";
        
           _context.Customer.Add(source);
           _context.SaveChanges();
        }
        catch (Exception ex) {
           // ...
        }
        

        【讨论】:

          猜你喜欢
          • 2019-07-11
          • 2017-05-27
          • 2018-10-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-09-05
          • 2015-06-21
          相关资源
          最近更新 更多