With EF CTP5 Code-First I am trying to map a class model which contains multiple collections in one class pointing to another class. Here is an example of what I mean:
public class Company
{
public int CompanyId { get; set; }
public IList<Person> FemaleEmployees { get; set; }
public IList<Person> MaleEmployees { get; set; }
}
public class Person
{
public int PersonId { get; set; }
public Company Company { get; set; }
}
If I let the database create from this model with a DbContext without further customization, like so:
public class MyContext : DbContext
{
public DbSet<Company> Companies { get; set; }
public DbSet<Person> People { get; set; }
}
... then I get two tables in SQL Server, a simple Companies table with only a CompanyId column and a People table with the following columns ("FKRN" means "Foreign Key Relationship Name", as created by EF in SQL Server):
PersonId int not nullable
CompanyCompanyId int nullable FKRN: Company_FemaleEmployees
CompanyCompanyId1 int nullable FKRN: Company_MaleEmployees
CompanyCompanyId2 int nullable FKRN: Person_Company
The last three columns have all a foreign key relationship to the primary key CompanyId of the Companies table.
Now I have several questions:
1) Why do I get three foreign key columns in the
Peopletable? I actually expected two. If I remove the propertypublic Company Company { get; set; }from thePersonthe third columnCompanyCompanyId2disappears but I also lose the reference property in the class.2) Let's say I drop the
Companyproperty from thePersontable (I don't need it really in my model). Is there a way to give the two remaining foreign key columns another name than the auto-createdCompanyCompanyIdandCompanyCompanyId1? (For instanceFCompanyIdandMCompanyIdto indicate the relation to theFemaleEmployeesandMaleEmployeescollections.)3) Is there any way to define this model with only one foreign key
CompanyIdin thePeopletable? Surely I would need a differentiating additional column in thePersonclass (likebool IsFemale). A Person is either part of theFemaleEmployeesor theMaleEmployeescollection, never in both (naturally in this example), so with SQL I could fetch those collections by something likeWHERE IsFemale = true/false AND CompanyId = 1. I am wondering if I could give EntityFramework a hint to load the two collections this way. (Here I would like to avoid to extend the model by aFemalePersonandMalePersonclass which both derive fromPersonas base class and then use for instance Table-Per-Hierarchy mapping, since these derived classes would be empty and artificial and had no other purpose except enabling the mapping to SQL Server.) Having only one foreign keyCompanyIdwould allow me to make itnon-nullablewhich isn't possible with two foreign keys (both can never be non-null in the same row).
Thank you for feedback and suggestions in advance!
To question (1): EF cannot map the single reference property
Companyin classPersonto two different collection endpointsFemaleEmployeesandMaleEmployeesin classCompanyat the same time. The mapping conventions assume that there is actually a third endpoint inCompanywhich isn't exposed in the model. Therefore a third foreign key is created.To question (2): With the EF 4.1 Release Candidate it is now possible to specify the database column name of foreign keys in the Fluent API (which wasn't possible with EF CTP5) by using the
Mapmethod of theForeignKeyNavigationPropertyConfigurationclass:modelBuilder.Entity<Company>() .HasMany(c => c.FemaleEmployees) .WithOptional() .Map(conf => conf.MapKey("FCompanyId")) .WillCascadeOnDelete(false); modelBuilder.Entity<Company>() .HasMany(c => c.MaleEmployees) .WithOptional() .Map(conf => conf.MapKey("MCompanyId")) .WillCascadeOnDelete(false);To question (3): I still have no idea.
Edit
Just to close this old question now: (3) (relating two navigation properties in one entity to the same endpoint of another entity) is not possible, for example: Specific Entity Framework Code First Many to 2 Model Mapping ... (and I remember many other questions which were looking for a solution for such a scenario without success)
I think you could do this:
public class Company
{
public int CompanyId { get; set; }
public ICollection<Person> Employees { get; set; }
public IEnumerable<Person> MaleEmployees {
get
{
Employees.Where(x=> !x.IsFemale);
}
}
}
public class Person
{
public int PersonId { get; set; }
public Company Company { get; set; }
}
You just have one CompanyID FK at People table.
You can load Male Employees using EF context:
context.Entry(companyInstance)
.Collection(p => p.Employees)
.Query()
.Where(u => !u.IsFemale)
.Load();
I think that your approach isn´t so good, because, what happens when I add a male to Company.FemaleEmployees? EF dont know this rules
来源:https://stackoverflow.com/questions/5067688/entity-framework-ctp5-code-first-mapping-a-class-with-multiple-collections-of-a