实体框架代码第一外键问题

时间:2014-12-27 11:39:20

标签: c# entity-framework ef-code-first foreign-keys data-annotations

在我看来,ForeignKey属性对我来说不起作用,但我猜我错了;)

用代码解释更容易:

public class BaseCard
{
    public int Id {get ; set; }
    public int BaseCardId { get; set; }

    public List<Skill> Skills { get; set; }
}

public class Skill
{
    public int Id { get; set; }

    public int BaseCardId { get; set; }
    [ForeignKey("BaseCardId")]
    public BaseCard BaseCard { get; set; }
}

当我尝试使用种子方法填充这些对象时,我收到此错误:

  

INSERT语句与FOREIGN KEY约束“FK_dbo.Skills_dbo.BaseCards_BaseCardId”冲突。冲突发生在数据库“数据库”,表“dbo.BaseCards”,列“Id”。

在我看来,ForeignKey中的Skill试图指向Id的{​​{1}}列,而不是BaseCards列,我可以弄清楚为什么......

如果我尝试删除BaseCardId的“普通”Id属性,并将BaseCard设置为PK(带有属性BaseCardId),我会得到以下内容错误:

  

存储更新,插入或删除语句会影响意外的行数(0)。自实体加载后,实体可能已被修改或删除。刷新ObjectStateManager条目。

有没有人知道如何让这段代码生效,因此[Key]类中的属性BaseCardId将指向Skill的{​​{1}}属性,而不是显而易见的BaseCardId属性?

提前致谢!

1 个答案:

答案 0 :(得分:8)

可以自己设置BaseCard的Id,但首先必须使用Fluent Api来指定这个和一对多的关系。此外,通过这种方式,您不必在模型类中指定属性。你的模型类是这样的:

public class BaseCard
{
  public int Id {get ; set; }

  public virtual ICollection<Skill> Skills { get; set; }
}

public class Skill
{
  public int Id { get; set; }

  public int BaseCardId { get; set; }
  public virtual BaseCard BaseCard { get; set; }
}

正如您所看到的,我将导航属性更改为虚拟。如果您将导航属性定义为虚拟EF将在运行时创建一个从您的BaseCard类派生的新类(动态代理)并使用它(相同的情况发生在技能)。这个新动态创建的类包含第一次访问时加载导航属性的逻辑。此功能称为延迟加载。它使实体框架能够避免加载数据库中不需要的整个依赖对象树。您可以在以下帖子中找到有关此主题的更多信息:Why Navigation Properties are virtual by default in EFEntity Framework 4.1 Virtual Properties

我在你的模型中提出的另一个变化是使用ICollection&lt;&gt;而不是列出&lt;&gt;在技​​能属性。正如您在此post中所看到的,在这种情况下,接口是一种很好的做法。它允许您稍后指定所需的实现(可以是List&lt;&gt;)。

现在,回到你的问题,在你的Context类中,你需要覆盖OnModelCreating方法来为BaseCards表中的Id指定关系和no autogenerate列。

public class YourContext : DbContext
{
    public DbSet<BaseCard> BaseCards { get; set; }

    public DbSet<Skill> Skill { get; set; }

    //...

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        // Configure the primary key for BaseCard
        modelBuilder.Entity<BaseCard>().HasKey(t => t.Id);
        //specify no autogenerate the Id Column
        modelBuilder.Entity<BaseCard>().Property(b => b.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);

        //one-to-many relationship 
        modelBuilder.Entity<Skill>().HasRequired(c => c.BaseCard)
                .WithMany(s => s.Skills)
                .HasForeignKey(c => c.BaseCardId);
    }
}

使用此配置,您必须始终使用不同的值设置BaseCard对象的Id。

如果您更喜欢使用数据注释,可以通过这种方式指定相同的内容:

public class BaseCard
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int Id { get; set; }
    public virtual ICollection<Skill> Skills { get; set; }
}

public class Skill
{
    [Key]
    public int Id { get; set; }

    public int BaseCardId { get; set; }

   [ForeignKey("BaseCardId")]
    public virtual BaseCard BaseCard { get; set; }
}

我的推荐是使用Fluent API,它更灵活,您不必触摸您的模型类。您可以在此post

中看到一些有用的消息