实体框架代码优先6.1.3 - 迁移会创建错误的外键

时间:2016-10-27 00:27:09

标签: entity-framework entity-framework-6

有两个独立的实体(源和目标)和一个将一个Source连接到一个Target的可选实体。除了在Source和Target实体上使用SourceTargetBinding类型的导航属性之外,该关系应该如下所示:

enter image description here

模特:

public class Source
{
    [Key]
    public long SourceID { get; set; }
    public string Name { get; set; }
    public SourceTargetBinding TargetBinding { get; set; }
}

public class Target
{
    [Key]
    public long TargetID { get; set; }
    public string Name { get; set; }
    public SourceTargetBinding SourceBinding { get; set; }
}
public class SourceTargetBinding
{
    [Key]
    public long SourceID { get; set; }
    public long TargetID { get; set; }
    public Source Source { get; set; }
    public Target Target { get; set; }
}

背景信息:

public class MyContext : DbContext
{
    public MyContext() : base("name=MyContextData")
    {
    }

    public DbSet<Source> Sources { get; set; }
    public DbSet<Target> Targets { get; set; }
    public DbSet<SourceTargetBinding> SourceTargetBindings { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<SourceTargetBinding>()
            .HasKey(b => b.SourceID);
        modelBuilder.Entity<SourceTargetBinding>()
            .HasRequired(b => b.Source)
            .WithOptional(s => s.TargetBinding);
        modelBuilder.Entity<SourceTargetBinding>()
            .HasRequired(b => b.Target)
            .WithOptional(t => t.SourceBinding);

        base.OnModelCreating(modelBuilder);
    }
}

这是生成的迁移。请注意从SourceTargetBindings到Targets的错误外键:

    public override void Up()
    {
        CreateTable(
            "dbo.Sources",
            c => new
                {
                    SourceID = c.Long(nullable: false, identity: true),
                    Name = c.String(),
                })
            .PrimaryKey(t => t.SourceID);

        CreateTable(
            "dbo.SourceTargetBindings",
            c => new
                {
                    SourceID = c.Long(nullable: false),
                    TargetID = c.Long(nullable: false),
                })
            .PrimaryKey(t => t.SourceID)
            .ForeignKey("dbo.Sources", t => t.SourceID)
            .ForeignKey("dbo.Targets", t => t.SourceID)
            .Index(t => t.SourceID);

        CreateTable(
            "dbo.Targets",
            c => new
                {
                    TargetID = c.Long(nullable: false, identity: true),
                    Name = c.String(),
                })
            .PrimaryKey(t => t.TargetID);
    }

如果我从source(Source.TargetBinding)和target(Target.SourceBinding)中删除导航属性,则EF会创建正确的外键。但我需要那些导航属性。

我不知道自己做错了什么。在我看来,TargetID将是FK明显的依赖列。如果有流畅的API语义来指定依赖列,我无法找到它。 Map()不起作用,因为TargetID作为模型中的属性公开(我也需要)。

我还尝试了反向语义 - 在源和目标上使用HasOptional-WithRequired而不是在连接实体上使用HasRequired-WithOptional。我得到了同样不正确的结果。

1 个答案:

答案 0 :(得分:0)

问题在于您的联结表。您需要定义composite key,因为您可能有许多SourceId,因此SourceId + TargetId将是唯一的,因此是关键:

public class SourceTargetBinding
{
    [Key, Column(Order = 0)]
    public long SourceID { get; set; }
    [Key, Column(Order = 1)]
    public long TargetID { get; set; }
    public Source Source { get; set; }
    public Target Target { get; set; }
}

您的流利代码变为:

modelBuilder.Entity<SourceTargetBinding>().HasKey(e => new { e.SourceId, e.TargetId });

编辑:如果你想要SourceTargetBinding 0:1,那么它需要用key和FK标记:

public class SourceTargetBinding
{
    [Key, ForeignKey("Source")]
    public long SourceID { get; set; }
    public long TargetID { get; set; }
    public Source Source { get; set; }
    public Target Target { get; set; }
}

编辑2:

    modelBuilder.Entity<SourceTargetBinding>()
        .HasRequired(b => b.Target)
        .WithOptional(t => t.SourceBinding)
        .Map(m => m.MapKey("TargetId"));