繁体   English   中英

实体框架迁移-违反参照完整性约束

[英]Entity Framework Migrations - A referential integrity constraint violation

我已经为此苦苦挣扎了一段时间,在Stack Overflow上找不到解决方案。

我有一个MVC应用程序,它使用带有存储库和工作单元模式的实体框架。 我最近从使用网络数据库(Sql Server 2008 R2,似乎不存在该问题)转移到本地数据库,并开始使用Entity Framework迁移,以便与另一位开发人员合作,并且通过对我们的模型。

我的模型如下所示:

[Table("Student")]
public class Student
{
    [Key]
    public int Id { get; set; }

    <... other fields ...>

    [Required(ErrorMessage = "A student type is required")]
    public int StudentTypeId { get; set; }

    [Required(ErrorMessage = "A student status is required")]
    public int StudentStatusId { get; set; }

    [ForeignKey("StudentTypeId")]
    public virtual StudentType StudentType { get; set; }

    [ForeignKey("StudentStatusId")]
    public virtual StudentStatus StudentStatus { get; set; }
}

每次尝试更新Student的StudentStatus属性时,都会收到以下异常:

“对数据库的更改已成功提交,但是在更新对象上下文时发生了错误。ObjectContext可能处于不一致状态。内部异常消息:发生了引用完整性约束冲突:'StudentStatus的属性值。关系的一端的“ Id”与另一端的“ Student.StudentStatusId”的属性值不匹配。”

我尝试在保存更改之前将Navigation属性重新设置为null。

student.StudentStatus = null;
student.StudentStatusId = 26;
_studentRepository.Update(student);
_unitOfWork.Commit();

我尝试检索特定的StudentStatus对象:

var studentStatus = _studentStatusRepository.GetById(26);
student.StudentStatusId = 26;
student.StudentStatus = studentStatus;
_studentRepository.Update(student);
_unitOfWork.Commit();

但是每次尝试都会在DataContext.SaveChanges()上引发相同的异常。

我可以毫无问题地更新StudentType(实际上是相同类型的关系和相似的类)。

更新方法的实现:

public virtual void Update(T entity)
{
    try
    {
        DataContext.Entry(entity).State = EntityState.Modified;
    }
    catch (Exception exception)
    {
        throw new EntityException(string.Format("Failed to update entity '{0}'", typeof(T).Name), exception);
    }
}

与以前的网络数据库相比,我注意到EF迁移在数据库中创建了更多索引,但是此后我手动删除了它们(不是我认为它们一定是问题所在)。 我尝试比较两个数据库中的关系,并且一切似乎都相同。

我知道在没有我提供更多信息的情况下可能很难指出出什么问题,但是还有什么可能导致此问题?

编辑 (添加了StudentStatus类和相应的StudentStatusType)

[Table("StudentStatus")]
public class StudentStatus
{
    [Key]
    public int Id { get; set; }

    [Required(ErrorMessage = "Student status name is required")]
    [MaxLength(50, ErrorMessage = "Student status name cannot be longer than 50 characters")]
    public string Name { get; set; }

    public int StudentStatusTypeId { get; set; }

    [ForeignKey("StudentStatusTypeId")]
    public virtual StudentStatusType StudentStatusType { get; set; }
}

[Table("StudentStatusType")]
public class StudentStatusType
{
    [Key]
    public int Id { get; set; }
    public string Name { get; set; }
}

编辑2

我忘了提到我启用了存储库级别的数据库日志记录来查看实体框架正在执行哪些SQL查询:

DataContext.Database.Log = s => Debug.WriteLine(s); 

结果是:

UPDATE [dbo].[Student]
SET <... some parameters ...>, [StudentStatusId] = @10, <... some parameters ...>
WHERE ([Id] = @14)

UPDATE [dbo].[Student]
SET <... some parameters ...>, [StudentStatusId] = @10, <... some parameters ...>
WHERE ([Id] = @14)

<... some parameters ...>

-- @10: '25' (Type = Int32)

-- @10: '25' (Type = Int32)

<... some parameters ...>

-- Executing at 12/01/2015 12:30:41 +00:00

-- Executing at 12/01/2015 12:30:41 +00:00

-- Completed in 0 ms with result: 1

-- Completed in 0 ms with result: 1

那么,即使我明确将其指定为26,EF为什么也尝试插入25的值? 这是否引起潜在的问题? 另外,为什么会有两个更新语句而不是一个?

在我看来,这种方法似乎更加直观:

int StudentStatusType持有的值StudentStatusType.Id ,所以它应该被标记[ForeignKey] 如果添加virtual StudentStatusType -property,则EntityFramework会自动将其绑定。

[Table("StudentStatus")]
public class StudentStatus
{
    [Key]
    public int Id { get; set; }

    [Required(ErrorMessage = "Student status name is required")]
    [MaxLength(50, ErrorMessage = "Student status name cannot be longer than 50 characters")]
    public string Name { get; set; }

    [ForeignKey("StudentStatusType")] 
    public int StudentStatusTypeId { get; set; }
    public virtual StudentStatusType StudentStatusType { get; set; }
}

[Table("StudentStatusType")]
public class StudentStatusType
{
    [Key]
    public int Id { get; set; }
    public string Name { get; set; }
}

EF可能由于StudentStatus上的导航属性完全与Student无关而感到困惑。 将以下行添加到StudentStatus类应该可以解决问题。

public virtual ICollection<Student> Students { get; set; }

在Adimeus的建议之后,我不得不调查初始数据的播种方式。

与StudentType相对的StudentStatus植入了我的服务层(而不是EF迁移的Configuration.cs文件)-别问我为什么; 这是另一个处理迁移的开发人员。

是:

if (!_studentStatusRepository.GetAll().Any())
{
    var newStudentStatus = _studentStatusTypeRepository.Get(x => x.Name == "New");
    var activeStudentStatus = _studentStatusTypeRepository.Get(x => x.Name == "Active");
    var deletedStudentStatus = _studentStatusTypeRepository.Get(x => x.Name == "Deleted");

    var studentStatuses = new List<StudentStatus>
    {
        new StudentStatus {Name = "New", StudentStatusType = newStudentStatus, StudentStatusTypeId = newStudentStatus.Id},
        new StudentStatus {Name = "Awaiting Approval", StudentStatusType = activeStudentStatus, StudentStatusTypeId = activeStudentStatus.Id},
        new StudentStatus {Name = "Approved", StudentStatusType = activeStudentStatus, StudentStatusTypeId = activeStudentStatus.Id},
        new StudentStatus {Name = "Deleted", StudentStatusType = deletedStudentStatus, StudentStatusTypeId = deletedStudentStatus.Id},
        new StudentStatus {Name = "Reinstated", StudentStatusType = deletedStudentStatus, StudentStatusTypeId = deletedStudentStatus.Id}
    };

    foreach (var studentStatus in studentStatuses.ToList())
    {
        StudentStatus status = studentStatus;
        var dbStudentStatus = _studentStatusRepository.Get(x => x.Name == status.Name);
        if (dbStudentStatus == null)
        {
            _studentStatusRepository.Add(studentStatus);
        }
    }
    _unitOfWork.Commit();
}

我已将种子移至EF迁移Configuration.cs文件(我想它可能可以进行优化,但这是一个快速测试):

var studentStatuses = new List<StudentStatus>
{
    new StudentStatus
    {
        Name = "New",
        StudentStatusType = context.StudentStatusTypes.Single(x => x.Name == "New"),
        StudentStatusTypeId = context.StudentStatusTypes.Single(x => x.Name == "New").Id,
    },
    new StudentStatus
    {
        Name = "Awaiting Approval",
        StudentStatusType = context.StudentStatusTypes.Single(x => x.Name == "Active"),
        StudentStatusTypeId = context.StudentStatusTypes.Single(x => x.Name == "Active").Id,
    },
    new StudentStatus
    {
        Name = "Approved",
        StudentStatusType = context.StudentStatusTypes.Single(x => x.Name == "Active"),
        StudentStatusTypeId = context.StudentStatusTypes.Single(x => x.Name == "Active").Id,
    },
    new StudentStatus
    {
        Name = "Deleted",
        StudentStatusType = context.StudentStatusTypes.Single(x => x.Name == "Deleted"),
        StudentStatusTypeId = context.StudentStatusTypes.Single(x => x.Name == "Deleted").Id,
    },
    new StudentStatus
    {
        Name = "Reinstated",
        StudentStatusType = context.StudentStatusTypes.Single(x => x.Name == "Deleted"),
        StudentStatusTypeId = context.StudentStatusTypes.Single(x => x.Name == "Deleted").Id,
    }
};

studentStatuses.ForEach(x => context.StudentStatuses.AddOrUpdate(y => y.Name, x));

问题不见了! 我现在可以更新StudentStatus。 不太了解为什么服务中的种子首先会导致问题。 如果有人可以解释,我将接受答案; 否则,几天后我会接受我的回答。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM