How do I setup the mapping for these table relationships using code first and the fluent api?
I have a poco for all three entities created but most people don’t have a poco for the “intermediate” table (PlanUser) so I couldn’t find a good example to go off of. I need to tell EF to map the PlanUser.PlanCustomerId column to Plan.CustomerId but it either doesn’t return the right results OR as in the current setup the mapping for Plan throws the error:
“The specified association foreign key columns ‘CustomerId’ are
invalid. The number of columns specified must match the number of
primary key columns.”


public class PlanUserMap : EntityTypeConfiguration<PlanUser>
{
public PlanUserMap()
{
this.ToTable("PlanUser");
this.HasKey(c => new { c.CustomerId, c.PlanCustomerId });
this.HasRequired(c => c.Plan).WithMany().HasForeignKey(x => x.CustomerId).WillCascadeOnDelete(false);
this.HasRequired(c => c.Customer).WithMany().HasForeignKey(x => x.CustomerId).WillCascadeOnDelete(false);
}
}
public class PlanMap : EntityTypeConfiguration<Plan>
{
public PlanMap()
{
this.ToTable("Plan");
this.HasKey(c => c.CustomerId);
// the below line returns only 1 row for any given customer even if there are multiple PlanUser rows for that customer
//this.HasMany(c => c.PlanUsers).WithRequired().HasForeignKey(c => c.PlanCustomerId);
// this throws an error
this.HasMany(c => c.PlanUsers).WithMany().Map(m => m.MapLeftKey("PlanCustomerId").MapRightKey("CustomerId"));
}
}
public partial class CustomerMap : EntityTypeConfiguration<Customer>
{
public CustomerMap()
{
this.ToTable("Customer");
this.HasKey(c => c.Id);
this.HasMany(c => c.PlanUsers).WithRequired().HasForeignKey(c => c.CustomerId);
}
}
@Slauma, sql profiler shows these queries being executed. The second one should include customer id 43 in addition to customer id 1 but it does not. I don’t know why its not retrieving that second row.
exec sp_executesql N'SELECT
[Extent1].[CustomerId] AS [CustomerId],
[Extent1].[PlanCustomerId] AS [PlanCustomerId],
[Extent1].[CreatedOnUtc] AS [CreatedOnUtc],
[Extent1].[IsSelected] AS [IsSelected],
[Extent1].[IsDeleted] AS [IsDeleted],
[Extent1].[AccessRights] AS [AccessRights]
FROM [dbo].[PlanUser] AS [Extent1]
WHERE [Extent1].[CustomerId] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=43
exec sp_executesql N'SELECT
[Extent1].[CustomerId] AS [CustomerId],
[Extent1].[Name] AS [Name],
[Extent1].[PlanTypeId] AS [PlanTypeId],
[Extent1].[OrderId] AS [OrderId],
[Extent1].[CreatedOnUtc] AS [CreatedOnUtc],
[Extent1].[IsActive] AS [IsActive]
FROM [dbo].[Plan] AS [Extent1]
WHERE [Extent1].[CustomerId] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=1
Here is the C# code that causes the queries to execute:
public List<Plan> GetPlans()
{
List<Plan> plans = new List<Plan>();
// add each plan they have access rights to to the list
foreach (var accessiblePlan in Customer.PlanUsers)
{
plans.Add(accessiblePlan.Plan);
}
return plans;
}
Ok, I figured it out. Thanks to @Slauma for the help! We are using the repository pattern with IOC(autofac). I shouldn’t be calling Customer.PlanUsers in my repository service in the first place. Somewhere along the line we started resolving the Customer entity using IOC in the repository service to get at certain customer properties quickly and put it in a read-only variable. Inevitably this hosed us because instead of querying the entity repositories themselves we started with the Customer entity expecting it to be filled with everything we needed and its not pre-filled.