TomSelleck
TomSelleck

Reputation: 6968

How to correctly initialize many to many relationship with code-first

I'm trying to use code-first to model my db.

What I'm trying to map here is a 0 -> * relationship for Users and Challenges.

E.g A user can have 0 or more challenges associated with them. A challenge can have 0 or more users associated with them.

This is the code I'm using, the problem is that the challenges table is generated with a UserAccount_ID property.

public class UserAccount
{
    public int Id { get; set; }
    public string Username { get; set; }

    public virtual List<Challenge> Challenges { get; set; }
}

public class Challenge
{
    public int Id { get; set; }
    public string ChallengeId {get; set;}
    public string Name { get; set; }
    public string Description { get; set; }

    public virtual List<Competitor> Competitors { get; set; }
}

public class Competitor
{
    public int Id { get; set; }
    public string Username { get; set; }
    public int Rank { get; set; }
}

And the incorrect property:

enter image description here

I've tried adding this to my context class but it doesn't have any effect:

protected override void OnModelCreating(DbModelBuilder mb)
{
    mb.Entity<UserAccount>()
        .HasMany(o1 => o1.Challenges)
        .WithOptional();

    base.OnModelCreating(mb);
}

Edit

Updated title and question - relationship is not zero to many but many to many.

Upvotes: 1

Views: 451

Answers (2)

Ehsan Ullah Nazir
Ehsan Ullah Nazir

Reputation: 1917

There are multiple Conventions for achieving Many to Many and One to Many relationship in entity framework. Will try to mention all .Hope these will be helpful.

UPDATE (as OP wants Many to Many): MANY to MANY Relationship

Convention 1 :

Collection navigation properties in both models

public class UserAccount
{
    public int Id { get; set; }
    public string Username { get; set; }

    public ICollection<Challenge> Challenges { get; set; }
}
    public class Challenge
{
   public int Id { get; set; }
   public string ChallengeId {get; set;}
   public string Name { get; set; }
   public string Description { get; set; }

   public ICollection<UserAccount> UserAccounts { get; set; }
}

Other ways for acheiving the same

mb.Entity<UserAccount>()
.HasMany(r => r.Challenges)
.WithMany()
.Map(m =>
    {
        m.MapLeftKey("UserAccountId");
        m.MapRightKey("ChallengeId");
        m.ToTable("UserAccountChallenge"); //Junction Table
    });

ONE to MANY Relationship

Convention 1:

This can be achieved by including reference navigation property of type UserAccount in the Challenge

    public class Challenge
    {
    public int Id { get; set; }
    public string ChallengeId {get; set;}
    public string Name { get; set; }
    public string Description { get; set; }

    public UserAccount UserAccount{get;set;}    
}

Convention 2:

Another convention is to include collection navigation property in UserAccount

public class UserAccount
{
    public int Id { get; set; }
    public string Username { get; set; }

    public ICollection<Challenge> Challenges { get; set; }
}

Convention 3:

Including navigation property at both ends will also result in one-to-many relationship

public class Challenge
{
public int Id { get; set; }
public string ChallengeId {get; set;}
public string Name { get; set; }
public string Description { get; set; }

public UserAccount UserAccount{get;set;}    
}

public class UserAccount
{
    public int Id { get; set; }
    public string Username { get; set; }

    public ICollection<Challenge> Challenges { get; set; }
}

Convention 4:

Fully defined relationship at both ends will creates one-to-many relationship

public class Challenge
{
  public int Id { get; set; }
  public string ChallengeId {get; set;}
  public string Name { get; set; }
  public string Description { get; set; }

  public int UserAccountId { get; set; }
  public UserAccount UserAccount{get;set;}    
}

Configure One-to-Many Relationship using Fluent API

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    // configures one-to-many relationship
    modelBuilder.Entity<UserAccount>()
        .HasRequired<Challeng>(s => s.ChallengId)
        .WithMany(g => g.UserAccount)
        .HasForeignKey<int>(s => s.ChallengId); 
}

Upvotes: 1

ocuenca
ocuenca

Reputation: 39326

That column comes from your relationship between Challenge and UserAccount. You could declare the FK property in your model:

public class Challenge
{
    public int Id { get; set; }
    public string ChallengeId {get; set;}
    public string Name { get; set; }
    public string Description { get; set; }

    //Add these properties
    public int UserAccountId{get;set;}
    public UserAccount UserAccount{get;set;}

    public virtual List<Competitor> Competitors { get; set; }
}

By default EF use some name conventions to identify the FK properties in your model, so it should be fine with that name. Now, if you are using Fluent Api, then I suggest to configure the relationship as you are intending to do:

protected override void OnModelCreating(DbModelBuilder mb)
{
    mb.Entity<UserAccount>()
        .HasMany(o1 => o1.Challenges)
        .WithOptional(e=>e.UserAccount);
        .HasForeignKey(e=>e.UserAccountId);

    base.OnModelCreating(mb);
}

Now if you don't want to include the FK property and the navigation property in your Challenge entity but you want to rename the FK property you can do this:

protected override void OnModelCreating(DbModelBuilder mb)
{
    mb.Entity<UserAccount>()
        .HasMany(o1 => o1.Challenges)
        .WithOptional();
        .Map(m => m.MapKey("UserAccountId"));

    base.OnModelCreating(mb);
}

Thinking a little more about your model it is true that maybe what you are really pursuing is a many to many relationship, if that if the case you can configure this way:

  mb.Entity<UserAccount>()
    .HasMany(r => r.Challenges)
    .WithMany() // No navigation property here
    .Map(m =>
        {
            m.MapLeftKey("UserAccountId");
            m.MapRightKey("ChallengeId");
            m.ToTable("UserAccountChallenge");
        });

Upvotes: 2

Related Questions