找到DBUpdateException的原因

时间:2014-03-18 20:55:48

标签: c# .net entity-framework

调用DbContext.SaveChanges时,我收到DbUpdateException:

  

未处理的类型异常   发生'System.Data.Entity.Infrastructure.DbUpdateException'   EntityFramework.dll。附加信息:发生错误   更新条目。有关详细信息,请参阅内部异常。

不幸的是,没有内部异常(至少,不是我能看到的)。有没有办法确切地看到为什么SaveChanges抛出异常?至少,看看SaveChanges在发生错误时尝试更新的表格会很有帮助。

7 个答案:

答案 0 :(得分:27)

这是我对SaveChanges的重写。它为我提供了一个放置断点的有用位置:

    public override int SaveChanges()
    {
        try
        {
            return base.SaveChanges();
        }
        catch (DbEntityValidationException e)
        {
            foreach (var eve in e.EntityValidationErrors)
            {
                Debug.WriteLine(@"Entity of type ""{0}"" in state ""{1}"" 
                   has the following validation errors:",
                    eve.Entry.Entity.GetType().Name, 
                    eve.Entry.State);
                foreach (var ve in eve.ValidationErrors)
                {
                    Debug.WriteLine(@"- Property: ""{0}"", Error: ""{1}""",
                        ve.PropertyName, ve.ErrorMessage);
                }
            }
            throw;
        }
        catch(DbUpdateException e)
        {
           //Add your code to inspect the inner exception and/or
           //e.Entries here.
           //Or just use the debugger.
           //Added this catch (after the comments below) to make it more obvious 
           //how this code might help this specific problem
        }
        catch (Exception e)
        {
            Debug.WriteLine(e.Message);
            throw;
        }
    }

参考:

Validation failed for one or more entities. See 'EntityValidationErrors' property for more details

答案 1 :(得分:16)

这是我对SaveChanges的重写,显示了处理DbUpdateException的其他代码(根据问题)。

    public override int SaveChanges()
    {
        try
        {
            return base.SaveChanges();
        }
        catch (DbEntityValidationException vex)
        {
            var exception = HandleDbEntityValidationException(vex);
            throw exception;
        }
        catch(DbUpdateException dbu)
        {
            var exception = HandleDbUpdateException(dbu);
            throw exception;
        }
    }

    private Exception HandleDbUpdateException(DbUpdateException dbu)
    {
        var builder = new StringBuilder("A DbUpdateException was caught while saving changes. ");

        try
        {
            foreach (var result in dbu.Entries)
            {
                builder.AppendFormat("Type: {0} was part of the problem. ", result.Entity.GetType().Name);
            }
        }
        catch (Exception e)
        {
            builder.Append("Error parsing DbUpdateException: " + e.ToString());
        }

        string message = builder.ToString();
        return new Exception(message, dbu);
    }

我没有使日志记录代码非常具体,但它改进了类似的标准错误消息:

The conversion of a datetime2 data type to a datetime data type resulted in an out-of-range value.

这样,至少我可以看到哪个实体有问题,并且通常足以解决问题。

答案 2 :(得分:9)

根据Colin的回答,可以像这样提供有关EF持久性故障的详细信息:

public bool SaveChangesEx()
{
    try
    {
        SaveChanges();
        return true;
    }
    catch (DbEntityValidationException exc)
    {
        // just to ease debugging
        foreach (var error in exc.EntityValidationErrors)
        {
            foreach (var errorMsg in error.ValidationErrors)
            {
                // logging service based on NLog
                Logger.Log(LogLevel.Error, $"Error trying to save EF changes - {errorMsg.ErrorMessage}");
            }
        }

        throw;
    }
    catch (DbUpdateException e)
    {
        var sb = new StringBuilder();
        sb.AppendLine($"DbUpdateException error details - {e?.InnerException?.InnerException?.Message}");

        foreach (var eve in e.Entries)
        {
            sb.AppendLine($"Entity of type {eve.Entity.GetType().Name} in state {eve.State} could not be updated");
        }

        Logger.Log(LogLevel.Error, e, sb.ToString());

        throw;
    }
}

除了验证错误,更新异常将输出一般错误和上下文信息。

注意:此代码需要C#6.0,因为它使用空传播和字符串插值。

对于.NET Core,代码略有变化,因为可能出现的异常具有不同的结构/填充不同:

    public void SaveChangesEx()
    {
        try
        {
            // this triggers defined validations such as required
            Context.Validate();
            // actual save of changes
            Context.SaveChangesInner();
        }
        catch (ValidationException exc)
        {
            Logger.LogError(exc, $"{nameof(SaveChanges)} validation exception: {exc?.Message}");
            throw;
        }
        catch (DbUpdateException exc)
        {
            Logger.LogError(exc, $"{nameof(SaveChanges)} db update error: {exc?.InnerException?.Message}");
            throw;
        }
        catch (Exception exc)
        {
            // should never reach here. If it does, handle the more specific exception
            Logger.LogError(exc, $"{nameof(SaveChanges)} generic error: {exc.Message}");
            throw;
        }
    }

如果不立即处理相同的上下文,可以增强上下文以自动拒绝失败时的更改:

public void RejectChanges()
{
    foreach (var entry in ChangeTracker.Entries().Where(e => e.Entity != null).ToList())
    {
        switch (entry.State)
        {
            case EntityState.Modified:
            case EntityState.Deleted:
                entry.State = EntityState.Modified; //Revert changes made to deleted entity.
                entry.State = EntityState.Unchanged;
                break;
            case EntityState.Added:
                entry.State = EntityState.Detached;
                break;
        }
    }
}

public bool SaveChangesInner()
{
    try
    {
        SaveChanges();
        return true;
    }
    catch (Exception)
    {
        RejectChanges();
        throw;
    }
}

答案 3 :(得分:3)

当真正的异常似乎在某处丢失时,最好的办法是打破每一个异常。无论它是在某个地方被捕获还是被吞没,无论是在你的范围之内还是之外,调试器都会中断,让你看看发生了什么。

有关详细信息,请参阅此MSDN链接:

How to: Break When an Exception is Thrown

答案 4 :(得分:2)

我遇到了同样的错误“将datetime2数据类型转换为日期时间数据类型会导致超出范围的值。\ r \ n语句已终止。”

我将Datetime值字段设置为 Datetime.Now

var person = new Person
            {
               FirstName = "Sebastian",
               LastName = "Back",
               **BirthDate = DateTime.Now,**
               IsActive = true,
            };

答案 5 :(得分:0)

我得到了同样的错误并且那样解决了;

我有一个数据类型为datetime的字段,我知道我在没有分配datetime属性的情况下进行设置。所以我将其更改为" Datetime.Now "然后我看到问题已经解决,并且已经成功保存了

答案 6 :(得分:0)

在我的情况下,我使用存储过程向DB添加实体。我的SP给出了运行时错误,因为我在C#中遇到了这个错误。我纠正了SP并且EF工作得很好。 因此,如果您正在使用SP在EF中添加,编辑,删除或更新,请检查相应的sp是否正常工作。