简体   繁体   English

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

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

I've been struggling with this for a while and can't find a solution on Stack Overflow. 我已经为此苦苦挣扎了一段时间,在Stack Overflow上找不到解决方案。

I've got an MVC App which uses Entity Framework with Repository and Unit of Work patterns. 我有一个MVC应用程序,它使用带有存储库和工作单元模式的实体框架。 I have recently moved from using a network database (Sql Server 2008 R2, where the issue didn't seem to exist) to local database and started using Entity Framework migrations in order to work with another developer and not affect each other by making changes to our models. 我最近从使用网络数据库(Sql Server 2008 R2,似乎不存在该问题)转移到本地数据库,并开始使用Entity Framework迁移,以便与另一位开发人员合作,并且通过对我们的模型。

My model looks like this: 我的模型如下所示:

[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; }
}

Every time I try to update StudentStatus property of Student I get the following exception: 每次尝试更新Student的StudentStatus属性时,都会收到以下异常:

"The changes to the database were committed successfully, but an error occurred while updating the object context. The ObjectContext might be in an inconsistent state. Inner exception message: A referential integrity constraint violation occurred: The property value(s) of 'StudentStatus.Id' on one end of a relationship do not match the property value(s) of 'Student.StudentStatusId' on the other end." “对数据库的更改已成功提交,但是在更新对象上下文时发生了错误。ObjectContext可能处于不一致状态。内部异常消息:发生了引用完整性约束冲突:'StudentStatus的属性值。关系的一端的“ Id”与另一端的“ Student.StudentStatusId”的属性值不匹配。”

I've tried re-setting the navigation property to null before saving the changes. 我尝试在保存更改之前将Navigation属性重新设置为null。

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

I've tried retrieving a specific StudentStatus object: 我尝试检索特定的StudentStatus对象:

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

but every try it throws the same exception on DataContext.SaveChanges(). 但是每次尝试都会在DataContext.SaveChanges()上引发相同的异常。

I can update StudentType (which is literally the same kind of relationship and similar class) without any problem. 我可以毫无问题地更新StudentType(实际上是相同类型的关系和相似的类)。

Implementation of Update method: 更新方法的实现:

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);
    }
}

I have noticed that EF Migrations created more indexes in the database, compared to my previous network database, but I have since deleted them manually (not that I thought they would necessarily be the problem). 与以前的网络数据库相比,我注意到EF迁移在数据库中创建了更多索引,但是此后我手动删除了它们(不是我认为它们一定是问题所在)。 I tried comparing relationships in the two databases and everything seems to be the same. 我尝试比较两个数据库中的关系,并且一切似乎都相同。

I know it might be difficult to point out what might be wrong without me providing more information, but what else could possibly be causing this issue? 我知道在没有我提供更多信息的情况下可能很难指出出什么问题,但是还有什么可能导致此问题?

EDIT (added StudentStatus class and corresponding StudentStatusType) 编辑 (添加了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; }
}

EDIT 2 编辑2

I forgot to mention that I enabled database logging on repository level to see what SQL queries are being executed by Entity Framework: 我忘了提到我启用了存储库级别的数据库日志记录来查看实体框架正在执行哪些SQL查询:

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

The result is: 结果是:

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

So why is EF trying to insert value of 25, even though I explicitly specify it to be 26? 那么,即使我明确将其指定为26,EF为什么也尝试插入25的值? Is this causing the underlying issue? 这是否引起潜在的问题? Also, why would there be two update statements and not one? 另外,为什么会有两个更新语句而不是一个?

To me, this approach seems far more intuitive: 在我看来,这种方法似乎更加直观:

int StudentStatusType holds the value of StudentStatusType.Id , so it should be marked [ForeignKey] . int StudentStatusType持有的值StudentStatusType.Id ,所以它应该被标记[ForeignKey] If you add a virtual StudentStatusType -property, EntityFramework will bind it automatically. 如果添加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 might be getting confused by the navigational property on StudentStatus not being related to Student at all. EF可能由于StudentStatus上的导航属性完全与Student无关而感到困惑。 Adding in the following line to the StudentStatus class should do the trick. 将以下行添加到StudentStatus类应该可以解决问题。

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

After Adimeus's suggestion I had to investigate how the initial data was seeded. 在Adimeus的建议之后,我不得不调查初始数据的播种方式。

StudentStatus as opposed to StudentType was seeded in my service layer (instead of Configuration.cs file of EF Migrations) - don't ask me why; 与StudentType相对的StudentStatus植入了我的服务层(而不是EF迁移的Configuration.cs文件)-别问我为什么; it was another developer dealing with migrations. 这是另一个处理迁移的开发人员。

Was: 是:

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();
}

I have moved the seeding to EF Migrations Configuration.cs file (I suppose it can probably be optimised, but it was a quick test): 我已将种子移至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));

The issue is gone! 问题不见了! I can update StudentStatus now. 我现在可以更新StudentStatus。 Don't quite understand why seeding in the service caused the issue in the first place. 不太了解为什么服务中的种子首先会导致问题。 If anyone can explain, I will accept the answer; 如果有人可以解释,我将接受答案; otherwise I will accept my answer in a few days time. 否则,几天后我会接受我的回答。

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

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