由于一个或多个外键属性不可为空,因此无法更改关系。 (2)

时间:2016-03-04 14:23:33

标签: c# .net entity-framework

我在带有Code First的.NET 4.5应用程序中使用Entity Framework 6.1.3,并在Oracle数据库服务器上使用手工制作的表模式。大多数事情都很好。对于新函数,SaveChanges中会抛出以下异常:

  

操作失败:无法更改关系,因为一个或多个外键属性不可为空。当对关系进行更改时,相关的外键属性将设置为空值。如果外键不支持空值,则必须定义新关系,必须为外键属性分配另一个非空值,或者必须删除不相关的对象。

堆栈追踪:

  • System.Data.Entity.Core.Objects.ObjectContext.PrepareToSaveChanges(System.Data.Entity.Core.Objects.SaveOptions)
  • System.Data.Entity.Core.Objects.ObjectContext.SaveChangesInternal(System.Data.Entity.Core.Objects.SaveOptions,bool)
  • System.Data.Entity.Internal.InternalContext.SaveChanges()
  • (我的代码)

它没有说明这个问题。它找不到我帮助我。 SQL日志是空的,所以我猜想在尝试联系数据库之前,本地EF已经检测到问题。

我的问题是:我该如何调试这个东西?哪个对象具有哪个值但不应该具有哪些外键的详细信息?是否有可用于Entity Framework的跟踪日志,其中包含有关已执行操作的内部数据?

这种情况太复杂了,无法在此展示,所以请不要问它。我想帮助自己解决这个问题,我只需要对此有所帮助。

2 个答案:

答案 0 :(得分:2)

您可以登录SQL

using (var context = new BlogContext())
{
    context.Database.Log = Console.Write;

    // Your code here...
}

您甚至可以制作这样的自定义日志格式化程序

public class OneLineFormatter : DatabaseLogFormatter
{
    public OneLineFormatter(DbContext context, Action<string> writeAction)
        : base(context, writeAction)
    {
    }

    public override void LogCommand<TResult>(
        DbCommand command, DbCommandInterceptionContext<TResult> interceptionContext)
    {
        Write(string.Format(
            "Context '{0}' is executing command '{1}'{2}",
            Context.GetType().Name,
            command.CommandText.Replace(Environment.NewLine, ""),
            Environment.NewLine));
    }

    public override void LogResult<TResult>(
        DbCommand command, DbCommandInterceptionContext<TResult> interceptionContext)
    {
    }
}

您可以使用自己的日志格式化程序截取确切的异常,前提是该异常是'DbEntityValidationException'

public class MyExcpetionCommandInterceptor : IDbCommandInterceptor
{


    public void NonQueryExecuting(
        DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
    {
        LogIfNonAsync(command, interceptionContext);
    }

    public void NonQueryExecuted(
        DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
    {
        LogIfError(command, interceptionContext);
    }

    public void ReaderExecuting(
        DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
    {
        LogIfNonAsync(command, interceptionContext);
    }

    public void ReaderExecuted(
        DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
    {
        LogIfError(command, interceptionContext);
    }

    public void ScalarExecuting(
        DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
    {
        LogIfNonAsync(command, interceptionContext);
    }

    public void ScalarExecuted(
        DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
    {
        LogIfError(command, interceptionContext);
    }

    private void LogIfNonAsync<TResult>(
        DbCommand command, DbCommandInterceptionContext<TResult> interceptionContext)
    {
        if (!interceptionContext.IsAsync)
        {
            Logger.Warn("Non-async command used: {0}", command.CommandText);
        }
    }

    private void LogIfError<TResult>(
        DbCommand command, DbCommandInterceptionContext<TResult> interceptionContext)
    {
        if (interceptionContext.Exception.GetType() == typeof(DbEntityValidationException) || typeof(DbEntityValidationException).IsAssignableFrom(interceptionContext.Exception.GetType()) )
        {
            Logger.Error("Command {0} failed with exception {1}",
                command.CommandText, interceptionContext.Exception);
        }
    }
}

Microsoft Logging and intercepting database operations

中有一篇不错的文章

答案 1 :(得分:0)

您可以覆盖DBContext的SaveChanges方法,并查看所有属性的更新/删除/修改,它将最小化您需要检查错误的列表。我只需要删除/分离的内容,但您可以根据需要修改 .where

public override int SaveChanges()
        {
            try
            {
                var debug = false;

                if (debug)
                {
                    var modifiedEntities = ChangeTracker.Entries()
                            .Where(p => p.State == EntityState.Deleted || p.State == EntityState.Detached).ToList();

                    foreach (var change in modifiedEntities)
                    {
                        var entityName = change.Entity.GetType().Name;
                        System.Diagnostics.Debug.WriteLine(string.Format("Entity {0}", entityName));

                    }
                }


                return base.SaveChanges();
            }
            catch (DbEntityValidationException e)
            {
                foreach (var eve in e.EntityValidationErrors)
                {
                    Debug.WriteLine("Error while Save Changes:");
                    Debug.WriteLine("Entity {0} has the following validation errors:", eve.Entry.Entity.GetType().Name);
                    foreach (var ve in eve.ValidationErrors)
                    {
                        Debug.WriteLine("Property:{0}, Error: {1}",
                            ve.PropertyName, ve.ErrorMessage);
                    }
                }
                throw;
            }
            catch (Exception)
            {
                throw;
            }
        }