3

複雑なモデルを永続化したい場合、このエラーが発生します。どこから来たのかはわかっていると思いますが、解決方法がわかりません。いくつかのフィードをインポートし、子 (多対多) を含むオブジェクトを自動的に作成します。

{"PRIMARY KEY 制約 'PK_dbo.Parent' に違反しています。オブジェクト 'dbo.Parent' に重複キーを挿入できません。重複キーの値は (291) です。\r\nステートメントは終了しました。"}

エラーはそれ自体を物語っていますが、それを防ぐ方法は? :)

それを引き起こすコード

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
}

TL;DR

学校のプロジェクトでは、3 つの XML フィードをデータベースにインポートする必要があります。

  • 学生.xml
  • 親.xml
  • Subjects.xml

Students.xml で設計上の欠陥に遭遇しました。可能な親の固定数 (3) です。

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

Parents.xml では、物事はより単純です。

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

また、Subjects.xml も非常にシンプルです。

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

モデル

そこで、DTO を含む 3 つのモデルを作成しました。

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>();
    }
}

XML から DTO へ

Importer クラスには、必要なものすべてを一度に取得するための巨大な LINQ クエリがあります。

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()
            };

これは問題なく動作し、一部の生徒は 2 人の親を取得し、一部の生徒は 1 人の親を取得するため、私のデータは良好に見えます。

問題

Global.asax.cs にこれらの AutoMappers があります。

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>();

しかし、インポートを開始すると、db.SaveChanges(). 親モデルの重複する ForeignKey について文句を言います。だから私は考えています:

これは多対多の関係であるため、John Doe の妹である Jane Doe が同じ Dad Doe を挿入しようとすると、クラッシュします。

では、マップされたビジネス オブジェクトのセット全体が各エンティティへの参照を 1 つだけ持つようにするにはどうすればよいでしょうか。重複したパパとママを削除するには?私はおそらく、件名についてもこれを行いたいと考えています。

4

3 に答える 3

1

本当の問題はマッピングにあります。Mapper は同じ親を 2 回追加するため、その新しいエンティティは追加済み状態になります。その後、dbContext はそれを新しいレコードのように扱い、挿入を試みます。次の 3 つのオプションが表示されます。

  1. StudentDTO.ParentDTO を StudentDTO.IDParentDTO に置き換えます
  2. StudentDTO.IDParentDTO を追加し、マッピングで StudentDTO.ParentDTO を無視します
  3. マッピングで遊ぶ。たくさんの機能がありますが、それらを見つける必要があります。この質問をチェック
于 2013-05-21T15:17:09.277 に答える
0

ユーザー定義のテーブル タイプでこのエラーを受け取りました。データの関係を構築するとき、同じレコードを複数回プルすることがあります。必要に応じて、 PRIMARY KEYを宣言するときにIGNORE_DUP_KEYをオン にします。

Microsoft index_option (w / IGNORE_DUP_KEY)

例:

CREATE TYPE [dbo].[udt_Promotion] AS TABLE(
  [PromotionID] [int] NOT NULL PRIMARY KEY CLUSTERED WITH (IGNORE_DUP_KEY = ON),
  ...
)
于 2013-12-27T19:02:26.877 に答える