【问题标题】:Code first mapping for self-related xref table自相关外部参照表的代码优先映射
【发布时间】:2013-04-08 21:34:43
【问题描述】:

我已经阅读了 StackOverflow 上的几个线程,但无法弄清楚这一点。我希望有人可以提供一些建议。我有一些像这样的 POCO 类:

Person
{
  int PersonCode {get; set;}
  ...
  virtual List<PersonContact> {get; set;}
}

PersonContact
{
  int PersonPersonCode {get; set;}
  int ContactPersonCode {get; set;}
  int PersonContactTypeCode {get; set;}

  virtual PersonContactType {get; set;}
  virtual Person Person {get; set;}    // not sure I really need this one
  virtual Person Contact {get; set;}
}

每个 Person 记录将有零到多个 PersonContact 记录。每个 PersonContact 记录将一个 Person 记录链接到另一个 Person 记录,并使用 PersonContactTypeCode 指示两个 Person 记录之间的关系类型。

我需要能够对此进行映射,以便可以将 Person 记录导航到他的相关 PersonContact 记录。像这样的:

var john = new Person(...);
var david = new Person(...);
john.PersonContacts.Add(new PersonContact
  {
    Contact = david,
    PersonContactType = ... // manager
  });

然后

john.PersonContacts
  .Where(c => c.PersonContactType.PersonContactTypeCode == "manager")
  .FirstOrDefault();

会回来

david

我已经尝试了很多数据注释和 Fluent API 的组合,以至于我几乎记不起我从哪里开始了。我似乎对这个组合有最好的运气:

modelBuilder.Entity<Person>()
    .HasMany(entity => entity.PersonContacts)
        .WithRequired(person => person.Person)
        .HasForeignKey(xref => xref.PersonPersonCode)
        .WillCascadeOnDelete(false);

modelBuilder.Entity<Person>()
    .HasMany(entity => entity.PersonContacts)
        .WithRequired(xref => xref.Contact)
        .HasForeignKey(entity => entity.ContactPersonCode)
        .WillCascadeOnDelete(false);

但是,当我尝试向一个 Person 添加多个 PersonContact 时,我收到此错误:

Multiplicity constraint violated. The role 'Person_PersonContacts_Source' of the
relationship '...Entities.Person_PersonContacts' has multiplicity
1 or 0..1.

我真的很感谢任何帮助,我现在完全被难住了。顺便说一句,如有必要,我愿意更改这些 POCO。

【问题讨论】:

    标签: entity-framework code-first


    【解决方案1】:

    我猜这是因为您使用相同的导航属性链接到 PersonContact.PersonPersonContact.Contact

    假设:

    Person
    {
      int PersonCode {get; set;}
      ...
      virtual ICollection<PersonContact> PersonContacts {get; set;}
    }
    

    尝试类似:

    modelBuilder.Entity<PersonContact>()
        .HasRequired(x => x.Person)
            .WithMany(x => x.PersonContacts)
            .HasForeignKey(x => x.PersonPersonCode)
            .WillCascadeOnDelete(false);
    
    modelBuilder.Entity<PersonContact>()
        .HasRequired(x => x.Contact)
            .WithMany()
            .HasForeignKey(x => x.ContactPersonCode)
            .WillCascadeOnDelete(false);
    

    【讨论】:

    • 我明天回到办公室试试这个,我会告诉你它是如何运行的。
    • 我选择了这个解决方案的轻微变化。成功了,谢谢!
    • 细微的变化是什么?
    【解决方案2】:

    试试这个:

    public class Person
    {
       [Key]
       [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
       public int PersonId {get; set;}
       ...
       public virtual ICollection<PersonContact> PersonContacts {get; set;}
    }
    
    
    public class PersonContact
    {
       [Key]
       [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
       public int ContactId {get; set;}
    
       [ForeignKey("Person"), DatabaseGenerated(DatabaseGeneratedOption.None)]
       public int PersonId {get; set;}       
    
       public virtual Person Person {get; set;}         
    }
    

    我使用了Property Mapping 而不是Fluent Mapping,就像您在尝试中尝试的那样。 如果您有任何问题,请告诉我。至于你的两个实体之间的关系,这就是你所需要的。

    【讨论】:

    • 如何处理级联删除?
    • @kirsteng Entity Framework 确保Objects 是使用cascade delete 创建的。关注此事的链接stackoverflow.com/questions/5520418/…
    • 糟糕,我的意思是你如何关闭级联删除?
    • @kirsteng 将public int PersonId {get; set;} 更改为public int PersonId? {get; set;} 使外键允许null。从未测试过,但在逻辑上是有道理的。
    • 是的,但我认为这不会影响级联删除
    【解决方案3】:

    我设法使用类似这样的东西来实现类似的工作

    在我的域类中

    public class PersonContact
    {
        public int Id { get; set; }
    
        [Required]
        public virtual Person ContactPerson { get; set; }
    
        [Required]
        public virtual Person Person { get; set; }
    
    [Required]
    public int ContactType { get; set; }
    
    }
    
    public class Person
    {
    int Id { get; set; }
    
        private readonly  List<PersonContact> _contacts = new  List<Contact>();
    
        public virtual  List<PersonContact> Contacts
        {
            get
            {
                return this._contacts;
            }
        }
    }
    

    在我的上下文中

     public DbSet<Person> People { get; set; }
     public DbSet<PersonContact> Contacts { get; set; }
    

    我在迁移中进行了此更改,并且必须编辑生成的创建表代码以设置 在 PersonContact 表中,将人员表的两个外键级联删除为 false。

    我在 PersonContact 表中得到一个额外的 Person_Id1 列。它似乎填充了与 Person_Id 相同的数据。当我创建绑定源时,EF 似乎需要这 - 因为没有它我会得到错误。

    我不会将显式外键放入,让迁移创建它们。

    【讨论】:

    • 自从您要求改进后,我对您的回答进行了一些更改。你得到an extra Person_Id1 column in the PersonContact table 的原因是你没有在你的POCO 类中映射PersonId 外键。下面的方法也可以解决这个问题
    • 谢谢,我通常似乎可以不用在 POCO 类中明确定义 FK。我应该总是明确地定义它们还是只在这样的场景中定义它们?
    • 你要么做属性映射,要么做流利的映射。在这个问题中,他尝试了流利的映射,在我的回答中,我做了我更喜欢的属性映射,在我的情况下更容易理解。注意:您的回答中的编辑未获批准,但您可以参考我的回答
    • 我看不到在哪里批准您的编辑 - 您删除了吗?我实际上在课堂上使用的是 bindingList 而不是列表。这可能是额外字段的原因吗?我尝试添加 FK - 但生成了额外的字段。如果我使用您的代码,则不会生成额外的字段。
    • Mhmmm 看起来 mod 不允许我进行编辑,请查看我对这个问题的回答。
    猜你喜欢
    • 2023-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-27
    • 2014-08-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多