1

I want to create class that contains multiple objects as a relation to another table.

For example, if I wanted to implement a team of three players using different objects instead of a collection, my code would've looked like this:

public class Team
{
    [Key]
    public int TeamId { get; set; }

    public string Name { get; set; }

    [ForeignKey("Goalkeeper")]
    [Required]
    public int GoalkeeperId { get; set; }
    public virtual Player Goalkeeper { get; set; }

    [ForeignKey("Defender")]
    [Required]
    public int DefenderId { get; set; }
    public virtual Player Defender { get; set; }

    [ForeignKey("Striker")]
    [Required]
    public int StrikerId { get; set; }
    public virtual Player Striker { get; set; }
}

public class Player
{
    [Key]
    public int PlayerId { get; set; }

    public string Name { get; set; }

    [ForeignKey("Team")]
    public int TeamId { get; set; }
    public virtual Team Team { get; set; }
}

Trying to create a migration based on this code I'm getting an error:

Unable to determine the relationship represented by navigation property 'Player.Team' of type 'Team'. Either manually configure the relationship, or ignore this property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.

What am I missing? Is this even a correct approach to implement this?

Kevin
  • 29
  • 5
  • https://stackoverflow.com/questions/5559043/entity-framework-code-first-two-foreign-keys-from-same-table have a look at this thread may be this can help. – Nadeem Jan 15 '21 at 11:49
  • https://stackoverflow.com/questions/10446641/in-sql-is-it-ok-for-two-tables-to-refer-to-each-other it's not ok for two tables to refer each other, where would you be inserting the records first. – Nadeem Jan 15 '21 at 11:56
  • @Nadeem, I've seen this one. I think this example is a bit different, as Team has a collection of Matches. Also I've tried most of solutions from there. If it's about tables refering each other, I would start with Player, Team object is virtual there and it can be created without it. – Kevin Jan 15 '21 at 11:59
  • Instead of two class use three classes. In Player class add one column named PlayerType where in you would define whether the player is goal keeper, defender or striker. Team class would have Team Id and Team Name. Add a third class e.g TeamDetails it would have team id and player id. – Nadeem Jan 15 '21 at 12:00
  • In this solution I'm not limiting players number to 3. I want to have exactly 3 players, a goalkeeper, a defender and a striker, having a direct access to Team.Defender etc.This solution does not match my critiera. – Kevin Jan 15 '21 at 12:03
  • In team details you can add as many player you want for a team. – Nadeem Jan 15 '21 at 12:09
  • Exactly. And that's not what I want to solve this issue. Also I don't have access to my goalkeeper/defender/striker directly what forces me to use additional queries to get them every time I want to use them. I think that there has to be better solution for that. – Kevin Jan 15 '21 at 12:12
  • Take a look on this: https://forums.asp.net/t/2135907.aspx?Multiple+Relationships+Foreign+Keys+Between+Two+Tables+in+Classes+ – Miamy Jan 15 '21 at 13:35

1 Answers1

1

I think that the problem is that EF Core cannot automatically detect what's the correct way of configure the mapping between the entities.

From the docs:

When there are multiple navigation properties defined between two types (that is, more than just one pair of navigations that point to each other) the relationships represented by the navigation properties are ambiguous. You will need to manually configure them to resolve the ambiguity.

Therefore, you should try to configure it manually in your DbContext. It would be something ish like this, but I might have a mistake somewhere since I didn't test it.

public class YourDatabaseContext : SqlContext
    {
        ...

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

            modelBuilder.Entity<Team>()
                .HasOne(x => x.Goalkeeper )
                .WithOne()
                .HasForeignKey<Team>(t => t.GoalkeeperId);

            modelBuilder.Entity<Team>()
                .HasOne(x => x.Defender)
                .WithOne()
                .HasForeignKey<Team>(t => t.DefenderId);

            modelBuilder.Entity<Team>()
                .HasOne(x => x.Striker)
                .WithOne()
                .HasForeignKey<Team>(t => t.StrikerId);
        }
    }

PD: I usually prefer this method of configuring the relationships with the FluentAPI instead of polluting the model with Attributes. To further organize the code, you can create a class extending IEntityTypeConfiguration<SomeEntity> per entity where you put the configuration code, and then in your DbContext.OnModelCreating you can add this line to scan your assembly an add all configurations:

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

            modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());
        }
dglozano
  • 6,369
  • 2
  • 19
  • 38
  • Wow this one works! Thanks. To be honest I still don't get why it doesn't work when I'm clearly associating Id with a virtual object, I'm doing exactly the same in a OnModelCreating code.. And thanks for those extra tips ;) – Kevin Jan 18 '21 at 18:09
  • @Kevin to be honest, I am not sure either what's the exact technical reason why EF find this ambiguous, glad to hear it helped :) – dglozano Jan 18 '21 at 18:34