簡體   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