How can I define a one-to-many connection with EF Code First?

.net c# entity-framework

Question

I'm trying to define a simple one-to-many relationship between two poco's, using the Entity Framework fluent API.

~ Team ~
public int TeamId { get; set; }
public ICollection<User> TeamMembers { get; set; } // All the team players. Two way nav.
public Player CreatedBy { get; set; } // Which player created this team. One way navigation.
                                      // Optional. Not all players create a team.

~ Player ~
public int PlayerId { get; set; }
public Team Team { get; set; } // The other side of the TeamMembers navigation.

NOTES:

  • A player doesn't have to be in a team. (unassigned/dropped players).
  • A team might have no players (they have all left/quit).

The closest I thought I got was something like this...

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Team>()
        .HasOptional(x => x.TeamMembers)
        .WithMany()
        .WillCascadeOnDelete(false);
}

... Which doesn't work .. and i'm not sure how to define the other navs also.

Can anyone help, please?

1
7
3/20/2011 2:20:13 PM

Accepted Answer

I think this object model is what you are looking for:

public class Team
{    
    public int TeamId { get; set; }
    public ICollection<Player> TeamMembers { get; set; } 
    public Player CreatedBy { get; set; } 
}

public class Player
{
    public int PlayerId { get; set; }
    public Team Team { get; set; } 
}       

public class Context : DbContext
{
    public DbSet<Player> Players { get; set; }
    public DbSet<Team> Teams { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Player>()
                    .HasOptional(p => p.Team)
                    .WithMany(t => t.TeamMembers)
                    .Map(c => c.MapKey("TeamId"));

        // Or alternatively you could start from the Team object:
        modelBuilder.Entity<Team>()
                    .HasMany(t => t.TeamMembers)
                    .WithOptional(p => p.Team)
                    .Map(c => c.MapKey("TeamId"));
    }
}

BTW, the following fluent API code that you are using is not correct:

...HasOptional(x => x.TeamMembers)

Because TeamMembers is a collection and cannot be used by HasOptional method which always has to be invoked with a single object.

Update - HasRequired vs. HasOptional:

While they both set up an association, they deliver slightly different results and have different requirements:

  • If it's a FK association (the FK property is exposed on the dependent object) then it must be a nullable type when using HasOptional and non-nullable type when using HasRequired or Code First will throw.

  • Code First will automatically switch cascade deletes on when using HasRequired method.

  • The other difference is the EF runtime behavior when it comes to deletion. Consider a scenario where we want to delete the principal object (e.g. Team) while it has a dependent object (e.g. Player) and cascade delete is switched off. With HasOptional EF runtime will silently update the dependent FK column to null while with HasRequired EF will throw and ask you to either explicitly remove the dependent object or relate it to another principal object (If you want to try this you should be aware that in both cases both principal and dependent objects must be already loaded in context so that EF will have a track of them).

12
3/22/2011 1:47:08 PM

Popular Answer

I've been able to get this to work automatically simply by doing something like this:

 public class Team {
    public int TeamId { get; set; }
    ...
    public virtual ICollection<Player> Players { get; set; }
}

But you'll have to be more specifical about what exactly you mean when you say "doesn't work". What doesn't work, exactly? Are you getting an error message? If so, what is it? Is the Team property of the Player object always returning null?



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow