Entity Framework Object Code First Foreign Key

It seems to me that the ForeignKey attribute ForeignKey not work for me, but I assume that I am using it incorrectly;)

This is easier to explain with the 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 populate these objects with the seed method, I get this error:

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

It seems to me that the ForeignKey in Skill trying to specify the Id BaseCards column instead of the BaseCardId column, and I cannot understand why ..

If I try to remove the "normal" Id BaseCard and set BaseCardId to PK (with the [Key] attribute), I get the following error:

A statement to update, insert, or delete affected an unexpected number of rows (0). Objects can be modified or deleted as objects are loaded. Update ObjectStateManager Records.

Does anyone know how I can make this code work, so the BaseCardId property from the Skill class will point to the BaseCardId BaseCard property instead of, apparently, the Id property?

Thanks in advance!

+5
source share
1 answer

You can set the BaseCard identifier itself, but first you must use Fluent Api to indicate this and one-way - a lot of relationships. In addition, you do not need to specify attributes in your model classes. Your model classes will be as follows:

 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 am changing the navigation properties as virtual. If you define your navigation property as virtual EF at run time, create a new class (dynamic proxy) derived from your BaseCard class and use it instead (the same thing happens with Skill). This new dynamically created class contains the logic for loading the navigation property on first access. This function is called lazy loading. It allows the Entity Framework to avoid loading the entire tree of dependent objects that are not needed from the database. You can find more information about this topic in these posts: Why are the default navigation properties virtual in EF and Entity Framework 4.1 Virtual Properties .

Another change I'm using in your model is to use ICollection <> type instead of List <> in the Skills property. As you can see in this post, the interface is good practice in this case. It allows you to later specify the implementation you want (maybe List <>).

Now, back to your problem, in your Context class, you need to override the OnModelCreating method to specify the relationship and column no autogenerate for Id in the BaseCards table.

 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 should always set BaseCard object identifiers with a different value.

If you prefer to use data annotations, you can specify the same thing:

 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 recommendation uses the Fluent API, it is more flexible, and you do not need to touch your model classes. In this post you can see useful tips.

+7
source

Source: https://habr.com/ru/post/1209952/


All Articles