ASP.无法添加或更新子行:外键约束失败

本文关键字:约束 失败 添加 更新 ASP | 更新日期: 2023-09-27 18:19:05

我有一个线程,其中包含一个起始帖子作为线程内容的一部分。它还包括一个回复列表,这也是帖子。

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中并保存即可。这工作没有问题。但是,如果提交了回复,我将它们添加到post-list中,并将线程的实体标记为修改,如下所示

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

这是一个问题,我得到一个违反外键的线程。FirstPost:

不能添加或更新子行:外键约束失败("MyTable")。"posts",约束"Thread_FirstPost"外键("Id")引用"threads" ("Id") ON DELETE NO ACTION ON UPDATE NO ACTION

我找到了很多关于这个的信息。总之,所有人都说,这与EF检查完整性的默认行为有关。因此,当一个线程必须被删除时,它取决于也必须被删除的FirstPost,但这取决于线程,这似乎使EF感到困惑。

对于这个问题,互联网有两种解决方案:使用fluent-API来禁用使用.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);
}

但是什么都不工作,我得到了和以前一样的异常。我不知道为什么,似乎所有有这个问题的人都可以用其中一种方法来解决它,但在我的情况下,两种方法都没有效果……

Table-Definitions from 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生成的Table-Definition

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-associations http://www.codeproject.com/Articles/368164/EF-Data-Annotations-and-Code-Fluent

http://geekswithblogs.net/danemorgridge/archive/2010/12/17/ef4-cpt5-code-first-remove-cascading-deletes.aspx

http://patrickdesjardins.com/blog/entity-framework-4-3-delete-cascade-with-code-first-poco

ASP。NET MVC 4多外键引用单父实体

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约束可能导致循环或多个级联路径-为什么?

实体框架:如何解决"FOREIGN KEY约束可能导致循环或多个级联路径"?

在ASP中指定ON DELETE NO ACTION。. NET MVC 4 c# Code First

ASP.无法添加或更新子行:外键约束失败

您的外键设置不正确,如果您使用原始类名,则可以不显式定义外键,我将在您的代码中解释:

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来定义一个线程

根本问题是您的模型包含PostThread之间的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);

这在理论上将ThreadFirstPost之间的关系变成了一对多,但实际上这意味着Thread现在有了第一个帖子的外键,这些复杂的PK/FK组合就消失了。请注意,FirstPostId应该是一个可空的int来支持这一点。

另一方面,如果在你看来Thread和它的第一个帖子是密切相关的,你可以考虑合并到一个线程,也有它的第一个帖子(CreationDate, Content)的属性。您将得到一个非常简单的线程(帖子?)模型,其中仍然没有多余的回复。