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 property public Company Company { get; set; }
from the Person
the third column CompanyCompanyId2
disappears but I also lose the reference property in the class.
2) Let's say I drop the Company
property from the Person
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-created CompanyCompanyId
and CompanyCompanyId1
? (For instance FCompanyId
and MCompanyId
to indicate the relation to the FemaleEmployees
and MaleEmployees
collections.)
3) Is there any way to define this model with only one foreign key CompanyId
in the People
table? Surely I would need a differentiating additional column in the Person
class (like bool IsFemale
). A Person is either part of the FemaleEmployees
or the MaleEmployees
collection, never in both (naturally in this example), so with SQL I could fetch those collections by something like WHERE 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 a FemalePerson
and MalePerson
class which both derive from Person
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 key CompanyId
would allow me to make it non-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 class Person
to two different collection endpoints FemaleEmployees
and MaleEmployees
in class Company
at the same time. The mapping conventions assume that there is actually a third endpoint in Company
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 the ForeignKeyNavigationPropertyConfiguration
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With