Microsoft.EntityFrameworkCore.Model.Validation[10625] The foreign key property ‘ShortlistItem.ShortlistId1’ was created in shadow state because a conflicting property with the simple name ‘ShortlistId’ exists in the entity type

I have come across this issue many times, and never really got my head around it. I happened to me again yesterday, and as I finally worked out the reason for the issue, I decided to note it down, so next time it happens I’ll know what to do!

Some people have a blog so they spread their knowledge with others, some to show how clever they are.

By contrast, I have a blog because I’m so forgetful that if I don’t note down things that I find, I’ll never remember them, and will have to go through the whole learning curve again! If anyone else finds my blog useful, well that’s just a bonus!

I am working on an application that has shortlists, which are the equivalent of shopping baskets, but for data, rather than physical items. I have two (simplified) classes as follows…

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

  public virtual ObservableCollection<ShortlistItem> Items { get; set; } = [];
}

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

  public int ShortlistId { get; set; }
  public virtual Shortlist Shortlist { get; set; } = null!;

  public int DataId { get; set; }
  public virtual Data Data { get; set; } = null!;
}

When I added a migration for these, I got an error which I’ve now forgotten! It’s been a long time since yesterday 😎. The basic point of the error was that I needed to give EF Core some help in working out the relations.

No problem, I added the following to my context…

  protected override void OnModelCreating(ModelBuilder builder) {
    base.OnModelCreating(builder);

    builder.Entity<Shortlist>()
      .HasMany<ShortlistItem>()
      .WithOne(pi => pi.Shortlist)
      .HasForeignKey(si => si.ShortlistId)
      .IsRequired()
      .OnDelete(DeleteBehavior.NoAction);
  }

That allowed me to create the migration, but gave a warning…

Microsoft.EntityFrameworkCore.Model.Validation[10625]

The foreign key property ‘ShortlistItem.ShortlistId1’ was created in shadow state because a conflicting property with the simple name ‘ShortlistId’ exists in the entity type, but is either not mapped, is already used for another relationship, or is incompatible with the associated primary key type.

Looking at the migration code, I could see that it had indeed added an extra foreign key with the name ShortlistId1.

To cut a long story short (partly because I can’t remember all the twists and turns, and partly because anyone reading this, including the future me will only be interested in a fix), it turned out that Microsoft did something that I would consider very poor design.

OK, so I am the first to admit that there are some very clever programmers at Microsoft (I know, my brother used to be one, and he’s clever 😄), but sometimes you do have to wonder at the decisions they make.

As I like generics, specifically the concise feel they give to the code, I had used the generic form of the HasMany method. I had blindly (and incorrectly as it turned out) assumed that the generic and non-generic forms did the same thing. Shows where assumptions can lead you.

Apparently the non-generic form of this method…

HasMany(s => s.Items)

…says to EF Core, "Hey, there is already a navigation property in my model, and I want you to use that when you create the migration."

By contrast, the generic form of this method…

HasMany<ShortlistItem>()

…says to EF Core, "Hey, please create a navigation property for me. However, as I already have a property in there named ShortlistId, you’ll need to create another for your use." EF Core duly does so, and names it ShortlistId1.

To come back to my comment above, I would consider it poor design to have two methods with the same name, but doing something subtly but very significantly different. I’m sure the programmers who designed this had very good reasons for doing so, but judging by the number of questions you see on the topic, I think many people outside of Microsoft might politely disagree!

Be First to Comment

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.