web-dev-qa-db-fra.com

La relation n'a pas pu être modifiée car une ou plusieurs propriétés de clé étrangère ne sont pas nullables.

Je reçois cette erreur lorsque je GetById () sur une entité, puis définit la collection d'entités enfants sur ma nouvelle liste, qui provient de la vue MVC.

L'opération a échoué: le la relation ne pouvait pas être changée parce qu'un ou plusieurs de la clé étrangère properties est non nullable. Lorsqu'un un changement est apporté à une relation, la La propriété de clé étrangère associée est définie sur une valeur nulle. Si la clé étrangère le fait supporte pas les valeurs NULL, un nouveau fichier la relation doit être définie, le Une propriété de clé étrangère doit être affectée une autre valeur non nulle, ou le objet non lié doit être supprimé.

Je ne comprends pas bien cette ligne:

La relation n'a pas pu être changée parce qu'un ou plusieurs de la clé étrangère properties est non nullable.

Pourquoi devrais-je changer la relation entre 2 entités? Il devrait rester le même pendant toute la durée de vie de l'application.

Le code sur lequel l'exception se produit est une simple affectation des classes enfant modifiées d'une collection à la classe parent existante. Espérons que cela permettrait de supprimer les classes enfants, d’en ajouter de nouvelles et de les modifier. J'aurais pensé qu'Entity Framework s'en occupe.

Les lignes de code peuvent être distillées à:

var thisParent = _repo.GetById(1);
thisParent.ChildItems = modifiedParent.ChildItems();
_repo.Save();
163
jaffa

Vous devez supprimer les anciens éléments enfants thisParent.ChildItems un à un manuellement. Entity Framework ne fait pas cela pour vous. Enfin, il ne peut pas décider de ce que vous voulez faire avec les anciens éléments enfants - si vous souhaitez les jeter ou si vous souhaitez les conserver et les affecter à d'autres entités mères. Vous devez informer Entity Framework de votre décision. Mais vous DEVEZ prendre l'une de ces deux décisions, car les entités enfants ne peuvent pas vivre seules sans une référence à un parent de la base de données (en raison de la contrainte de clé étrangère). C'est essentiellement ce que l'exception dit. 

Modifier

Ce que je ferais si des éléments enfants pouvaient être ajoutés, mis à jour et supprimés:

public void UpdateEntity(ParentItem parent)
{
    // Load original parent including the child item collection
    var originalParent = _dbContext.ParentItems
        .Where(p => p.ID == parent.ID)
        .Include(p => p.ChildItems)
        .SingleOrDefault();
    // We assume that the parent is still in the DB and don't check for null

    // Update scalar properties of parent,
    // can be omitted if we don't expect changes of the scalar properties
    var parentEntry = _dbContext.Entry(originalParent);
    parentEntry.CurrentValues.SetValues(parent);

    foreach (var childItem in parent.ChildItems)
    {
        var originalChildItem = originalParent.ChildItems
            .Where(c => c.ID == childItem.ID && c.ID != 0)
            .SingleOrDefault();
        // Is original child item with same ID in DB?
        if (originalChildItem != null)
        {
            // Yes -> Update scalar properties of child item
            var childEntry = _dbContext.Entry(originalChildItem);
            childEntry.CurrentValues.SetValues(childItem);
        }
        else
        {
            // No -> It's a new child item -> Insert
            childItem.ID = 0;
            originalParent.ChildItems.Add(childItem);
        }
    }

    // Don't consider the child items we have just added above.
    // (We need to make a copy of the list by using .ToList() because
    // _dbContext.ChildItems.Remove in this loop does not only delete
    // from the context but also from the child collection. Without making
    // the copy we would modify the collection we are just interating
    // through - which is forbidden and would lead to an exception.)
    foreach (var originalChildItem in
                 originalParent.ChildItems.Where(c => c.ID != 0).ToList())
    {
        // Are there child items in the DB which are NOT in the
        // new child item collection anymore?
        if (!parent.ChildItems.Any(c => c.ID == originalChildItem.ID))
            // Yes -> It's a deleted child item -> Delete
            _dbContext.ChildItems.Remove(originalChildItem);
    }

    _dbContext.SaveChanges();
}

Note: Ceci n'est pas testé. Cela suppose que la collection d'éléments enfants est de type ICollection. (J'ai généralement IList et le code est un peu différent.) J'ai également supprimé toutes les abstractions du référentiel pour que cela reste simple.

Je ne sais pas si c'est une bonne solution, mais je pense qu'il faut travailler dur dans ce sens pour prendre en charge toutes sortes de changements dans la collection de navigation. Je serais également heureux de voir un moyen plus simple de le faire.

145
Slauma

La raison pour laquelle vous faites face à cela est due à la différence entre composition et agrégation

Dans la composition, l'objet enfant est créé lors de la création du parent et détruit lors de la destruction de son parent . Donc, sa durée de vie est contrôlée par son parent. par exemple. Un article de blog et ses commentaires. Si un message est supprimé, ses commentaires doivent être supprimés. Cela n'a aucun sens d'avoir des commentaires pour un article qui n'existe pas. Idem pour les commandes et les articles de commande.

Dans l'agrégation, l'objet enfant peut exister indépendamment de son parent . Si le parent est détruit, l'objet enfant peut toujours exister, car il peut être ajouté à un autre parent ultérieurement. Par exemple: la relation entre une liste de lecture et les chansons de cette liste. Si la liste de lecture est supprimée, les chansons ne doivent pas être supprimées. Ils peuvent être ajoutés à une liste de lecture différente.

La manière dont Entity Framework différencie les relations d'agrégation et de composition est la suivante:

  • Pour la composition: l'objet enfant doit disposer d'une clé primaire composite (ParentID, ChildID). Ceci est voulu par la volonté des parents d’identifier les identifiants des enfants. 

  • Pour l'agrégation: il s'attend à ce que la propriété de clé étrangère de l'objet enfant soit nullable.

La raison pour laquelle vous rencontrez ce problème est donc due à la façon dont vous avez défini votre clé primaire dans votre table enfant. Ce devrait être composite, mais ce n'est pas. Entity Framework considère donc cette association comme une agrégation, ce qui signifie que lorsque vous supprimez ou effacez les objets enfants, les enregistrements enfants ne seront pas supprimés. Cela supprime simplement l'association et définit la colonne de clé étrangère correspondante sur NULL (pour que ces enregistrements enfants puissent ensuite être associés à un autre parent). Puisque votre colonne n'autorise pas la valeur NULL, vous obtenez l'exception que vous avez mentionnée. 

Solutions:

1- Si vous avez une bonne raison de ne pas vouloir utiliser une clé composite, vous devez supprimer les objets enfants explicitement. Et cela peut être fait plus simplement que les solutions suggérées précédemment:

context.Children.RemoveRange(parent.Children);

2- Sinon, en définissant la clé primaire appropriée sur votre table enfant, votre code semblera plus significatif:

parent.Children.Clear();
81
Mosh

C'est un très gros problème. Voici ce qui se passe dans votre code:

  • Vous chargez Parent à partir de la base de données et obtenez une entité attachée
  • Vous remplacez sa collection d'enfants par une nouvelle collection d'enfants isolés
  • Vous enregistrez les modifications, mais pendant cette opération, tous les enfants sont considérés comme ajoutés, car EF ne les connaissait pas jusqu'à présent. Donc, EF essaie de mettre à zéro la clé étrangère des anciens enfants et d'insérer tous les nouveaux enfants => lignes en double.

Maintenant, la solution dépend vraiment de ce que vous voulez faire et comment voudriez-vous le faire? 

Si vous utilisez ASP.NET MVC, vous pouvez essayer d'utiliser UpdateModel ou TryUpdateModel .

Si vous voulez simplement mettre à jour manuellement les enfants existants, vous pouvez simplement faire quelque chose comme:

foreach (var child in modifiedParent.ChildItems)
{
    context.Childs.Attach(child); 
    context.Entry(child).State = EntityState.Modified;
}

context.SaveChanges();

L'attachement n'est en fait pas nécessaire (définir l'état sur Modified attachera également l'entité) mais j'aime bien parce que cela rend le processus plus évident.

Si vous souhaitez modifier des éléments existants, supprimer des éléments existants et en insérer de nouveaux, vous devez procéder comme suit:

var parent = context.Parents.GetById(1); // Make sure that childs are loaded as well
foreach(var child in modifiedParent.ChildItems)
{
    var attachedChild = FindChild(parent, child.Id);
    if (attachedChild != null)
    {
        // Existing child - apply new values
        context.Entry(attachedChild).CurrentValues.SetValues(child);
    }
    else
    {
        // New child
        // Don't insert original object. It will attach whole detached graph
        parent.ChildItems.Add(child.Clone());
    }
}

// Now you must delete all entities present in parent.ChildItems but missing
// in modifiedParent.ChildItems
// ToList should make copy of the collection because we can't modify collection
// iterated by foreach
foreach(var child in parent.ChildItems.ToList())
{
    var detachedChild = FindChild(modifiedParent, child.Id);
    if (detachedChild == null)
    {
        parent.ChildItems.Remove(child);
        context.Childs.Remove(child); 
    }
}

context.SaveChanges();
67
Ladislav Mrnka

J'ai trouvé this answer beaucoup plus utile pour la même erreur ... Il semble que EF ne l'aime pas quand vous supprimez, il préfère Supprimer.

Vous pouvez supprimer une collection d'enregistrements attachés à un enregistrement comme celui-ci.

order.OrderDetails.ToList().ForEach(s => db.Entry(s).State = EntityState.Deleted);

Dans l'exemple, l'état de tous les enregistrements de détail attachés à une commande est défini sur Supprimer. (En préparation pour ajouter des détails mis à jour, dans le cadre d'une mise à jour de la commande)

35
Greg Little

Je n'ai aucune idée pourquoi les deux autres réponses sont si populaires! 

Je pense que vous aviez raison de supposer que le cadre ORM devrait le gérer - après tout, c'est ce qu'il promet de réaliser. Sinon, votre modèle de domaine est corrompu par des problèmes de persistance. NHibernate gère cela avec joie si vous configurez correctement les paramètres de cascade. Dans Entity Framework, il est également possible, ils s'attendent simplement à ce que vous respectiez de meilleures normes lors de la configuration de votre modèle de base de données, en particulier lorsqu'ils doivent déduire ce qui doit être fait en cascade:

Vous devez définir correctement la relation parent-enfant en utilisant un " identifiant la relation ".

Si vous faites cela, Entity Framework sait que l'objet enfant est identifié par le parent et qu'il doit donc s'agir d'une situation "cascade-delete-orphans".

Autre que ce qui précède, vous pourriez en avoir besoin (de l'expérience NHibernate)

thisParent.ChildItems.Clear();
thisParent.ChildItems.AddRange(modifiedParent.ChildItems);

au lieu de remplacer entièrement la liste. 

METTRE À JOUR

Le commentaire de @ Slauma m'a rappelé que les entités détachées sont une autre partie du problème global. Pour résoudre ce problème, vous pouvez utiliser un classeur de modèle personnalisé qui construit vos modèles en essayant de le charger à partir du contexte. Cet article de blog montre un exemple de ce que je veux dire.

19
Andre Luus

Si vous utilisez AutoMapper avec Entity Framework sur la même classe, vous pouvez rencontrer ce problème. Par exemple si votre classe est

class A
{
    public ClassB ClassB { get; set; }
    public int ClassBId { get; set; }
}

AutoMapper.Map<A, A>(input, destination);

Cela va essayer de copier les deux propriétés. Dans ce cas, ClassBId n'est pas Nullable. Étant donné qu'AutoMapper copiera destination.ClassB = input.ClassB;, cela posera un problème.

Définissez votre AutoMapper sur Ignore la propriété ClassB.

 cfg.CreateMap<A, A>()
     .ForMember(m => m.ClassB, opt => opt.Ignore()); // We use the ClassBId
9
jsgoupil

Cela est dû au fait que l'entité enfant est marquée comme modifiée au lieu de supprimée.

Et la modification apportée par EF à l'entité enfant lorsque parent.Remove(child) est exécuté consiste simplement à définir la référence à son parent sur null.

Vous pouvez vérifier l'EntityState de l'enfant en tapant le code suivant dans la fenêtre Immediate de Visual Studio lorsque l'exception se produit, après l'exécution de SaveChanges():

_context.ObjectStateManager.GetObjectStateEntries(System.Data.EntityState.Modified).ElementAt(X).Entity

où X devrait être remplacé par l'entité supprimée.

Si vous n'avez pas accès à la ObjectContext pour exécuter _context.ChildEntity.Remove(child), vous pouvez résoudre ce problème en intégrant la clé étrangère à la clé primaire de la table enfant.

Parent
 ________________
| PK    IdParent |
|       Name     |
|________________|

Child
 ________________
| PK    IdChild  |
| PK,FK IdParent |
|       Name     |
|________________|

De cette façon, si vous exécutez parent.Remove(child), EF marquera correctement l'entité comme supprimée.

4
Mauricio Ramalho

Je viens d'avoir la même erreur . J'ai deux tables avec une relation parent-enfant, mais j'ai configuré un "cascade sur suppression" sur la colonne de clé étrangère dans la définition de la table de la table enfant . Alors quand je manuellement supprimer la ligne parent (via SQL) dans la base de données, elle supprimera automatiquement les lignes enfants.

Cependant, cela ne fonctionnait pas dans EF, l'erreur décrite dans ce fil de discussion apparaissait . La raison en était que, dans mon modèle de données d'entité (fichier edmx), les propriétés de l'association entre la table parent et la table enfant n'étaient pas correct . L'option End1 OnDelete a été configurée pour être none ("End1" dans mon modèle est la fin qui a une multiplicité de 1).

J'ai modifié manuellement l'option End1 OnDelete en Cascade et cela a fonctionné . Je ne sais pas pourquoi EF n'est pas en mesure de le détecter, lorsque je mets à jour le modèle à partir de la base de données (j'ai le premier modèle de base de données).

Pour être complet, voici comment mon code à supprimer ressemble à ceci:

   public void Delete(int id)
    {
        MyType myObject = _context.MyTypes.Find(id);

        _context.MyTypes.Remove(myObject);
        _context.SaveChanges(); 
   }    

Si je n’avais pas défini de suppression en cascade, je devrais supprimer les lignes enfants manuellement avant de supprimer la ligne parent.

3
Martin

Vous devez effacer manuellement la collection ChildItems et y ajouter de nouveaux éléments: 

thisParent.ChildItems.Clear();
thisParent.ChildItems.AddRange(modifiedParent.ChildItems);

Après cela, vous pouvez appeler la méthode d'extension DeleteOrphans qui traitera les entités orphelines (elle doit être appelée entre les méthodes DetectChanges et SaveChanges). 

public static class DbContextExtensions
{
    private static readonly ConcurrentDictionary< EntityType, ReadOnlyDictionary< string, NavigationProperty>> s_navPropMappings = new ConcurrentDictionary< EntityType, ReadOnlyDictionary< string, NavigationProperty>>();

    public static void DeleteOrphans( this DbContext source )
    {
        var context = ((IObjectContextAdapter)source).ObjectContext;
        foreach (var entry in context.ObjectStateManager.GetObjectStateEntries(EntityState.Modified))
        {
            var entityType = entry.EntitySet.ElementType as EntityType;
            if (entityType == null)
                continue;

            var navPropMap = s_navPropMappings.GetOrAdd(entityType, CreateNavigationPropertyMap);
            var props = entry.GetModifiedProperties().ToArray();
            foreach (var prop in props)
            {
                NavigationProperty navProp;
                if (!navPropMap.TryGetValue(prop, out navProp))
                    continue;

                var related = entry.RelationshipManager.GetRelatedEnd(navProp.RelationshipType.FullName, navProp.ToEndMember.Name);
                var enumerator = related.GetEnumerator();
                if (enumerator.MoveNext() && enumerator.Current != null)
                    continue;

                entry.Delete();
                break;
            }
        }
    }

    private static ReadOnlyDictionary<string, NavigationProperty> CreateNavigationPropertyMap( EntityType type )
    {
        var result = type.NavigationProperties
            .Where(v => v.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many)
            .Where(v => v.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One || (v.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.ZeroOrOne && v.FromEndMember.GetEntityType() == v.ToEndMember.GetEntityType()))
            .Select(v => new { NavigationProperty = v, DependentProperties = v.GetDependentProperties().Take(2).ToArray() })
            .Where(v => v.DependentProperties.Length == 1)
            .ToDictionary(v => v.DependentProperties[0].Name, v => v.NavigationProperty);

        return new ReadOnlyDictionary<string, NavigationProperty>(result);
    }
}
2
Sat

J'ai rencontré ce problème aujourd'hui et je voulais partager ma solution. Dans mon cas, la solution consistait à supprimer les éléments enfants avant d'obtenir le parent de la base de données.

Auparavant, je le faisais comme dans le code ci-dessous. Je vais ensuite obtenir la même erreur répertoriée dans cette question.

var Parent = GetParent(parentId);
var children = Parent.Children;
foreach (var c in children )
{
     Context.Children.Remove(c);
}
Context.SaveChanges();

Ce qui a fonctionné pour moi, c’est d’obtenir d’abord les éléments enfants, en utilisant le parentId (clé étrangère), puis de les supprimer. Ensuite, je peux obtenir le parent de la base de données et, à ce stade, il ne devrait plus y avoir d’éléments enfants et je peux en ajouter de nouveaux.

var children = GetChildren(parentId);
foreach (var c in children )
{
     Context.Children.Remove(c);
}
Context.SaveChanges();

var Parent = GetParent(parentId);
Parent.Children = //assign new entities/items here
2
Dino Bansigan

J'ai essayé ces solutions et beaucoup d'autres, mais aucune d'entre elles n'a vraiment fonctionné. Comme c'est la première réponse sur google, je vais ajouter ma solution ici.

La méthode qui a bien fonctionné pour moi consistait à supprimer les relations de la relation lors des commits. EF ne pouvait donc rien bousiller. Je l'ai fait en recherchant l'objet parent dans DBContext et en le supprimant. Comme les propriétés de navigation de l'objet retrouvé sont toutes nulles, les relations entre les enfants sont ignorées lors de la validation.

var toDelete = db.Parents.Find(parentObject.ID);
db.Parents.Remove(toDelete);
db.SaveChanges();

Notez que cela suppose que les clés étrangères sont configurées avec ON DELETE CASCADE. Ainsi, lorsque la ligne parent est supprimée, les enfants sont nettoyés par la base de données.

1
Steve

Ce type de solution a fait le tour pour moi:

Parent original = db.Parent.SingleOrDefault<Parent>(t => t.ID == updated.ID);
db.Childs.RemoveRange(original.Childs);
updated.Childs.ToList().ForEach(c => original.Childs.Add(c));
db.Entry<Parent>(original).CurrentValues.SetValues(updated);

Il est important de dire que cela supprime tous les enregistrements et les insère à nouveau . Mais pour mon cas (moins de 10) ça va.

J'espère que ça aide.

1

J'ai utilisé la solution de Mosh , mais il n'était pas évident pour moi de mettre en œuvre correctement la clé de composition dans le code. 

Alors voici la solution:

public class Holiday
{
    [Key, Column(Order = 0), DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int HolidayId { get; set; }
    [Key, Column(Order = 1), ForeignKey("Location")]
    public LocationEnum LocationId { get; set; }

    public virtual Location Location { get; set; }

    public DateTime Date { get; set; }
    public string Name { get; set; }
}
1
PeterB

J'ai également résolu mon problème avec la réponse de Mosh et je pensais que la réponse de PeterB était un peu similaire car elle utilisait une énumération comme clé étrangère. N'oubliez pas que vous devrez ajouter une nouvelle migration après avoir ajouté ce code.

Je peux également recommander cet article de blog pour d'autres solutions:

http://www.kianryan.co.uk/2013/03/orphaned-child/

Code:

public class Child
{
    [Key, Column(Order = 0), DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    public string Heading { get; set; }
    //Add other properties here.

    [Key, Column(Order = 1)]
    public int ParentId { get; set; }

    public virtual Parent Parent { get; set; }
}
0
Ogglas

J'ai rencontré ce problème avant plusieurs heures et j'ai tout essayé, mais dans mon cas, la solution était différente de celle indiquée ci-dessus.

Si vous utilisez une entité déjà extraite de la base de données et tentez de la modifier, l'erreur se produira, mais si vous obtenez une nouvelle copie de l'entité de la base de données, aucun problème ne devrait se produire . Ne l'utilisez pas:

 public void CheckUsersCount(CompanyProduct companyProduct) 
 {
     companyProduct.Name = "Test";
 }

Utilisez ceci:

 public void CheckUsersCount(Guid companyProductId)
 {
      CompanyProduct companyProduct = CompanyProductManager.Get(companyProductId);
      companyProduct.Name = "Test";
 }
0
Tanyo Ivanov

En utilisant la solution de Slauma, j'ai créé des fonctions génériques pour aider à mettre à jour les objets enfants et les collections d'objets enfants.

Tous mes objets persistants implémentent cette interface

/// <summary>
/// Base interface for all persisted entries
/// </summary>
public interface IBase
{
    /// <summary>
    /// The Id
    /// </summary>
    int Id { get; set; }
}

Avec cela, j'ai implémenté ces deux fonctions dans mon référentiel

    /// <summary>
    /// Check if orgEntry is set update it's values, otherwise add it
    /// </summary>
    /// <param name="set">The collection</param>
    /// <param name="entry">The entry</param>
    /// <param name="orgEntry">The original entry found in the database (can be <code>null</code> is this is a new entry)</param>
    /// <returns>The added or updated entry</returns>
    public T AddOrUpdateEntry<T>(DbSet<T> set, T entry, T orgEntry) where T : class, IBase
    {
        if (entry.Id == 0 || orgEntry == null)
        {
            entry.Id = 0;
            return set.Add(entry);
        }
        else
        {
            Context.Entry(orgEntry).CurrentValues.SetValues(entry);
            return orgEntry;
        }
    }

    /// <summary>
    /// check if each entry of the new list was in the orginal list, if found, update it, if not found add it
    /// all entries found in the orignal list that are not in the new list are removed
    /// </summary>
    /// <typeparam name="T">The type of entry</typeparam>
    /// <param name="set">The database set</param>
    /// <param name="newList">The new list</param>
    /// <param name="orgList">The original list</param>
    public void AddOrUpdateCollection<T>(DbSet<T> set, ICollection<T> newList, ICollection<T> orgList) where T : class, IBase
    {
        // attach or update all entries in the new list
        foreach (T entry in newList)
        {
            // Find out if we had the entry already in the list
            var orgEntry = orgList.SingleOrDefault(e => e.Id != 0 && e.Id == entry.Id);

            AddOrUpdateEntry(set, entry, orgEntry);
        }

        // Remove all entries from the original list that are no longer in the new list
        foreach (T orgEntry in orgList.Where(e => e.Id != 0).ToList())
        {
            if (!newList.Any(e => e.Id == orgEntry.Id))
            {
                set.Remove(orgEntry);
            }
        }
    }

Pour l'utiliser, je fais ce qui suit:

var originalParent = _dbContext.ParentItems
    .Where(p => p.Id == parent.Id)
    .Include(p => p.ChildItems)
    .Include(p => p.ChildItems2)
    .SingleOrDefault();

// Add the parent (including collections) to the context or update it's values (except the collections)
originalParent = AddOrUpdateEntry(_dbContext.ParentItems, parent, originalParent);

// Update each collection
AddOrUpdateCollection(_dbContext.ChildItems, parent.ChildItems, orgiginalParent.ChildItems);
AddOrUpdateCollection(_dbContext.ChildItems2, parent.ChildItems2, orgiginalParent.ChildItems2);

J'espère que cela t'aides


EXTRA: vous pouvez également créer une classe distincte DbContextExtentions (ou votre propre inferface contextuelle):

public static void DbContextExtentions {
    /// <summary>
    /// Check if orgEntry is set update it's values, otherwise add it
    /// </summary>
    /// <param name="_dbContext">The context object</param>
    /// <param name="set">The collection</param>
    /// <param name="entry">The entry</param>
    /// <param name="orgEntry">The original entry found in the database (can be <code>null</code> is this is a new entry)</param>
    /// <returns>The added or updated entry</returns>
    public static T AddOrUpdateEntry<T>(this DbContext _dbContext, DbSet<T> set, T entry, T orgEntry) where T : class, IBase
    {
        if (entry.IsNew || orgEntry == null) // New or not found in context
        {
            entry.Id = 0;
            return set.Add(entry);
        }
        else
        {
            _dbContext.Entry(orgEntry).CurrentValues.SetValues(entry);
            return orgEntry;
        }
    }

    /// <summary>
    /// check if each entry of the new list was in the orginal list, if found, update it, if not found add it
    /// all entries found in the orignal list that are not in the new list are removed
    /// </summary>
    /// <typeparam name="T">The type of entry</typeparam>
    /// <param name="_dbContext">The context object</param>
    /// <param name="set">The database set</param>
    /// <param name="newList">The new list</param>
    /// <param name="orgList">The original list</param>
    public static void AddOrUpdateCollection<T>(this DbContext _dbContext, DbSet<T> set, ICollection<T> newList, ICollection<T> orgList) where T : class, IBase
    {
        // attach or update all entries in the new list
        foreach (T entry in newList)
        {
            // Find out if we had the entry already in the list
            var orgEntry = orgList.SingleOrDefault(e => e.Id != 0 && e.Id == entry.Id);

            AddOrUpdateEntry(_dbContext, set, entry, orgEntry);
        }

        // Remove all entries from the original list that are no longer in the new list
        foreach (T orgEntry in orgList.Where(e => e.Id != 0).ToList())
        {
            if (!newList.Any(e => e.Id == orgEntry.Id))
            {
                set.Remove(orgEntry);
            }
        }
    }
}

et l'utiliser comme:

var originalParent = _dbContext.ParentItems
    .Where(p => p.Id == parent.Id)
    .Include(p => p.ChildItems)
    .Include(p => p.ChildItems2)
    .SingleOrDefault();

// Add the parent (including collections) to the context or update it's values (except the collections)
originalParent = _dbContext.AddOrUpdateEntry(_dbContext.ParentItems, parent, originalParent);

// Update each collection
_dbContext.AddOrUpdateCollection(_dbContext.ChildItems, parent.ChildItems, orgiginalParent.ChildItems);
_dbContext.AddOrUpdateCollection(_dbContext.ChildItems2, parent.ChildItems2, orgiginalParent.ChildItems2);
0
Bluemoon74

Ce problème se pose car nous essayons de supprimer la table parent alors que des données de table enfant sont présentes . Nous résolvons le problème à l'aide de la suppression en cascade.

Dans le modèle, créez une méthode dans la classe dbcontext.  

 modelBuilder.Entity<Job>()
                .HasMany<JobSportsMapping>(C => C.JobSportsMappings)
                .WithRequired(C => C.Job)
                .HasForeignKey(C => C.JobId).WillCascadeOnDelete(true);
            modelBuilder.Entity<Sport>()
                .HasMany<JobSportsMapping>(C => C.JobSportsMappings)
                  .WithRequired(C => C.Sport)
                  .HasForeignKey(C => C.SportId).WillCascadeOnDelete(true);

Après cela, dans notre appel API

var JobList = Context.Job                       
          .Include(x => x.JobSportsMappings)                                     .ToList();
Context.Job.RemoveRange(JobList);
Context.SaveChanges();

Cascade delete option supprime la table enfant parent ainsi que parent avec ce code simple. Faites-le essayer de cette manière simple.

Supprimer la plage utilisée pour supprimer la liste des enregistrements de la base de données Merci

0
Sowmiya V

J'étais confronté au même problème lorsque je supprimais mon enregistrement qu'un problème quelconque se produisait, car lorsque vous supprimez votre enregistrement, il vous manque quelque chose avant de supprimer l'en-tête/l'enregistrement principal; vous devez écrire le code correspondant à supprimer ses détails avant en-tête/maître J'espère que votre problème sera résolu.

0
Ghazi Hur