在Entity Framework Code First中定义分层数据的关系

时间:2011-10-28 21:27:12

标签: entity-framework-4.1 ef-code-first hierarchical-data

我为其中一个表定义了层次关系,其中关系存储在单独的连接表中。连接表还包含有关关系类型的信息。 (简化)架构如下所示:

Bills
   ID int IDENTITY(1,1) NOT NULL (PK)
   Code varchar(5) NOT NULL
   Number varchar(5) NOT NULL
   ...

BillRelations
   ID int IDENTITY(1,1) NOT NULL (PK)
   BillID int NOT NULL
   RelatedBillID int NOT NULL
   Relationship int NOT NULL
   ...

我在BILL表中的BillID和RelatedBillID上定义了FK关系。

我正在尝试在Entity Framework Code First中映射它,但收效甚微。我的课程如下所示。忽略RelationshipWrapper内容,它是围绕名为enum的{​​{1}}的包装类,对应于“关系”列中的值。

Relationship

我已尝试过各种各样的版本,都是通过DbContext上的模型构建器和数据注释使用显式定义的关系,但上面定义了我正在寻找的内容。我正在使用集合,因为外键属性不是主键。

使用此设置我收到错误:public class Bill { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int ID { get; set; } [Required] [StringLength(5)] public string Code { get; set; } [Required] [StringLength(5)] public string Number { get; set; } public virtual ICollection<BillRelation> RelatedBills { get; set; } ... } public class BillRelation { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int ID { get; set; } public long BillID { get; set; } [ForeignKey("BillID")] public virtual Bill Bill { get; set; } public long RelatedBillID { get; set; } [ForeignKey("RelatedBillID")] public virtual Bill RelatedBill { get; set; } public RelationshipWrapper Relationship { get; set; } ... } (或类似的东西)。

如果我使用以下内容获得了ModelBuilder路由,则会收到“数据库中不存在BillRelations表”的错误。

Bill_ID column is not defined

我能够通过定义关系的一半来实现它,Bills - &gt; BillRelations,然后在我的存储库中使用Join在BillRelations类中为Bill的RelatedBills集合中的每个相关账单填写modelBuilder.Entity<Bill>().HasMany( b => b.RelatedBills ) .WithRequired( r => r.Bill ) .Map( m => m.MapKey( "BillID" ).ToTable( "BillRelations" ) ); modelBuilder.Entity<BillRelation>().HasRequired( r => r.RelatedBill ) .WithRequiredDependent() .Map( m => m.MapKey( "RemoteBillID" ).ToTable( "BillRelations" ) ); RelatedBill属性。如果我能帮忙,我宁愿不这样做。

我想到的唯一其他解决方案是在一个单独的表中建模每个关系(有4种类型),并通过连接表为4种关系类型中的每一种使用标准的Bill&lt; - &gt; Bill映射 - - 如果我能避免的话,我宁愿不这样做。

任何人都可以看到我做错了什么或告诉我,如果我想做什么甚至可以在EF Code First 4.1中使用?

1 个答案:

答案 0 :(得分:2)

只是一些想法:

  • 使用数据注释的映射不起作用,因为EF Code First约定无法识别哪些导航属性属于一起。显然,您希望将Bill.RelatedBillsBillRelation.Bill相关联。但是因为有第二个导航属性BillRelation.RelatedBill引用Bill实体,AssociationInverseDiscoveryConvention无法应用于识别正确的关系。只有在实体上只有一对导航属性时,此约定才有效。因此,EF实际上假设三种关系,每种关系在模型中只有一个暴露的结束。根据EF默认命名约定,Bill.RelatedBills所属的关系假定另一方没有公开的外键 - 带有下划线的Bill_ID。它在数据库中不存在,因此例外。

  • 在您的Fluent API映射中,我只想尝试删除...ToTable(...)。我认为没有必要,因为映射知道外键属于哪个表。这可能会修复第二个错误(“表...不存在......”)

  • 您的第二个映射 - 一对一关系 - 可能无法按预期工作,因为一对一关系“通常”映射到数据库中表之间的共享主键关联。 (我不确定EF是否真的需要共享主键。)因为您的BillId列似乎是同时不是主键的外键,我会尝试将关系映射为一个-太多。此外,因为您的外键列在模型中公开为属性,所以您应该使用HasForeignKey而不是MapKey

    modelBuilder.Entity<Bill>()
        .HasMany( b => b.RelatedBills )
        .WithRequired( r => r.Bill )
        .HasForeignKey ( r => r.BillID );
    // turn off/on cascade delete by chaining
    //  .WillCascadeOnDelete(true/false)
    // here at the end
    
    modelBuilder.Entity<BillRelation>()
        .HasRequired( r => r.RelatedBill )
        .WithMany()
        .HasForeignKey ( r => r.RelatedBillID );
    // turn off/on cascade delete by chaining
    //  .WillCascadeOnDelete(true/false)
    // here at the end
    

修改

如果您将[InverseProperty]属性放在其中一个导航属性上,则可能不需要整个Fluent映射 - 例如在Bill类中:

[InverseProperty("Bill")]
public virtual ICollection<BillRelation> RelatedBills { get; set; }

在此属性中,您可以指定相关实体上关联导航属性的名称。这会将Bill.RelatedBillsBillRelation.Bill绑定到一对导航属性,这些导航属性是同一关联的末尾。我希望EF能用剩余的导航属性BillRelation.RelatedBill做正确的事情,即创建一对多的关系 - 我希望......如果默认的级联删除对你不起作用,你会被迫使用Fluent API,因为没有数据注释属性来配置级联删除。