c# - ef6 和级联删除

标签 c# entity-framework

我在 EntityFramework 6 中发现了一个小问题,我不确定是不是我做错了什么。

默认情况下,我认为应该按照此处所述启用它: http://www.entityframeworktutorial.net/code-first/cascade-delete-in-code-first.aspx

但我在我的应用程序中发现情况并非如此。 我有这个模型:

public class Category
{
    public string Id { get; set; }
    [Required] [MaxLength(100)] [Index(IsUnique = true)] public string Name { get; set; }
    [MaxLength(2083)] public string Image { get; set; }
    public bool Live { get; set; }

    public IList<Criteria> Criteria { get; set; }
    public IList<Feed> Feeds { get; set; }
    public IList<Sortation> Sortations { get; set; }
    public IList<Quote> Quotes { get; set; }
    public IList<Question> Questions { get; set; }
}

它没有针对问题的级联删除,因此我将 DbContext 更新为:

modelBuilder.Entity<Category>().HasMany(m => m.Questions).WithOptional().HasForeignKey(m => m.CategoryId).WillCascadeOnDelete(true);

当我运行 update-database 时,我看到约束现在是正确的:

ALTER TABLE [dbo].[Questions]  WITH CHECK ADD  CONSTRAINT [FK_dbo.Questions_dbo.Categories_CategoryId] FOREIGN KEY([CategoryId])
REFERENCES [dbo].[Categories] ([Id])
ON DELETE CASCADE
GO

我试图删除一个类别,但出现错误:

The DELETE statement conflicted with the REFERENCE constraint

经过调查,它提示 Answers 表。这是问题答案模型:

public class Question : Key
{
    public string CategoryId { get; set; }
    [Required, MaxLength(255)] public string Text { get; set; }
    [MaxLength(255)] public string AltText { get; set; }
    public int Order { get; set; }
    public int Priority { get; set; }
    public QuestionType Type { get; set; }

    public IList<Answer> Answers { get; set; }
}

public class Answer: Key
{
    public int QuestionId { get; set; }
    public int? CriteriaId { get; set; }
    [Required] [MaxLength(255)] public string Text { get; set; }
    public int Order { get; set; }
    public int Priority { get; set; }
    [MaxLength(2083)] public string Image { get; set; }

    public Criteria Criteria { get; set; }
    public Question Question { get; set; }
    public Scenario Scenario { get; set; }
    public IList<AnswerFormula> Formulas { get; set; }
    public IList<Image> Images { get; set; }
}

映射看起来像这样:

modelBuilder.Entity<Question>().HasMany(m => m.Answers).WithRequired(m => m.Question).HasForeignKey(m => m.QuestionId)

但是如果我检查约束,我会看到:

ALTER TABLE [dbo].[Answers]  WITH CHECK ADD  CONSTRAINT [FK_dbo.Answers_dbo.Questions_QuestionId] FOREIGN KEY([QuestionId])
REFERENCES [dbo].[Questions] ([Id])
GO

我认为是错误的,我认为应该是:

ALTER TABLE [dbo].[Answers]  WITH CHECK ADD  CONSTRAINT [FK_dbo.Answers_dbo.Questions_QuestionId] FOREIGN KEY([QuestionId])
REFERENCES [dbo].[Questions] ([Id])
ON DELETE CASCADE
GO

所以我将我的映射更改为:

modelBuilder.Entity<Question>().HasMany(m => m.Answers).WithRequired(m => m.Question).HasForeignKey(m => m.QuestionId).WillCascadeOnDelete(true);

然后运行“add-migration AnswerCascadeDelete”,它告诉我没有任何变化....

有人知道为什么吗?


作为更新,这是我的 DbContext:

public class DatabaseContext : IdentityDbContext<User>
{
    public DatabaseContext()
        : base("DefaultConnection")
    {
        Database.CommandTimeout = 900;
        Database.Log = s => Debug.WriteLine(s);
        Configuration.LazyLoadingEnabled = false;
    }

    public DbSet<Feed> Feeds { get; set; }

    public DbSet<Organisation> Organisations { get; set; }
    public DbSet<Category> Categories { get; set; }
    public DbSet<Criteria> Criteria { get; set; }
    public DbSet<Attribute> Attributes { get; set; }
    public DbSet<AttributeFormula> CriteriaForumlas { get; set; }

    public DbSet<AttributeType> AttributeTypes { get; set; }
    public DbSet<AttributeOperation> AttributeOperations { get; set; }
    public DbSet<Scenario> Scenarios { get; set; }
    public DbSet<Question> Questions { get; set; }
    public DbSet<Answer> Answers { get; set; }
    public DbSet<AnswerFormula> AnswerForumlas { get; set; }

    public DbSet<Quote> Quotes { get; set; }

    public DbSet<Claim> Claims { get; set; }
    public DbSet<Client> Clients { get; set; }
    public DbSet<RefreshToken> RefreshTokens { get; set; }

    public DbSet<Search> Searches { get; set; }
    public DbSet<Charge> Charges { get; set; }
    public DbSet<Exclusion> Exclusions { get; set; }
    public DbSet<Sortation> Sortations { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        // Table renames
        modelBuilder.Entity<Criteria>().ToTable("Criteria");
        modelBuilder.Entity<IdentityRole>().ToTable("Roles");
        modelBuilder.Entity<IdentityUserRole>().ToTable("UserRoles");
        modelBuilder.Entity<IdentityUserClaim>().ToTable("UserClaims");
        modelBuilder.Entity<IdentityUserLogin>().ToTable("UserLogins");
        modelBuilder.Entity<ImageText>().ToTable("ImageText");

        // One to One
        modelBuilder.Entity<Attribute>().HasOptional(m => m.Type).WithRequired(m => m.Attribute).WillCascadeOnDelete(false);
        modelBuilder.Entity<Attribute>().HasOptional(m => m.Operation).WithRequired(m => m.Attribute).WillCascadeOnDelete(false);
        modelBuilder.Entity<Answer>().HasOptional(m => m.Scenario).WithRequired(m => m.Answer).WillCascadeOnDelete(false);
        modelBuilder.Entity<Answer>().HasOptional(m => m.Criteria).WithMany().HasForeignKey(m => m.CriteriaId).WillCascadeOnDelete(false);

        // One to Many  
        modelBuilder.Entity<Criteria>().HasMany(m => m.Attributes).WithRequired().HasForeignKey(m => m.CriteriaId).WillCascadeOnDelete(true);
        modelBuilder.Entity<IdentityRole>().HasMany(m => m.Users).WithRequired().HasForeignKey(m => m.RoleId).WillCascadeOnDelete(false);
        modelBuilder.Entity<Organisation>().HasMany(m => m.Feeds).WithRequired().HasForeignKey(m => m.OrganisationId).WillCascadeOnDelete(true);
        modelBuilder.Entity<Organisation>().HasMany(m => m.Users).WithRequired().HasForeignKey(m => m.OrganisationId).WillCascadeOnDelete(true);
        modelBuilder.Entity<Question>().HasMany(m => m.Answers).WithRequired(m => m.Question).HasForeignKey(m => m.QuestionId).WillCascadeOnDelete(true);

        modelBuilder.Entity<Category>().HasMany(m => m.Sortations).WithRequired().HasForeignKey(m => m.CategoryId).WillCascadeOnDelete(true);
        modelBuilder.Entity<Category>().HasMany(m => m.Criteria).WithRequired().HasForeignKey(m => m.CategoryId).WillCascadeOnDelete(true);
        modelBuilder.Entity<Category>().HasMany(m => m.Feeds).WithRequired().HasForeignKey(m => m.CategoryId).WillCascadeOnDelete(true);
        modelBuilder.Entity<Category>().HasMany(m => m.Questions).WithOptional().HasForeignKey(m => m.CategoryId).WillCascadeOnDelete(true);
        modelBuilder.Entity<Category>().HasMany(m => m.Quotes).WithRequired().HasForeignKey(m => m.CategoryId).WillCascadeOnDelete(true);

        modelBuilder.Entity<User>().HasMany(m => m.Searches).WithRequired().HasForeignKey(m => m.UserId).WillCascadeOnDelete(true);
        modelBuilder.Entity<User>().HasMany(m => m.Charges).WithRequired().HasForeignKey(m => m.UserId).WillCascadeOnDelete(true);
        modelBuilder.Entity<Answer>().HasMany(m => m.Images).WithRequired().HasForeignKey(m => m.AnswerId).WillCascadeOnDelete(true);
        modelBuilder.Entity<Image>().HasMany(m => m.ImageText).WithRequired().HasForeignKey(m => m.ImageId).WillCascadeOnDelete(true);

        modelBuilder.Entity<Attribute>().HasMany(m => m.Formulas).WithRequired().HasForeignKey(m => m.AttributeId).WillCascadeOnDelete(true);
        modelBuilder.Entity<Answer>().HasMany(m => m.Formulas).WithRequired().HasForeignKey(m => m.AnswerId).WillCascadeOnDelete(true);

        // Create our primary keys
        modelBuilder.Entity<IdentityUserLogin>().HasKey(m => m.UserId);
        modelBuilder.Entity<IdentityRole>().HasKey(m => m.Id);
        modelBuilder.Entity<IdentityUserRole>().HasKey(m => new {m.RoleId, m.UserId});
        modelBuilder.Entity<AttributeOperation>().HasKey(m => m.AttributeId);
        modelBuilder.Entity<AttributeType>().HasKey(m => m.AttributeId);
        modelBuilder.Entity<Scenario>().HasKey(m => m.AnswerId);
    }
}

如您所见,我已对所有关系显式设置 WillCascadeOnDelete。 我认为大多数都会默认设置,在这种情况下它实际上不会在迁移中生成任何代码。 但是当我检查我的任何表时,只有一些表启用了级联删除,我不明白为什么......

最佳答案

据我所知,EF 通过以下配置设置处理关系和级联删除:

不要在以下情况下进行级联删除:

  • .WillCascadeOnDelete(false) 明确说明
  • 具有可选关系,即:.WithOptional()

级联删除如果:

  • .WillCascadeOnDelete(true) 明确说明
  • 具有必需的关系,即:.WithRequired(/*subject*/)

注意:这些也可以由数据注释触发,例如; [RequiredAttribute] ,或者,可选变体;使用 Nullable<Type>

现在,在你的约束中有:

ALTER TABLE [dbo].[Answers] WITH CHECK ADD CONSTRAINT [FK_dbo.Answers_dbo.Questions_QuestionId] FOREIGN KEY([QuestionId]) REFERENCES [dbo].[Questions] ([Id]) GO

但这指的是答案,而不是问题。所以,我认为约束是有效的:

问题可以没有答案。

如果你检查 questions 上的约束,我会收集您会发现级联删除已启用。

关于c# - ef6 和级联删除,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54072699/

相关文章:

c# - 如何使用 C# 检查注册表值是否存在?

c# - WPF 路径以某种大小消失

c# - 从特定位置读取 C# 中的二进制文件

c# - 如何在 c# .net web 应用程序中获取特定的资源字符串而不考虑当前的文化

c# - 在导航属性 Entity Framework 上正确使用接口(interface)

c# - 如何从接受参数的 Controller 返回不同的 ActionResult?

c# - 编辑实体时出现 DbUpdateConcurrencyException

entity-framework - 在 Azure 上使用 Entity Framework Code First 迁移更改 PK 数据类型时出现问题

c# - EF 代码首先是 : one-to-many twice to same collection type

entity-framework - 我是否需要使用 Fluent API 配置与 Entity Framework 的关系的双方?