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
People
table? I actually expected two. If I remove the propertypublic Company Company { get; set; }
from thePerson
the third columnCompanyCompanyId2
disappears but I also lose the reference property in the class.2) Let's say I drop the
Company
property from thePerson
table (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-createdCompanyCompanyId
andCompanyCompanyId1
? (For instanceFCompanyId
andMCompanyId
to indicate the relation to theFemaleEmployees
andMaleEmployees
collections.)3) Is there any way to define this model with only one foreign key
CompanyId
in thePeople
table? Surely I would need a differentiating additional column in thePerson
class (likebool IsFemale
). A Person is either part of theFemaleEmployees
or theMaleEmployees
collection, 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 aFemalePerson
andMalePerson
class which both derive fromPerson
as 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 keyCompanyId
would allow me to make itnon-nullable
which 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
Company
in classPerson
to two different collection endpointsFemaleEmployees
andMaleEmployees
in classCompany
at the same time. The mapping conventions assume that there is actually a third endpoint inCompany
which 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
Map
method of theForeignKeyNavigationPropertyConfiguration
class: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