3

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?

8
  • stackoverflow.com/questions/5559043/… have a look at this thread may be this can help. Commented Jan 15, 2021 at 11:49
  • stackoverflow.com/questions/10446641/… it's not ok for two tables to refer each other, where would you be inserting the records first. Commented Jan 15, 2021 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. Commented Jan 15, 2021 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. Commented Jan 15, 2021 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. Commented Jan 15, 2021 at 12:03

1 Answer 1

3

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());
        }
Sign up to request clarification or add additional context in comments.

2 Comments

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 to be honest, I am not sure either what's the exact technical reason why EF find this ambiguous, glad to hear it helped :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.