简体   繁体   English

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

[英]Entity Framework Code First Foreign Key issue

It seems to me like the ForeignKey attribute is not working for me, but I guess I'm using it wrong ;) 在我看来, ForeignKey属性对我不起作用,但我想我使用它错了;)

It's easier to explain with code: 使用代码更容易解​​释:

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

When I try to fill these objects with the seed method, I'm getting this error: 当我尝试使用种子方法填充这些对象时,我收到此错误:

INSERT statement conflicted with the FOREIGN KEY constraint "FK_dbo.Skills_dbo.BaseCards_BaseCardId". INSERT语句与FOREIGN KEY约束“FK_dbo.Skills_dbo.BaseCards_BaseCardId”冲突。 The conflict occurred in database "Database", table "dbo.BaseCards", column 'Id'. 冲突发生在数据库“数据库”,表“dbo.BaseCards”,列“Id”中。

It seems to me like the ForeignKey in Skill tries to point at the Id column of BaseCards instead of the BaseCardId column, and I can't figure out why.. 在我看来,像SkillForeignKey试图指向BaseCardsId列而不是BaseCardId列,我无法弄清楚为什么..

If I try to remove the "normal" Id property of BaseCard , and set the BaseCardId as the PK (with attribute [Key] ), I get the following error: 如果我尝试删除BaseCard的“普通” Id属性, BaseCard设置为PK(具有属性[Key] ), BaseCardId出现以下错误:

Store update, insert, or delete statement affected an unexpected number of rows (0). 存储更新,插入或删除语句会影响意外的行数(0)。 Entities may have been modified or deleted since entities were loaded. 自实体加载后,实体可能已被修改或删除。 Refresh ObjectStateManager entries. 刷新ObjectStateManager条目。

Does anyone know how I can get this code to work so the property BaseCardId from the Skill class will point to the BaseCardId property of BaseCard , instead of apparently the Id property? 有谁知道我能得到这个代码的工作,所以物业BaseCardIdSkill类将指向BaseCardId财产BaseCard ,而不是明显的Id属性?

Thanks in advance! 提前致谢!

It is possible set by yourself the Id of BaseCard, but first you have to use Fluent Api to specify this and the one-to-many relationship. 您可以自己设置BaseCard的Id,但首先必须使用Fluent Api来指定这个和一对多的关系。 Also, at this way, you don't have to specify attributes in your model classes. 此外,通过这种方式,您不必在模型类中指定属性。 Your model classes would be like this: 你的模型类是这样的:

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

As you can see, I change the navigation properties as virtual.If you define your navigation property as virtual EF will at runtime create a new class (dynamic proxy) derived from your BaseCard class and use it instead (the same happens with Skill). 如您所见,我将导航属性更改为虚拟。如果您将导航属性定义为虚拟EF将在运行时创建一个从您的BaseCard类派生的新类(动态代理)并使用它(Skill也是如此)。 This new dynamically created class contains logic to load navigation property when accessed for the first time. 这个新动态创建的类包含第一次访问时加载导航属性的逻辑。 This feature is called lazy loading.It enables Entity Framework to avoid loading an entire tree of dependent objects which are not needed from the database. 此功能称为延迟加载。它使实体框架能够避免加载数据库中不需要的整个依赖对象树。 You can find more info about this subject in these posts: Why Navigation Properties are virtual by default in EF and Entity Framework 4.1 Virtual Properties . 您可以在以下帖子中找到有关此主题的更多信息: 为什么导航属性在EFEntity Framework 4.1虚拟属性中 默认为 虚拟

The other change that I proporse in your model is use ICollection<> type instead List<> in the Skills property. 我在您的模型中提出的另一个更改是在Skills属性中使用ICollection <>类型而不是List <>。 As you can see in this post , an Interface is good practice in this case. 正如您在本文中所看到 ,在这种情况下,接口是一种很好的做法。 It lets you later specify the implementation that you want (could be a List<>). 它允许您稍后指定所需的实现(可以是List <>)。

Now, back to your problem,in your Context class you need to override the OnModelCreating method to specify the relationship and the no autogenerate column for Id in the BaseCards table. 现在,回到您的问题,在您的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);
    }
}

With this configuration, you have to set always the Id of BaseCard objects with a different value. 使用此配置,您必须始终使用不同的值设置BaseCard对象的Id。

If you prefer use data annotations it is possible specify the same at this way: 如果您更喜欢使用数据注释,则可以通过以下方式指定相同的内容:

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

My recomendation is use Fluent API, it's more flexible and you don't have to touch your model classes. 我的推荐是使用Fluent API,它更灵活,您不必触摸您的模型类。 You can see some useful cosiderations in this post 你可以在这篇文章中看到一些有用的消息

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM