2017-09-25 7 views
2

Je tente de créer une petite solution de démonstration pour tester les suppressions en cascade CF EF.Entity Framework Code Première cascade Supprimer un à plusieurs

Avec le code que j'ai écrit j'obtiens l'erreur suivante en essayant d'ajouter une personne avec 2 voitures.

L'objectif est d'ajouter une personne avec 2 voitures. Puis supprimez la personne et les voitures liées seront supprimées en même temps.

System.InvalidCastException: impossible à jeter objet de type 'System.Collections.Generic.List`1 [EF_Cascading_Delete_Experiment.Car]' taper »EF_Cascading_Delete_Experiment.Car.

Je suis en train de construire un exemple simple où il y a une personne avec une liste des voitures

Voici ma personne & Classes de voitures:

public class Person 
{ 
    [Key] 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public List<Car> Cars { get; set; } 
} 
public class Car 
{ 
    [Key] 
    public int id { get; set; } 
    public string CarName { get; set; } 
} 

Voici mon code simpliste tente de annonce une personne avec 2 voitures:

public static void CarTest() 
    { 
     using (Model1 db = new Model1()) 
     { 
      Person personToAdd = new Person(); 
      personToAdd.Name = "trev"; 
      personToAdd.Cars = new List<Car>(); 

      Car car1 = new Car 
      { 
       CarName = "Vectra" 
      }; 

      Car car2 = new Car 
      { 
       CarName = "Focus" 
      }; 

      personToAdd.Cars.Add(car1); 
      personToAdd.Cars.Add(car2); 

      db.Person.Add(personToAdd); 

      db.SaveChanges(); 
     } 
    } 

l'erreur se produit sur la ligne

db.Person.Add(personToAdd); 

Ceci est mon DbContext:

public class Model1 : DbContext 
{ 
    // Your context has been configured to use a 'Model1' connection string from your application's 
    // configuration file (App.config or Web.config). By default, this connection string targets the 
    // 'EF_Cascading_Delete_Experiment.Model1' database on your LocalDb instance. 
    // 
    // If you wish to target a different database and/or database provider, modify the 'Model1' 
    // connection string in the application configuration file. 
    public Model1() 
     : base("name=Model1") 
    { 
    } 

    // Add a DbSet for each entity type that you want to include in your model. For more information 
    // on configuring and using a Code First model, see http://go.microsoft.com/fwlink/?LinkId=390109. 

    public virtual DbSet<Person> Person { get; set; } 
    public virtual DbSet<Car> Car { get; set; } 

    protected override void OnModelCreating(DbModelBuilder modelBuilder) 
    { 
     modelBuilder.Entity<Person>() 
      .HasOptional(a => a.Cars) 
      .WithOptionalDependent() 
      .WillCascadeOnDelete(true); 
    } 
} 

Le code de migration généré par EF ressemble à ceci:

public partial class addedbackeverythingincludingcascadingdelete : DbMigration 
{ 
    public override void Up() 
    { 
     CreateTable(
      "dbo.Cars", 
      c => new 
       { 
        id = c.Int(nullable: false, identity: true), 
        CarName = c.String(), 
       }) 
      .PrimaryKey(t => t.id); 

     CreateTable(
      "dbo.People", 
      c => new 
       { 
        Id = c.Int(nullable: false, identity: true), 
        Name = c.String(), 
        Cars_id = c.Int(), 
       }) 
      .PrimaryKey(t => t.Id) 
      .ForeignKey("dbo.Cars", t => t.Cars_id, cascadeDelete: true) 
      .Index(t => t.Cars_id); 

    } 

    public override void Down() 
    { 
     DropForeignKey("dbo.People", "Cars_id", "dbo.Cars"); 
     DropIndex("dbo.People", new[] { "Cars_id" }); 
     DropTable("dbo.People"); 
     DropTable("dbo.Cars"); 
    } 
} 

Pour moi, il semble que le code de migration est pas correcte? Qui aurait été généré en fonction de ma personne & Classe de voiture. Mais je ne peux pas comprendre pourquoi?

Lorsque je regarde les tables dans la base de données, elles me semblent erronées.

enter image description here

Sûrement dans la table Car il devrait y avoir un personID? Pas un CarId dans la table Person?

SOLUTION:

Avec un grand merci à Ivan ce fut ma solution. Je l'ai mis ici pour que je puisse marquer sa question comme réponse.

Mes cours regardent maintenant comme ceci:

public class Person 
{ 
    [Key] 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public virtual List<Car> Cars { get; set; } 
} 
public class Car 
{ 
    [Key] 
    public int id { get; set; } 
    public string CarName { get; set; } 
} 

Lors des tests, même si Ivan dit que je ne devrais pas avoir besoin j'ai trouvé la suppression en cascade ne fonctionnerait pas si je gardais le code:

modelBuilder.Entity<Person>() 
      .HasMany(a => a.Cars) 
      .WithOptional() // or `WithRequired() in case Car requires Person 
      .WillCascadeOnDelete(true); 
+0

Essayez avec mot-clé virtuel. par exemple 'public virtual ICollection Cars {get; ensemble; } 'au lieu de' public Liste Cars {get; ensemble; } ' –

+0

@AmitKumar même erreur. –

+0

Si une personne peut avoir plusieurs voitures, alors votre relation est mal établie. Car devrait avoir un Person_Id FK, pas l'inverse. Regardez votre image liée, elle vous montre une relation (beaucoup de personne à une voiture), pas une relation (une personne à une voiture). – Flater

Répondre

4

La configuration de la relation couramment

modelBuilder.Entity<Person>() 
    .HasOptional(a => a.Cars) 
    .WithOptionalDependent() 
    .WillCascadeOnDelete(true); 

est faux. HasOptional, HasRequired, WithOptionalDependent, WithOptionalPrincipal etc sont pour les relations one-to-one, alors que vous avez one-to-many.

La configuration correcte est la suivante:

modelBuilder.Entity<Person>() 
    .HasMany(a => a.Cars) 
    .WithOptional() // or `WithRequired() in case Car requires Person 
    .WillCascadeOnDelete(true); 

Maintenant, la migration devrait ressembler à ceci:

CreateTable(
    "dbo.People", 
    c => new 
     { 
      Id = c.Int(nullable: false, identity: true), 
      Name = c.String(), 
     }) 
    .PrimaryKey(t => t.Id); 

CreateTable(
    "dbo.Cars", 
    c => new 
     { 
      id = c.Int(nullable: false, identity: true), 
      CarName = c.String(), 
      Person_Id = c.Int(), 
     }) 
    .PrimaryKey(t => t.id) 
    .ForeignKey("dbo.People", t => t.Person_Id, cascadeDelete: true) 
    .Index(t => t.Person_Id); 
+0

pensez-vous que mes cours sont corrects? –

+0

également - quand je regarde la relation entre les gens et les voitures maintenant il dit que la règle de suppression et de mise à jour sont "Aucune action" –

+0

C'est l'un des * EF * modèle * valide représentant «one-to-many» - avec la propriété de navigation de collection au * un * côté. Vous pouvez également ajouter la navigation de référence et/ou la propriété FK à plusieurs côtés, mais ce n'est pas * obligatoire *. –

1

En fait, vous n'avez pas ajouté de propriété Person dans votre classe de modèle de voiture. Votre modèle devrait ressembler à ceci.

Voir ici.Cascade delete in one to many relationship

public class Person 
{ 
    [Key] 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public virtual ICollection<Car> Cars { get; set; } 
} 
public class Car 
{ 
    [Key] 
    public int id { get; set; } 
    public string CarName { get; set; } 
    public virtual Person Person { get; set;} 
} 

Exécutez maintenant la migration. Et testez-le.

1

D'abord, je pense qu'il est préférable d'ajouter le mot-clé virtuel en classe personne (pour le chargement paresseux):

public virtual List<Car> Cars { get; set; } 

Vous pouvez ajouter une référence à la personne de la classe de voiture avec l'annotation requise:

[Required] 
public virtual Person Person { get; set; } 

étant donné qu'une personne est requise, la suppression d'une personne entraînera la suppression de toutes ses voitures.