c# - Entity Framework 代码优先外键问题

标签 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; }
}

当我尝试使用 seed 方法填充这些对象时,出现此错误:

INSERT statement conflicted with the FOREIGN KEY constraint "FK_dbo.Skills_dbo.BaseCards_BaseCardId". The conflict occurred in database "Database", table "dbo.BaseCards", column 'Id'.

在我看来,Skill 中的 ForeignKey 试图指向 BaseCardsIdBaseCardId 列,我不明白为什么..

如果我尝试删除 BaseCard 的“正常”Id 属性,并将 BaseCardId 设置为 PK(具有属性 [Key]),我收到以下错误:

Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries.

有谁知道我如何让这段代码工作,以便 Skill 类的属性 BaseCardId 将指向 BaseCardId 属性BaseCard,显然不是 Id 属性?

提前致谢!

最佳答案

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 类的新类(动态代理)并改用它(与技能)。这个新的动态创建的类包含在第一次访问时加载导航属性的逻辑。此功能称为延迟加载。它使 Entity Framework 能够避免从数据库中加载不需要的依赖对象的整个树。您可以在这些帖子中找到有关此主题的更多信息:Why Navigation Properties are virtual by default in EFEntity Framework 4.1 Virtual Properties .

我在您的模型中提出的另一个更改是在 Skills 属性中使用 ICollection<> 类型而不是 List<>。正如您在此 post 中看到的那样,在这种情况下,接口(interface)是一种很好的做法。它允许您稍后指定所需的实现(可以是 List<>)。

现在,回到您的问题,在您的 Context 类中,您需要重写 OnModelCreating 方法以指定关系以及 BaseCards 表中 Id 的非自动生成列。

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 中看到一些有用的考虑

关于c# - Entity Framework 代码优先外键问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27666857/

相关文章:

C# 自定义 Setter 事件或继承的 Setter

c# - 如何使用字段名称的子部分通过表达式指定双顺序

c# - 如何跨多个帐户使用 Outlook 插件捕获已发送的项目?

entity-framework - EFCore 为简单的 LEFT OUTER 连接返回太多列

c# - MVC、 View 和 System.Data.Entity

entity-framework - 在对数据库进行逆向工程时改进导航属性名称

sql - EF Core 检查约束

c# - 当函数结果未分配给变量时显示警告

c# - Entity Framework 自定义数据类型映射

ef-code-first - EF 4.1搞砸了。 FK命名策略是否已更改?