Entity Framework 6: Code First Suppression en cascade

c# entity-framework entity-framework-6 sql

Question

Il y a donc plusieurs questions similaires ici, mais j'ai toujours du mal à déterminer ce qui me manque exactement dans mon scénario simplifié.

Disons que j'ai les tables suivantes, intelligemment nommées d'après moi-même:

'JohnsParentTable' (Id, Description) 
'JohnsChildTable' (Id, JohnsParentTableId, Description)

Avec les classes résultantes ressemblant tellement

public class JohnsParentTable
{
    public int Id { get; set; }
    public string Description { get; set; }
    public virtual ICollection<JohnsChildTable> JohnsChildTable { get; set; }

    public JohnsParentTable()
    {
        JohnsChildTable = new List<JohnsChildTable>();
    }
}

internal class JohnsParentTableConfiguration : EntityTypeConfiguration<JohnsParentTable>
{
    public JohnsParentTableConfiguration()
    {
        ToTable("dbo.JohnsParentTable");
        HasKey(x => x.Id);
        Property(x => x.Id).HasColumnName("Id").IsRequired().HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
        Property(x => x.Description).HasColumnName("Description").IsRequired().HasMaxLength(50);
    }
}

public class JohnsChildTable
{
    public int Id { get; set; }
    public string Description { get; set; }
    public int JohnsParentTableId { get; set; }
    public JohnsParentTable JohnsParentTable { get; set; }
}

internal class JohnsChildTableConfiguration : EntityTypeConfiguration<JohnsChildTable>
{
    public JohnsChildTableConfiguration()
    {
        ToTable("dbo.JohnsChildTable");
        HasKey(x => x.Id);
        Property(x => x.Id).HasColumnName("Id").IsRequired().HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
        Property(x => x.Description).HasColumnName("Description").IsRequired().HasMaxLength(50);
        HasRequired(a => a.JohnsParentTable).WithMany(c => c.JohnsChildTable).HasForeignKey(a => a.JohnsParentTableId);
    }
}

Dans la base de données, j'ai une ligne dans la table parent avec un ID de 1 avec deux lignes dans la table enfant liée à ce parent. Si je fais ça:

var parent = db.JohnsParentTable.FirstOrDefault(a => a.Id == 1)

L'objet est correctement rempli. Cependant, si j'essaie de supprimer cette ligne:

var parent = new Data.Models.JohnsParentTable() { Id = 1 };
db.JohnsParentTable.Attach(parent);
db.JohnsParentTable.Remove(parent);

db.SaveChanges();

Entity Framework tente d'exécuter les tâches suivantes:

DELETE [dbo].[JohnsParentTable]
WHERE ([Id] = @0)
-- @0: '1' (Type = Int32)
-- Executing at 1/23/2014 10:34:01 AM -06:00
-- Failed in 103 ms with error: The DELETE statement conflicted with the REFERENCE constraint "FK_JohnsChildTable_JohnsParentTable". The conflict occurred in database "mydatabase", table "dbo.JohnsChildTable", column 'JohnsParentTableId'.
The statement has been terminated.

Ma question est la suivante: que manque-t-il exactement pour qu'Entity Framework sache qu'il doit supprimer les lignes 'JohnsChildTable' avant de supprimer le parent?

Réponse acceptée

Cela dépend si vous voulez que Entity Framework supprime les enfants ou si la base de données s'en charge.

Si vous voulez que EF génère une instruction delete pour tous les enfants et les exécute avant de supprimer le parent, vous devez d'abord charger tous les enfants en mémoire.

Vous ne pouvez donc pas simplement créer une entité "factice" avec uniquement la clé renseignée et attendre que les enfants soient supprimés.

Pour que cela fonctionne, vous devez laisser la base de données gérer la suppression.

Toutefois, la suppression de la cascade doit être activée sur la clé étrangère de la table enfant. Si tel est le cas, Entity Framework crée simplement une instruction delete pour le parent et la base de données sait également supprimer les enfants.

Entity Framework crée une clé étrangère avec les suppressions en cascade activées par défaut, si la relation est requise, comme dans votre cas. (La clé étrangère ne peut pas être nulle).

Si vous avez créé la base de données vous-même, vous devez vous souvenir de l'activer.


Réponse populaire

Je pense qu'il est préférable de remplacer la méthode OnModelCreating et d'ajouter ce code.

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
   modelBuilder.Entity<JohnsChildTable>()
               .HasRequired(t=>t.JohnsParentTable)
               .WithMany(t=>t.JohnsChildTables)
               .HasForeignKey(d=>d.JohnsParentTableId)
               .WillCascadeOnDelete(true);

            base.OnModelCreating(modelBuilder);
}

J'ai mis à true WillCascadeOnDelete (true)



Related

Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi