web-dev-qa-db-fra.com

Entity Framework: violation des contraintes d'intégrité référentielle dans plusieurs relations

Hey j'ai une application avec un tas de mise en cache inproc et un framework d'entités. Lorsque je veux écrire une mise à jour sur une entité, je rattache la copie en cache. Je surveille toutes les choses que j'ai attachées dans le cycle de vie du contexte afin de ne pas essayer de les attacher deux fois.

Une erreur s'est produite lors de l'attachement (très rarement dans la plupart des cas, cela fonctionne bien et est très rapide), ce qui indique ce qui suit:

Une violation de contrainte d'intégrité référentielle s'est produite: La propriété les valeurs qui définissent les contraintes référentielles ne sont pas cohérentes entre les objets principaux et dépendants dans la relation.

J'ai jeté un regard très attentif sur l'entité qui a l'air normal. Je pense que ce problème est dû à l'attachement/au détachement d'une clé étrangère lors de l'exécution des correctifs. 

Existe-t-il un bon moyen d’obtenir plus d’informations sur cette erreur ou cela peut-il se produire pour des raisons autres que le fait que l’entité était dans un état non attendu par EF?

EDIT: DB Diagram (note: j'utilise Codefirst. Je viens d'utiliser l'outil EDMX pour créer le diagramme. J'ai également découpé un tas de propriétés standard dans le modèle pour plus de simplicité.)

enter image description here

33
Luke McGregor

L'erreur peut se produire pour la relation un-à-plusieurs entre Person et Location que vous avez apparemment dans votre modèle en plus de la relation plusieurs-à-plusieurs. Par exemple, le code suivant lève l'exception:

using (var context = new MyContext())
{
    var person = new Person
    {
        CurrentLocationId = 1,
        CurrentLocation = new Location { Id = 2 }
    };
    context.People.Attach(person); // Exception
}

"Les valeurs de propriété qui définissent les contraintes référentielles" sont la valeur de propriété de clé étrangère CurrentLocationId et la valeur de clé primaire CurrentLocation.Id. Si ces valeurs sont différentes, l'exception est levée. (Avoir CurrentLocation comme null est autorisé.)

À mon avis, cette exception ne peut être levée que pour les associations de clé étrangère, car ce n'est que pour ce type d'association que vous avez des propriétés qui définissent des contraintes référentielles dans votre modèle. Il ne peut pas être lancé pour des associations indépendantes. Etant donné que chaque relation plusieurs-à-plusieurs est une association indépendante (pas de propriété de clé étrangère dans le modèle), je suppose que l'erreur n'est pas liée à votre relation plusieurs-à-plusieurs, mais à une à plusieurs.

45
Slauma

Je suis tombé sur une exception très similaire:

"A referential integrity constraint violation occurred: 
The property value(s) of 'ObjectA.PropertyX' on one end of a relationship 
do not match the property value(s) of 'ObjectB.PropertyY' on the other end."

La raison en était la suivante: Le côté client de l'API Web a envoyé une demande PUT avec l'objet entier, y compris la propriété de navigation (dans cet exemple, ObjectA (plus correctement ObjectB.ObjectA) était une propriété de navigation et était entièrement fourni par le client. ). Cela est dû au fait que le client reçoit l'intégralité de l'objet du serveur et le renvoie tel quel au serveur avec des modifications mineures.

D'autre part, ObjectB.PropertyY venait juste d'être changé (c'était la raison de la demande PUT en premier lieu).

ObjectB.PropertyY étant une référence au même objet ObjectA (une clé étrangère), EF a tenté de le réconcilier et a échoué avec l'exception ci-dessus.

La solution était simple:

ObjectB.ObjectA = null;

avant que SaveChanges () résolve cela complètement.

J'espère que ça aidera quelqu'un.

6
Erez Lerner

Je viens juste de rencontrer le même problème et la solution à la mienne était d’avoir ajouté des mappages à l’association, puis de configurer les contraintes référentielles. 

Afin de résoudre le problème, je devais ouvrir la fenêtre de mappages pour l'association et il y avait un lien pour supprimer les mappages. Cela fait, la fenêtre Mapping Details (Détails du mappage) est alors interdite. Il semble que l'ajout de la contrainte référentielle laisse tous les mappages en place.

Nous pensons que cela pourrait valoir la peine d'être publié au cas où quelqu'un d'autre chercherait des solutions à ce message d'erreur à l'avenir.

2
Jason Underhill

Pour ajouter à la réponse de @ Slauma, il ne suffit pas d'ajouter des objets à votre contexte. Par exemple, si vous modifiez le CurrentLocationId dans une personne, vous devez également modifier l'objet CurrentLocation incorporé dans l'objet Personne. EF remplira automatiquement l'objet CurrentLocation car CurrentLocationId a une clé étrangère dans la table CurrentLocation. Lorsque vous éditez le CurrentLocationId sans mettre également à jour l'objet CurrentLocation, ils ne sont plus synchronisés. C'est ce qui cause l'exception dans ce cas.

Supposons donc que vous deviez mettre à jour CurrentLocationId de l'objet Personne. Nous supposerons que vous avez pré-récupéré les données de personne et les données de localisation.

public class DbData 
{
    List<Person> PersonList;
    List<Location> LocationList;
    public DbData()
    {
        using (var context = new MyContext())
        {
             PersonList = context.Persons.ToList();
             LocationList = context.Locations.ToList();
        }
    }

    public void UpdatePersonLocation(Person person, int newLocationId)
    {
        using (var context = new MyContext())
        {
            var location = LocationList.Where(l=>l.id==newLocationId).Single();
            //you need to update both the id and the location for this to not throw the exception
            person.CurrentLocationId == newLocationId;
            person.CurrentLocation == location;  
            context.Entry(person).State = System.Data.Entity.EntityState.Modified;
            context.SaveChanges();
        }
    }
    //or if you're giving it the location object...
    public void UpdatePersonLocation(Person person, Location location)
    {
        using (var context = new MyContext())
        {
            //you need to update both the id and the location for this to not throw the exception
            person.CurrentLocationId == location.id;
            person.CurrentLocation == location;  
            context.Entry(person).State = System.Data.Entity.EntityState.Modified;
            context.SaveChanges();
        }
    }
}
1
Migit

@LukeMcGregor salut,

Je pense que je peux offrir une perspective différente en tant que personne qui a le même problème.

Après avoir effectué toutes les vérifications nécessaires, je peux dire que je préfère obtenir cette erreur.

Parce que dans mon scénario: je voulais inclure un objet qui a provoqué une erreur d'incompatibilité. C'est l'objet de localisation dans votre scénario. Si j'ajoute un objet avec un ID, j'obtiens cette erreur car l'ID de l'objet précédent (celui qui n'est pas mis à jour) ne correspond pas à l'ID mis à jour.

Mais ce n'est pas un gros problème. Comme solution; S'il se trouve toujours du côté de l'interface utilisateur, l'objet peut toujours être inclus s'il existe toujours.

Vous pouvez soit vider l'objet lorsque vous recevez la demande de mise à jour de l'utilisateur. (= Null) Vous pouvez également mettre à jour l'objet avec l'ID mis à jour par l'utilisateur avant la mise à jour côté service (joindre, modifier, etc.) et le mettre à jour de cette manière.

C'est tout. Il peut rester tel quel dans la base de données et les diagrammes.

1
OkurYazar

Je viens d'avoir ce problème et est venu avec une solution assez rapide. Mon problème était avec une table plusieurs-beaucoup. 

Public class Pictures_Tag
{
    [Key]
    [Column(Order = 0)]
    [ForeignKey("Picture")]
    public Int16 Picture_ID { get; set; }
    [Key]
    [Column(Order = 1)]
    [ForeignKey("ImageTag")]
    public Int16 ImageTag_ID { get; set; }
    public virtual Picture Picture { get; set; }
    public virtual ImageTag ImageTag { get; set; }
}

J'ai ajouté la ligne où j'ai assigné Picture = db.Pictures... et ensuite ça a bien fonctionné (je ne sais pas trop pourquoi)

[HttpPost]
public ActionResult Edit2(WebSiteEF2017C.Models.Pictures_Tag p)
{     
    using (var db = new thisModel(Session["org"].ToString())
    {
         p.Picture = db.Pictures.Where(z => z.ID == p.Picture_ID).FirstOrDefault();
         db.Pictures_Tags.Attach(p);
         db.Entry(p).State = EntityState.Modified;
         db.SaveChanges();
         return View(db.Pictures_Tags.Include(x => x.Picture)
                    .Where(n => n.Picture_ID == p.Picture_ID & n.ImageTag_ID == p.ImageTag_ID).FirstOrDefault());
    }
}
0
clarence_odbody