La Violation de la contrainte de CLÉ PRIMAIRE: Impossible d'insérer une clé en double dans l'objet

Quand je veux persister un modèle complexe, j'ai cette erreur. Je pense que je sais d'où il vient, mais je ne sais pas comment le résoudre. Je suis de l'importation d'un peu de flux et de créer des objets automatiquement, y compris les enfants (plusieurs-à-plusieurs).

{"Violation de la contrainte de CLÉ PRIMAIRE 'PK_dbo.Parent'. Impossible d'insérer
clé en double dans l'objet 'dbo.Parent'. La valeur de clé en double est
(291).\r\nle instruction a été arrêtée."}

L'erreur parle d'elle-même, mais comment l'empêcher? 🙂

Le code qu'il déclenche

var parser = new SchoolFeedReader();
var update = parser.GetAll();
var students = Mapper.Map<List<StudentDTO>, List<Student>>(update);
using (var db = new SchoolContext())
{
    //I'm updating every night, so clean out the database before import
    db.Database.ExecuteSqlCommand("DELETE FROM Student");
    db.Database.ExecuteSqlCommand("DELETE FROM Parent");
    db.Database.ExecuteSqlCommand("DELETE FROM Subject");
    db.Database.ExecuteSqlCommand("DELETE FROM StudentParent");
    db.Database.ExecuteSqlCommand("DELETE FROM StudentSubject");

    students.ForEach(s => db.Students.Add(s));
    db.SaveChanges(); //Triggers the Exception
}

Le TL;DR

Pour un schoolproject j'ai besoin d'importer des 3 Flux XML dans la base de données.

  • Students.xml
  • Parents.xml
  • Subjects.xml

Dans Students.xml j'ai rencontré un défaut de conception: un nombre fixe (3) de Parents possibles.

<student>
    <StudentId>100</StudentId>
    <Name>John Doe</Name>
    <Location>Main Street</Location>
    <Parent1>1002</Parent1>
    <Parent2>1002</Parent2>
    <Parent3/>
</student>
(... more students)

Dans Parents.xml les choses sont plus simples.

<parent>
    <ParentId>1102</ParentId>
    <Name>Dad Doe</Name>
    <Email>dad@doe.com</Email>
</parent>
(... more parents)

Et Subjects.xml est également très simple.

<subject>
    <StudentId>100</StudentId>
    <Name>English</Name>
</subject>
(... more subjects)

Les Modèles

J'ai donc créé 3 modèles, y compris les Dto.

public class Student
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public long StudentId { get; set; }
public string Name { get; set; }
public string Location { get; set; }
[InverseProperty("Students")]
public virtual ICollection<Parent> Parents { get; set; }
public virtual ICollection<Subject> Subjects { get; set; } 
}
public class StudentDTO
{
public long StudentId { get; set; }
public string Name { get; set; }
public string Location { get; set; }
public List<ParentDTO> Parents { get; set; }
public List<SubjectDTO> Subjects { get; set; } 
}
public class Parent
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public long ParentId { get; set; }
public string Name { get; set; }
public string Email { get; set; }
[InverseProperty("Parents")]
public virtual ICollection<Student> Students { get; set; } 
}
public class ParentDTO
{
public long ParentId { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public List<StudentDTO> Students { get; set; }
public ParentDTO()
{
Students = new List<StudentDTO>();
}
}
public class Subject
{
public long SubjectId { get; set; }
public string Name { get; set; }
public virtual List<Student> Students { get; set; }
}
public class SubjectDTO
{
public string Name { get; set; }
public List<StudentDTO> Students { get; set; }
public SubjectDTO()
{
Students = new List<StudentDTO>();
}
}

À partir de XML à des Otd

L'Importateur de la classe a, ce géant de la requête LINQ pour obtenir tout ce dont j'ai besoin, dans un grand coup.

var query = from student in _xStudents.Descendants("Student")
select new StudentDTO
{
StudentId = (long)student.Element("StudentId"),
Name = (String)student.Element("Name"),
Subjects = (
from subject in _xSubjects.Descendants("Subject").DefaultIfEmpty()
where (String)student.Element("StudentId") == (String)subject.Element("StudentId")
select new SubjectDTO
{
Name = (String)subject.Element("Name")
}
).ToList(),
Parents = (
from parent in _xParents.Descendants("Parent").DefaultIfEmpty()
group parent by (String)parent.Element("ParentId") into pg
where (String)student.Element("Parent1") == (String)pg.FirstOrDefault().Element("ParentId") ||
(String)student.Element("Parent2") == (String)pg.FirstOrDefault().Element("ParentId") ||
(String)student.Element("Parent3") == (String)pg.FirstOrDefault().Element("ParentId")
select new ParentDTO
{
ParentId = (long)pg.FirstOrDefault().Element("ParentId"),
Name = (String)pg.FirstOrDefault().Element("Name")
}
).ToList()
};

Qui fonctionne très bien, certains étudiants obtiennent 2 parents, certains se 1, de sorte que mes données semble bon.

Le Problème

J'ai ces AutoMappers dans mon Global.asax.cs:

Mapper.CreateMap<StudentDTO, Student>()
.ForMember(dto => dto.Parents, opt => opt.MapFrom(x => x.Parents))
.ForMember(dto => dto.Subjects, opt => opt.MapFrom(x => x.Subjects));
Mapper.CreateMap<ParentDTO, Parent>();
Mapper.CreateMap<SubjectDTO, Subject>();

Mais quand je lance l'import j'ai des erreurs sur mon db.SaveChanges(). Il se plaint d'un double ForeignKey sur le modèle Parent. Donc, je pense qu':

c'est un Plusieurs-à-Plusieurs relations, donc si John Doe sœur, Jane Doe, essaie d'insérer le même Papa Doe, puis il se bloque

Alors, Comment puis-je m'assurer que l'ensemble de parcours de Business Objects seulement 1 référence de chaque entité; comment supprimer le doublon de papa et de maman? J'ai probablement envie de le faire aussi pour le Sujet.

  • Sur une note connexe: si vous voulez avoir deux parents, puis ajouter les clés étrangères pour Parent1 et Parent2 (Père et Mère en termes historiques). De la création de nombreuses de nombreuses pour la de la parentalité permet à quelqu'un d'avoir 20+ parents, comme Eric Cartman.
  • Sur une seconde réflexion: Depuis l'un des parents peuvent avoir plusieurs enfants parmi les étudiants, si vous essayez d'insérer une même mère deux fois, vous obtiendrez la clé primaire de violation de contrainte.
InformationsquelleAutor NaNerd | 2013-05-21