实体框架代码优先 - 可选/必需的双向导航

时间:2015-06-20 19:07:15

标签: entity-framework ef-code-first foreign-keys entity-framework-6 foreign-key-relationship

我有一个类似于以下代码的第一个模型:

public class TestContext : DbContext
{
    public DbSet<Class1> Class1s { get; set; }
    public DbSet<Class2> Class2s { get; set; }
}

public class Class1
{
    public int Class1Id { get; set; }
}

public class Class2
{
    public int Class2Id { get; set; }

    public int Class1Id { get; set; }
    public Class1 Class1 { get; set; }
}

每个Class1对象都有零到多个Class2对象。每个Class2对象必须具有对Class1对象的引用。

我想要做的是从Class1到Class2的可选引用(跟踪与Class1对象关联的Class2对象之一),所以我添加了两个属性:

public class Class1
{
    public int Class1Id { get; set; }
    public int? Class2Id { get; set; }
    public Class2 Class2 { get; set; }
}

现在,当我运行Add-Migration时出现以下错误:

  

无法确定之间关联的主要结束   类型'ConsoleApplication15.Class2'和'ConsoleApplication15.Class1'。   必须明确配置此关联的主要结尾   使用关系流畅的API或数据注释。

其他StackOverflow答案表明解决方案是在依赖项

上指定外键字段
public class Class2
{
    [Key, ForeignKey("Class1")]
    public int Class2Id { get; set; }

    public int Class1Id { get; set; }
    public Class1 Class1 { get; set; }
}

我的添加迁移命令现在已完成,但创建的迁移如下:

public override void Up()
{
    DropForeignKey("dbo.Class2", "Class1Id", "dbo.Class1");
    DropIndex("dbo.Class2", new[] { "Class1Id" });
    DropColumn("dbo.Class2", "Class2Id");
    RenameColumn(table: "dbo.Class2", name: "Class1Id", newName: "Class2Id");
    DropPrimaryKey("dbo.Class2");
    AddColumn("dbo.Class1", "Class2Id", c => c.Int());
    AlterColumn("dbo.Class2", "Class2Id", c => c.Int(nullable: false));
    AddPrimaryKey("dbo.Class2", "Class2Id");
    CreateIndex("dbo.Class2", "Class2Id");
    AddForeignKey("dbo.Class2", "Class2Id", "dbo.Class1", "Class1Id");
}

这看起来不像我想要的......它正在删除Class2上的主键,它最后添加的外键是完全错误的。

如何描述我对EF的要求,以便生成正确的模型?

1 个答案:

答案 0 :(得分:1)

问题是您希望创建one-to-one relationship的惯例EF,如果是这样,您需要指定哪个实体是主体。

如果要在每个实体中创建具有不同PK的一对一关系,则无法在两个实体中声明FK属性。在配置one-to-one relationships时,实体框架要求依赖项的主键也是外键(您在最后一个场景中尝试执行此操作)。

一种解决方案可能是删除FK属性(Class1Id上的Class2Class2Id上的Class1)并覆盖上下文中的OnModelCreating方法以添加这个配置:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
     modelBuilder.Entity<Class2>()
                 .HasRequired(c2 => c2.Class1).
                 .WithOptional(c1=>c1.Class2);
}

如果您想使用FK属性,可以创建两个单独的关系,如下所示:

        modelBuilder.Entity<Class2>()
                    .HasRequired(s => s.Class1)
                    .WithMany()
                    .HasForeignKey(s => s.Class1Id);

        modelBuilder.Entity<Class1>()
                    .HasOptional(s => s.Class2)
                    .WithMany()
                    .HasForeignKey(s => s.Class2Id);

我认为最后一个解决方案更适合您尝试实现的方案。