[英]ASP.NET MVC 5 EF Cannot add or update a child row: a foreign key constraint fails
我有一個線程,其中包含一個startpost作為線程內容的一部分。 它還包括回復列表,這些列表也是帖子。
class Post {
[Key]
public int Id{get;set;}
public DateTime CreationDate{get;set;}
public virtual string Content{get;set;}
public int ThreadId{get;set;}
public virtual Thread Thread{get;set;}
}
class Thread {
[Key]
public int Id{get;set;}
public string Title{get;set;}
public int FirstPostId{get;set;}
public virtual Post FirstPost{get;set;}
public List<Post> Replys{get;set;}
}
創建線程后,我只需將它們添加到DbContext並保存。 這沒有問題。 但是,如果提交了答復,則將其添加到帖子列表中,並將線程的實體標記為已修改,如下所示
var db = new MyContext();
var thread = db.Threads.Where(thread => thread.Id = threadId).FirstOrDefault();
thread.Replys.Add(newPost);
db.Entry<Thread>(thread).State = System.Data.Entity.EntityState.Modified;
db.SaveChanges();
這是我在Thread.FirstPost上違反外鍵的問題:
無法添加或更新子行:外鍵約束失敗(“ MyTable”。“ posts”,CONSTRAINT“ Thread_FirstPost”外鍵(“ Id”)參考“線程”(“ Id”)刪除無操作更新無操作)
我發現了許多有關此的信息。 簡而言之,這與EF檢查完整性的默認行為有關。 因此,當必須刪除線程時,它取決於還必須刪除的FirstPost,但這取決於似乎使EF混淆的線程。
互聯網針對此問題提供了兩種解決方案:使用fluent-API使用.WillCascadeOnDelete(false);
禁用實體的級聯.WillCascadeOnDelete(false);
或通過刪除約定將其完全禁用。 我嘗試了兩種方式:
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
//base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
modelBuilder.Entity<Thread>()
.HasOptional(t => t.FirstPost)
.WithRequired()
.WillCascadeOnDelete(false);
modelBuilder.Entity<Post>()
.HasOptional(p => p.Thread)
.WithMany()
.HasForeignKey(p => p.ThreadId)
.WillCascadeOnDelete(false);
}
但是沒有任何效果,我得到了比以前相同的例外。 我不知道為什么,似乎所有其他有此問題的人都可以使用其中一種方法來解決它,但就我而言,這兩種方法都無效。
Visual Studio Server-Explorer中的表定義
CREATE TABLE `posts` (
`Id` int(11) NOT NULL,
`CreationDate` datetime NOT NULL,
`Content` longtext NOT NULL,
`ThreadId` int(11) NOT NULL,
PRIMARY KEY (`Id`),
KEY `ThreadId` (`ThreadId`),
CONSTRAINT `Thread_Post` FOREIGN KEY (`Id`) REFERENCES `threads` (`Id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `Thread_Replys` FOREIGN KEY (`ThreadId`) REFERENCES `threads` (`Id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=latin1
CREATE TABLE `threads` (
`Id` int(11) NOT NULL AUTO_INCREMENT,
`Title` longtext NOT NULL,
`PostId` int(11) NOT NULL,
`ViewsCount` int(11) NOT NULL,
`IsClosed` tinyint(1) NOT NULL,
`IsVisible` tinyint(1) NOT NULL,
`ReplysCount` int(11) NOT NULL,
PRIMARY KEY (`Id`),
UNIQUE KEY `Id` (`Id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
EF生成的表定義(來自數據庫服務器)
CREATE TABLE `Posts`(
`Id` int NOT NULL,
`CreationDate` datetime NOT NULL,
`Content` longtext NOT NULL,
`ThreadId` int NOT NULL
)
ALTER TABLE `Posts` ADD PRIMARY KEY (Id)
ALTER TABLE `Posts` ADD CONSTRAINT Thread_Post
ALTER TABLE `Posts` ADD KEY (`ThreadId`)
ALTER TABLE `Posts` ADD CONSTRAINT Thread_Replys
ALTER TABLE `Posts` ADD CONSTRAINT Thread_Post
FOREIGN KEY (Id)
REFERENCES `Threads` (Id)
ALTER TABLE `Posts` ADD CONSTRAINT Thread_Replys
FOREIGN KEY (ThreadId)
REFERENCES `Threads` (Id)
ALTER TABLE `Posts` ADD CONSTRAINT Thread_Replys
FOREIGN KEY (ThreadId)
REFERENCES `Threads` (Id)
ON DELETE NO ACTION ON UPDATE NO ACTION
CREATE TABLE `Threads`(
`Id` int NOT NULL AUTO_INCREMENT UNIQUE,
`Title` longtext NOT NULL,
`PostId` int NOT NULL,
`ViewsCount` int NOT NULL,
`IsClosed` bool NOT NULL,
`IsVisible` bool NOT NULL,
`ReplysCount` int NOT NULL
)
ALTER TABLE `Threads` ADD PRIMARY KEY (Id)
這是我在研究此主題時發現的一些頁面: http : //weblogs.asp.net/manavi/associations-in-ef-code-first-ctp5-part-3-one-to-one-foreign-key -協會
http://www.codeproject.com/Articles/368164/EF-Data-Annotations-and-Code-Fluent
http://patrickdesjardins.com/blog/entity-framework-4-3-delete-cascade-with-code-first-poco
http://www.davepaquette.com/archive/2012/09/15/whered-my-data-go-andor-how-do-i-get-rid-of-it.aspx
http://czetsuya-tech.blogspot.de/2012/01/specify-on-delete-no-action-or-on.html#.Viy-0X54u9J
引入FOREIGN KEY約束可能會導致循環或多個級聯路徑-為什么?
您的外鍵設置不正確,最好不要顯式定義外鍵,以防您使用原始類名,我將在代碼中進行解釋:
class Post {
[Key]
public int Id{get;set;}
public DateTime CreationDate{get;set;}
public virtual string Content{get;set;}
public int ThreadId{get;set;} **-> here you used ThreadId which is implicitly a foreignkey for Thread, and that's good**
public virtual Thread Thread{get;set;}
}
class Thread {
[Key]
public int Id{get;set;}
public string Title{get;set;}
public int FirstPostId{get;set;} **-> here you can do the same by changing this to PostId**
public virtual Post FirstPost{get;set;} **-> and this to Post**
public List<Post> Replys{get;set;}
}
或者,您可以使用數據注釋的更好的選擇:
替換為:
public virtual Post FirstPost{get;set;}
有了這個:
[ForeignKey("FirstPostId")]
public virtual Post FirstPost{get;set;}
這告訴EF FirstPostId
是`FirstPost的外鍵。
讓我知道是否可行。
更新
我已經手動更改了您的sql代碼,現在可以使用:
CREATE TABLE threads (
Id int NOT NULL,
Title nvarchar(max) NOT NULL,
PostId int,
ViewsCount int NOT NULL,
IsClosed tinyint NOT NULL,
IsVisible tinyint NOT NULL,
ReplysCount int NOT NULL,
PRIMARY KEY (Id),
)
CREATE TABLE posts (
Id int NOT NULL,
CreationDate datetime NOT NULL,
Content nvarchar(max) NOT NULL,
ThreadId int NOT NULL,
PRIMARY KEY (Id),
CONSTRAINT Thread_Post FOREIGN KEY (Id) REFERENCES threads (Id) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT Thread_Replys FOREIGN KEY (ThreadId) REFERENCES threads (Id) ON DELETE NO ACTION ON UPDATE NO ACTION
)
ALTER TABLE threads ADD CONSTRAINT Post_Thread FOREIGN KEY (PostId) REFERENCES posts (Id) ON DELETE NO ACTION ON UPDATE NO ACTION
您不能將線程定義為需要postID,也不能同時定義為post來要求threadID,如果那樣的話,您將無法創建兩者(除非您同時創建兩者-意味着在同一個db.savechanges( ))。
想一想。 您想使用尚不存在的帖子來定義線程。
根本問題是您的模型在Post
和Thread
之間包含1:1關聯,其中Thread
是主要實體或獨立實體。 這由...部分表示
modelBuilder.Entity<Thread>()
.HasOptional(t => t.FirstPost)
.WithRequired()
您會看到它反映在DDL語句中...
ALTER TABLE `Posts` ADD CONSTRAINT Thread_Post
FOREIGN KEY (Id)
REFERENCES `Threads` (Id)
因此, Post's
主鍵也是 Thread
的外鍵。 這意味着每個Thread
最多只能插入一個Post
! (因為每個后續的Post
必須具有一個新的PK值,但這並不引用現有的Thread
因此您會遇到約束違規的情況)。
您可以(使)以Post
為主體來解決此問題。 在這種情況下, Thread
將具有引用其第一個Post
的PK / FK組合。 但是,對我而言,1:1關聯表示實體與它們幾乎是一個實體的點密切相關( Student
- StudentDetails
)。 因此,我認為1:1關聯並不適合。
我建議這種映射:
modelBuilder.Entity<Thread>()
.HasOptional(t => t.FirstPost)
.WithMany()
.HasForeignKey(t => t.FirstPostId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Post>()
.HasRequired(p => p.Thread)
.WithMany(t => t.Replies)
.HasForeignKey(p => p.ThreadId)
.WillCascadeOnDelete(false);
從理論上講,這將Thread
和FirstPost
之間的關系變成一對多,但實際上這意味着Thread
現在具有其第一個帖子的外鍵,並且這些復雜的PK / FK組合也消失了。 請注意, FirstPostId
應為可為null的int來支持此功能。
在另一方面,如果在您看來,一個Thread
和它的第一篇文章是密切相關的,你可以考慮合並雙方進入一個線程,也有它的第一篇文章(屬性CreationDate
, Content
)。 最后,您將獲得一個非常簡單的線程模型(帖子?),其中包含答復,但仍然沒有多余的內容。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.