实体框架迁移

编程入门 行业动态 更新时间:2024-10-17 11:21:39
实体框架迁移 - 违反参照完整性约束(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; } }

每次我尝试更新StudentStateStatus属性时,我都会得到以下异常:

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

我已经尝试在保存更改之前重新将导航属性设置为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 Migrations在数据库中创建了更多的索引,但是之后我手动删除了它们(不是我认为它们肯定会成为问题)。 我试图在两个数据库中比较关系,并且一切看起来都是一样的。

我知道如果没有我提供更多信息,可能很难指出什么可能是错误的,但是还有什么可能导致这个问题呢?

编辑 (添加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

我忘记提及我在存储库级别启用数据库日志记录以查看Entity Framework正在执行的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

那么为什么EF试图插入25的值,即使我明确指定它是26? 这是造成潜在的问题吗? 另外,为什么会有两个更新语句而不是一个?

I've been struggling with this for a while and can't find a solution on Stack Overflow.

I've got an MVC App which uses Entity Framework with Repository and Unit of Work patterns. 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.

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:

"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."

I've tried re-setting the navigation property to null before saving the changes.

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

I've tried retrieving a specific StudentStatus object:

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().

I can update StudentType (which is literally the same kind of relationship and similar class) without any problem.

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). 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)

[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

I forgot to mention that I enabled database logging on repository level to see what SQL queries are being executed by Entity Framework:

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? Is this causing the underlying issue? Also, why would there be two update statements and not one?

最满意答案

对我而言,这种方法似乎更直观:

int StudentStatusType保存int StudentStatusType的值,所以它应该被标记为[ForeignKey] 。 如果添加virtual StudentStatusType属性,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; } }

After Adimeus's suggestion I had to investigate how the initial data was seeded.

StudentStatus as opposed to StudentType was seeded in my service layer (instead of Configuration.cs file of EF Migrations) - don't ask me why; 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):

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. 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.

更多推荐

本文发布于:2023-08-06 22:32:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1456713.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:实体   框架

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!