Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EF Fluent API Many To Many with different ID Field Names

In this question: Ef Many To Many, an answer came up on how to manually specify a linking table. But I have a slightly unique situation (which I'm sure isn't really unique).

My two tables each have an Id field. E.G.: [dbo].[Account].[Id] and [dbo].[Person].[Id]. Each of these tables in my Code-First has the following OnModelCreating:

modelBuilder.Entity<Account>.HasKey(x => x.Id);
modelBuilder.Entity<Person>.HasKey(x => x.Id);

But my [dbo].[AccountsToPersons]... table has fields E.G.: [AccountId] and [PersonId]

The AccountsToPersons table is not represented by a class in code.

I obviously already have an existing Model, but we are using EF Code-First Fluent API instead of updating model from database.

So how do I change this code to make it work with mapping different ID columns names?

public DbSet<Person> Persons { get; set; }
public DbSet<Account> Accounts { get; set; }
. . .
modelBuilder.Entity<Account>()
  .HasMany(a => a.Persons)
  .WithMany()
  .Map(x =>
  {
    x.MapLeftKey("AccountId"); // <-- Account.Id to AccountsToPersons.AccountId??
    x.MapRightKey("PersonId"); // <-- Person.Id  to AccountsToPersons.PersonId??
    x.ToTable("AccountsToPersons");
  });

When running a basic Linq To EF Query (from x in context.Accounts select x).ToList();, the query fails with the following error:

  • "Invalid Column Name 'Person_Id'."

But when running the Query (from x in context.Persons select x).ToList();, I get no error.

Other than basic typed columns, my models have these added to them:

// In my Account Model, I also have this property:
public IList<Person> Persons { get; set; }

// In my Person Model, I also have this property:
public IList<Account> Accounts { get; set; } // <-- in the Person model

And please note that even though my Accounts query passes and has field information, the Persons field is always null, even though I'm sure there are links in my AccountsToPersons table.

like image 593
Suamere Avatar asked Nov 21 '14 18:11

Suamere


2 Answers

Try adding p => p.Accounts to your WithMany clause:

modelBuilder.Entity<Account>()
  .HasMany(a => a.Persons)
  .WithMany(p => p.Accounts) // <-- I think this should fix it
  .Map(x =>
  {
    x.MapLeftKey("AccountId"); // <-- Account.Id to AccountsToPersons.AccountId??
    x.MapRightKey("PersonId"); // <-- Person.Id  to AccountsToPersons.PersonId??
    x.ToTable("AccountsToPersons");
  });
like image 126
SOfanatic Avatar answered Oct 05 '22 23:10

SOfanatic


I just built up a test solution for your problem and for me it looks that is working.

One thing that i see you did different than me is:

public IList<Person> Persons { get; set; } // <-- in the Account model
public IList<Account> Accounts { get; set; } // <-- in the Person model

Try modifying into this:

public DbSet<Person> Persons { get; set; }
public DbSet<Account> Accounts { get; set; }

If this doesn't work i`ll post my entire setup.

My structure looks like this and it works for the queries you displayed:

public class Person
{
    public int ID { get; set; }

    public string Name { get; set; }

    public IList<Account> Accounts { get; set; }
}

public class Account
{
    public int ID { get; set; }

    public string Name { get; set; }

    public IList<Person> Persons { get; set; }
}

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public DbSet<Account> AccountSet { get; set; }

    public DbSet<Person> PersonSet { get; set; }

    public ApplicationDbContext()
        : base("DefaultConnection")
    {
        this.Database.Log = (msg) => { Debug.Write(msg); };
    }

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

        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();

        modelBuilder.Entity<Account>().HasKey(x => x.ID);
        modelBuilder.Entity<Person>().HasKey(x => x.ID);

        modelBuilder.Entity<Account>()
            .HasMany(a => a.Persons)
            .WithMany()
            .Map(w =>
            {
                w.MapLeftKey("AccountId");
                w.MapRightKey("PersonId");
                w.ToTable("AccountsToPersons");
            });
    }
}
like image 32
alexo Avatar answered Oct 05 '22 23:10

alexo