I currently have a Entity Framework 4.0 model in place with Table Per Type (TPT), but there are a few performance issues (lots of LOJ's/CASE statements), as well as an issue mapping between two particular domain areas (many-to-many).
I've decided to try out TPH.
I have an entity called "Location" which is abstract, and the base for all other entities.
I then have "Country", "City", "State", "Street", etc which all derive from Location.
"LocationType" is the dicriminator.
That part is working fine, but i'm having issues trying to define navigational properties for the derived types.
For instance, a "State" has a single "Country", so i should be able to do this:
var state = _ctx.Locations.OfType<State>().Include("Country").First();
var countryForState = state.Country;
But this would require a navigational property called "Country" on the "State" derived entity. How do i do this? When i generate the model from the database, i have a single table with all the FK's pointing to records in the same table:
(NOTE: I created those FK's manually in the DB).
But the FK's are placed as nav's on the "Location" entity, so how do i move these navigational properties down to the derived entities? I can't copy+paste the navs across, and i can't "create new navigational property", because it won't let me define the start/end role.
How do we do this?
It's also not clear with TPH if we can do it model-first, or we HAVE to start with a DB, fix up the model then re-generate the DB. I am yet to find a good example on the internet about how to define navs on children with TPH.
NOTE: I do not want to do code-first. My current solution has TPT with the EDMX, and pure POCO's, i am hoping to not affect the domain model/repositories (if possible), and just update the EF Model/database.
EDIT
Still no solution - however im trying to do model-first, and doing Add -> New Association, which does in fact allow me to add a nav to the derived entities. But when i try and "Generate database from Model", it still tries to create tables for "Location_Street", "Location_Country" etc. It's almost like TPH cannot be done model first.
EDIT
Here is my current model:
The validation error i am currently getting:
Error 1 Error 3002: Problem in mapping fragments starting at line 359:Potential runtime violation of table Locations's keys (Locations.LocationId): Columns (Locations.LocationId) are mapped to EntitySet NeighbourhoodZipCode's properties (NeighbourhoodZipCode.Neighbourhood.LocationId) on the conceptual side but they do not form the EntitySet's key properties (NeighbourhoodZipCode.Neighbourhood.LocationId, NeighbourhoodZipCode.ZipCode.LocationId).
Just thought i'd keep editing this question with edit's regarding where i am currently at. I'm beginning to wonder if TPH with self-referencing FK's is even possible.
EDIT
So i figured out the above error, that was because i was missing the join-table for the Neighbourhood-ZipCode many to many.
Adding the join table (and mapping the navs to that) solved the above error.
But now im getting this error:
Error 3032: Problem in mapping fragments starting at lines 373, 382:Condition members 'Locations.StateLocationId' have duplicate condition values.
If i have a look at the CSDL, here is the association mapping for "CountyState" (a State has many counties, a County has 1 state):
<AssociationSetMapping Name="CountyState" TypeName="Locations.CountyState" StoreEntitySet="Locations">
<EndProperty Name="State">
<ScalarProperty Name="LocationId" ColumnName="StateLocationId" />
</EndProperty>
<EndProperty Name="County">
<ScalarProperty Name="LocationId" ColumnName="LocationId" />
</EndProperty>
<Condition ColumnName="StateLocationId" IsNull="false" />
</AssociationSetMapping>
It's that Condition ColumnName="StateLocationId"
which is complaining, because ZipCodeState
association also this condition.
But i don't get it. The discriminators for all entities are unique (i have triple checked), and i would have thought this was a valid scenario:
- County has a single State, denoted by StateLocationId (Locations table)
- ZipCode has a single State, denoted by StateLocationId (Locations table)
Is that not valid in TPH?