Comment puis-je empêcher Entity Framework d’essayer d’enregistrer / insérer des objects enfants?

Lorsque je sauvegarde une entité avec une structure d’entité, j’ai naturellement supposé qu’elle essaierait uniquement de sauvegarder l’entité spécifiée. Cependant, il essaie également de sauvegarder les entités enfants de cette entité. Cela provoque toutes sortes de problèmes d’intégrité. Comment forcer EF à ne sauvegarder que l’entité que je veux enregistrer et donc ignorer tous les objects enfants?

Si je définis manuellement les propriétés sur null, j’obtiens une erreur “L’opération a échoué: la relation n’a pas pu être modifiée car une ou plusieurs des propriétés de la clé étrangère ne sont pas nullables.” Ceci est extrêmement contre-productif puisque j’ai défini l’object enfant sur null pour que EF le laisse seul.

Pourquoi ne veux-je pas enregistrer / insérer les objects enfants?

Puisque cela est discuté dans les commentaires, je vais expliquer pourquoi je veux que mes objects enfants restnt seuls.

Dans l’application que je construis, le modèle d’object EF n’est pas chargé depuis la firebase database, mais utilisé comme object de données que je remplis lors de l’parsing d’un fichier plat. Dans le cas des objects enfants, beaucoup d’entre eux se réfèrent à des tables de consultation définissant diverses propriétés de la table parente. Par exemple, l’emplacement géographique de l’entité principale.

Étant donné que j’ai rempli ces objects moi-même, EF suppose que ces objects sont nouveaux et doivent être insérés avec l’object parent. Cependant, ces définitions existent déjà et je ne veux pas créer de doublons dans la firebase database. Je n’utilise que l’object EF pour effectuer une recherche et remplir la clé étrangère dans mon entité de table principale.

Même avec les objects enfants qui sont des données réelles, je dois d’abord enregistrer le parent et obtenir une clé primaire, ou bien EF semble simplement créer des problèmes. J’espère que cela donne une explication.

Pour autant que je sache, vous avez deux options.

Option 1)

Null tous les objects enfants, cela garantira à EF de ne rien append. Il ne supprimera également rien de votre firebase database.

Option 2)

Définissez les objects enfants comme détachés du contexte à l’aide du code suivant

context.Entry(yourObject).State = EntityState.Detached 

Notez que vous ne pouvez pas détacher une List / Collection . Vous devrez parcourir votre liste et détacher chaque élément de votre liste comme

 foreach (var item in properties) { db.Entry(item).State = EntityState.Detached; } 

Longue histoire courte: Utilisez la clé étrangère et cela sauvera votre journée.

Supposons que vous ayez une entité scolaire et une entité de la ville , et que la relation entre une ville et de nombreuses écoles et une école appartient à une ville. Et supposons que les villes existent déjà dans la table de recherche, donc vous ne voulez PAS qu’elles soient insérées à nouveau lors de l’insertion d’une nouvelle école.

Au début, vous pouvez définir des entités comme ceci:

 public class City { public int Id { get; set; } public ssortingng Name { get; set; } } public class School { public int Id { get; set; } public ssortingng Name { get; set; } [Required] public City City { get; set; } } 

Et vous pourriez faire l’insertion de l’ école comme ceci (supposez que vous avez déjà une propriété de ville assignée à newItem ):

 public School Insert(School newItem) { using (var context = new DatabaseContext()) { context.Set().Add(newItem); // use the following statement so that City won't be inserted context.Entry(newItem.City).State = EntityState.Unchanged; context.SaveChanges(); return newItem; } } 

L’approche ci-dessus peut fonctionner parfaitement dans ce cas, cependant, je préfère l’approche de la clé étrangère qui est pour moi plus claire et plus flexible. Voir la solution mise à jour ci-dessous:

 public class City { public int Id { get; set; } public ssortingng Name { get; set; } } public class School { public int Id { get; set; } public ssortingng Name { get; set; } [ForeignKey("City_Id")] public City City { get; set; } [Required] public int City_Id { get; set; } } 

De cette manière, vous définissez explicitement que l’ école a une clé étrangère City_Id et qu’elle fait référence à l’entité City . Donc, en ce qui concerne l’insertion de l’ école , vous pouvez faire:

  public School Insert(School newItem, int cityId) { if(cityId <= 0) { throw new Exception("City ID no provided"); } newItem.City = null; newItem.City_Id = cityId; using (var context = new DatabaseContext()) { context.Set().Add(newItem); context.SaveChanges(); return newItem; } } 

Dans ce cas, vous spécifiez explicitement le City_Id du nouvel enregistrement et supprimez la ville du graphique pour que EF ne prenne pas la peine de l’append au contexte avec School .

Bien qu’à la première impression, l’approche par clé étrangère semble plus compliquée, mais croyez-moi, cette mentalité vous fera gagner beaucoup de temps quand il s’agira d’établir une relation plusieurs-à-plusieurs a une propriété de la ville) et ainsi de suite.

J’espère que ça te sera utile.

L’une des solutions suggérées consiste à atsortingbuer la propriété de navigation à partir du même contexte de firebase database. Dans cette solution, la propriété de navigation atsortingbuée en dehors du contexte de la firebase database serait remplacée. Veuillez vous reporter à l’exemple suivant pour illustration.

 class Company{ public int Id{get;set;} public Virtual Department department{get; set;} } class Department{ public int Id{get; set;} public Ssortingng Name{get; set;} } 

Enregistrement dans la firebase database:

  Company company = new Company(); company.department = new Department(){Id = 45}; //an Department object with Id = 45 exists in database. using(CompanyContext db = new CompanyContext()){ Department department = db.Departments.Find(company.department.Id); company.department = department; db.Companies.Add(company); db.SaveChanges(); } 

Microsoft l’enregistre comme une fonctionnalité, mais je trouve cela ennuyeux. Si l’object départemental associé à l’object société possède un identifiant qui existe déjà dans la firebase database, alors pourquoi EF n’associe-t-il pas uniquement un object société à un object firebase database? Pourquoi devrions-nous nous occuper de l’association par nous-mêmes? Prendre soin de la propriété de navigation pendant l’ajout d’un nouvel object, c’est comme déplacer les opérations de firebase database de SQL vers C #, ce qui est lourd pour les développeurs.

Vous devez d’abord savoir qu’il existe deux manières de mettre à jour l’entité dans EF.

  • Objets attachés

    Lorsque vous modifiez la relation des objects attachés au contexte de l’object à l’aide de l’une des méthodes décrites ci-dessus, Entity Framework doit synchroniser les clés, les références et les collections étrangères.

  • Objets déconnectés

    Si vous travaillez avec des objects déconnectés, vous devez gérer manuellement la synchronisation.

Dans l’application que je construis, le modèle d’object EF n’est pas chargé depuis la firebase database, mais utilisé comme object de données que je remplis lors de l’parsing d’un fichier plat.

Cela signifie que vous travaillez avec un object déconnecté, mais vous ne savez pas si vous utilisez une association indépendante ou une association de clé étrangère .

  • Ajouter

    Lors de l’ajout d’une nouvelle entité avec un object enfant existant (object existant dans la firebase database), si l’object enfant n’est pas suivi par EF, l’object enfant sera réinséré. Sauf si vous attachez manuellement l’object enfant en premier.

     db.Entity(entity.ChildObject).State = EntityState.Modified; db.Entity(entity).State = EntityState.Added; 
  • Mettre à jour

    Vous pouvez simplement marquer l’entité comme modifiée, puis toutes les propriétés scalaires seront mises à jour et les propriétés de navigation seront simplement ignorées.

     db.Entity(entity).State = EntityState.Modified; 

Graphique Diff

Si vous souhaitez simplifier le code lorsque vous travaillez avec un object déconnecté, vous pouvez essayer la bibliothèque de diff graphique .

Voici l’introduction, Présentation de GraphDiff pour Entity Framework Code First – Autoriser les mises à jour automatisées d’un graphe d’entités détachées .

Exemple de code

  • Insérer une entité si elle n’existe pas, sinon mettre à jour.

     db.UpdateGraph(entity); 
  • Insérer l’entité si elle n’existe pas, sinon mettre à jour AND insérer un object enfant s’il n’existe pas, sinon mettre à jour.

     db.UpdateGraph(entity, map => map.OwnedEntity(x => x.ChildObject)); 

Si vous souhaitez simplement stocker les modifications apscopes à un object parent et éviter de stocker les modifications apscopes à l’un de ses objects enfants , pourquoi ne pas procéder comme suit:

 using (var ctx = new MyContext()) { ctx.Parents.Attach(parent); ctx.Entry(parent).State = EntityState.Added; // or EntityState.Modified ctx.SaveChanges(); } 

La première ligne joint l’object parent et le graphique entier de ses objects enfants dépendants au contexte dans l’état Unchanged .

La deuxième ligne modifie uniquement l’état de l’object parent, laissant ses enfants à l’état Unchanged .

Notez que j’utilise un contexte nouvellement créé, ce qui évite d’enregistrer d’autres modifications dans la firebase database.

Le meilleur moyen d’y parvenir consiste à remplacer la fonction SaveChanges dans votre contexte de données.

  public override int SaveChanges() { var added = this.ChangeTracker.Ensortinges().Where(e => e.State == System.Data.EntityState.Added); // Do your thing, like changing the state to detached return base.SaveChanges(); } 

Cela a fonctionné pour moi:

 // temporarily 'detach' the child entity/collection to have EF not attempting to handle them var temp = entity.ChildCollection; entity.ChildCollection = new HashSet(); .... do other stuff context.SaveChanges(); entity.ChildCollection = temp; 

Je connais son ancien message, mais si vous utilisez une approche basée sur le code, vous pouvez obtenir le résultat souhaité en utilisant le code suivant dans votre fichier de mappage.

 Ignore(parentObject => parentObject.ChildObjectOrCollection); 

Cela va essentiellement dire à EF d’exclure la propriété “ChildObjectOrCollection” du modèle afin qu’il ne soit pas mappé à la firebase database.

Ce que nous avons fait est avant d’append le parent au dbset, déconnecter les collections enfants du parent, en veillant à pousser les collections existantes vers d’autres variables pour permettre de les utiliser ultérieurement, puis en remplaçant les collections enfant actuelles par de nouvelles collections vides. Définir les collections enfants sur null / Rien semblait échouer pour nous. Après cela, ajoutez le parent au dbset. De cette façon, les enfants ne sont pas ajoutés tant que vous ne le souhaitez pas.

J’ai le même problème lorsque j’essaie de sauvegarder mon profil, je dépose déjà des salutations et de nouvelles pour créer un profil. Lorsque j’insère un profil, il insère également dans les salutations. Donc j’ai essayé comme ça avant savechanges ().

db.Entry (Profile.Salutation) .State = EntityState.Unchanged;