实体框架代码第一个一对多关系映射与链接表

时间:2011-08-15 12:18:45

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

当两个表之间存在链接(连接)表时,我对两个表之间的一对多关系有疑问。

示例表:

ChildTable:
ID int NOT NULL PK
Relation int NOT NULL

ParentTable:
ID int NOT NULL PK
Name nvarchar(50) NOT NULL

ParentChildren:
ParentTable_ID int NOT NULL PFK
ChildTable_ID int NOT NULL PFK

实体:

public class ChildTable
{
    public ChildTable()
    {
        this.ParentTables = new List<ParentTable>();
    }

    public int ID { get; set; }       
    public int Relation { get; set; }
    public virtual ICollection<ParentTable> ParentTables { get; set; }
}

public class ParentTable
{
    public ParentTable()
    {
        this.ChildTables = new List<ChildTable>();
    }

    public int ID { get; set; }   
    public string Name { get; set; }
    public virtual ICollection<ChildTable> ChildTables { get; set; }
}

映射:

public class ChildTableMap : EntityTypeConfiguration<ChildTable>
{
    public ChildTableMap()
    {
        // Primary Key
        this.HasKey(t => t.ID);

        // Properties
        // Table & Column Mappings
        this.ToTable("ChildTable");
        this.Property(t => t.ID).HasColumnName("ID");
        this.Property(t => t.Relation).HasColumnName("Relation");
    }
}
public class ParentTableMap : EntityTypeConfiguration<ParentTable>
{
    public ParentTableMap()
    {
        // Primary Key
        this.HasKey(t => t.ID);

        // Properties
        this.Property(t => t.Name)
            .IsRequired()
            .HasMaxLength(50);

        // Table & Column Mappings
        this.ToTable("ParentTable");
        this.Property(t => t.ID).HasColumnName("ID");
        this.Property(t => t.Name).HasColumnName("Name");

        // Relationships
        this.HasMany(t => t.ChildTables)
            .WithMany(t => t.ParentTables)
            .Map(m =>
                {
                    m.ToTable("ParentChildren");
                    m.MapLeftKey("ParentTable_ID");
                    m.MapRightKey("ChildTable_ID");
                });

    }
}

上下文:

public class TestContext : DbContext
{
    static TestContext()
    { 
        Database.SetInitializer<TestContext>(null);
    }

    public DbSet<ChildTable> ChildTables { get; set; }
    public DbSet<ParentTable> ParentTables { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Remove<IncludeMetadataConvention>();
        modelBuilder.Configurations.Add(new ChildTableMap());
        modelBuilder.Configurations.Add(new ParentTableMap());
    }
}

添加子级和父级的代码:

using (var context = new TestContext())
        {
                var parent = new ParentTable { Name = "Mother Goose" };                   
                var child = new ChildTable { Relation = 1 };                   
                context.ParentTables.Add(parent);
                context.ChildTables.Add(child);
                parent.ChildTables.Add(child);
                context.SaveChanges();
        }

一切都按预期工作,但我真的只有一个父母和很多孩子。

如何进行包含写入链接表的映射(更改后的ChildTable类没有使用ParentTable的ICollection)?

1 个答案:

答案 0 :(得分:7)

无法定义此类映射。连接表仅适用于多对多关系。您必须记住,您的数据库架构(您无法更改,正如您在问题的评论中所述)已经定义了多对多关系。例如,这两个条目......

ParentTable_ID:  1    2
ChildTable_ID:   1    1

...是ParentChildren表中的有效条目,它们不违反PK约束,并且他们说孩子1有两个父母1和2。

如果您的业务逻辑仅允许子级只有一个父级,则数据库架构的建模错误。你不能用EF修复这个漏洞。

可能的解决方法可能是在代码中的业务逻辑中确保ParentTables实体的ChildTable集合永远不会有多个元素,可能是通过引入未映射的帮助程序属性:< / p>

public class ChildTable
{
    public ChildTable()
    {
        this.ParentTables = new List<ParentTable>();
    }

    public int ID { get; set; }       
    public int Relation { get; set; }
    public virtual ICollection<ParentTable> ParentTables { get; set; }

    [NotMapped] // or use .Ignore(c => c.ParentTable) in Fluent API
    public ParentTable ParentTable
    {
        get
        {
            return ParentTables.SingleOrDefault();
            // let is crash if there is more than one element in the list
        }

        set
        {
            var oldParent = ParentTables.SingleOrDefault();
            if (oldParent != value)
            {
                ParentTables.Clear();
                if (value != null)
                    ParentTables.Add(value);
            }
        }
    }
}

如果您只使用ParentTable属性,那只会有一点帮助。您必须避免使用该集合并添加多个父级。你不能使它private,因为它必须被映射。在这段关系的另一边也不安全。您可以编写:parent1.ChildTables.Add(child1); parent2.ChildTables.Add(child1);,这将导致数据库中child1的两个父项。您可以通过编写帮助方法来捕获这一点,该方法用于在ParentTable实体上添加子项,以检查添加的子项是否还没有this父项以外的父项。