我一直在努力解决这个问题,但无法在Stack Overflow上找到解决方案。
我有一个MVC应用程序,它使用Entity Framework with Repository和Unit of Work模式。我最近从使用网络数据库(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属性时,我都会遇到以下异常:
&#34;已成功提交对数据库的更改,但更新对象上下文时发生错误。 ObjectContext可能处于不一致状态。内部异常消息:发生参照完整性约束违规: &#39; StudentStatus.Id&#39;的属性值。在一段关系的一端与'Student.StudentStatusId&#39;的属性值不匹配。在另一端。&#34;
我已尝试在保存更改之前将导航属性重新设置为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?这会导致潜在的问题吗?另外,为什么会有两个更新语句而不是一个?
答案 0 :(得分:1)
对我而言,这种方法似乎更直观:
int StudentStatusType
保留StudentStatusType.Id
的值,因此应将其标记为[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; }
}
答案 1 :(得分:0)
EF可能会因StudentStatus
上的导航属性与Student
完全无关而感到困惑。将以下行添加到StudentStatus
类应该可以解决问题。
public virtual ICollection<Student> Students { get; set; }
答案 2 :(得分:0)
在Adimeus的建议之后,我不得不调查初始数据是如何播种的。
StudentStatus而不是StudentType是在我的服务层(而不是EF Migrations的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 Migrations 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了。不太明白为什么服务中的播种首先引起了这个问题。如果有人能解释,我会接受答案;否则我会在几天后接受我的回答。