Reputation: 531
I am working with a legacy system that implements a TPH for a certain number of items. So the current structure looks like this
Abstract Class 1 Abstract Class 2 Abstract Class 3
| | |
--------- --------- ---------
| | | | | | | | |
T1 T2 T3 T4 T5 T6 T7 T8 T9
So Type (T*) is a discriminator across all tables but since certain types share common columns, there are a significant number of different tables. The problem is that all of these items actually share a small commonality but there is no way to gather all of these items into a collection. In reality the hierarchy actually should look more like this.
--------------- Base Abstract 1 ----------
| | |
Abstract Class 1 Abstract Class 2 Abstract Class 3
| | |
--------- --------- ---------
| | | | | | | | |
T1 T2 T3 T4 T5 T6 T7 T8 T9
So essentially what we have is a TPT where each Table per type is a TPH. For a real world example, here is what we need.
--------------- Vehicle ---------------
| | |
Car Boat Plane
| | |
--------- --------- ---------
| | | | | | | | |
BMW Toyota Fiat T4 T5 T6 T7 T8 T9
Obviously there are some design flaws with the initial design and no one anticipated needing to grab a list of all vehicles without querying 3 different tables. So my question is, with the existing structure is there a way add this new hierarchy to entity framework. I was thinking something like this
Vehicle
-------
VehicleId
TypeId (Boat, Plane, Car, etc)
ItemFK (BoatID, PlaneId, CarId)
Is this possible? Is there a way to map these in entity framework? I cant seem to match them correctly. It seems it could possibly work if we were to replace BoatId, PlaneId, and CarId with VehicleId (like Conditional Mapping in Entity Framework - OR operation with TPH) but at that point we would be doing a really invasive schema change which is not really an option and I'm not sure that would even work. Essentially I need a way to map existing keys into a new hierarchy. Any help is greatly appreciated. I'm at a loss and can't seem to find any solution that answers my question.
Upvotes: 10
Views: 1546
Reputation: 15015
You could use this structure
public class Vehicle
{
[Key]
public int Id { set; get; }
///
// common properties
///
public Car Car { set; get; }
public Boat Boat { set; get; }
public Plane Plane { set; get; }
}
public class Car
{
[Key, ForeignKey("Vehicle")]
public int VehicleId { set; get; }
public Vehicle Vehicle { set; get; }
///
// Car properties
///
}
public class Boat
{
[Key, ForeignKey("Vehicle")]
public int VehicleId { set; get; }
public Vehicle Vehicle { set; get; }
///
// Boat properties
///
}
public class Plane
{
[Key, ForeignKey("Vehicle")]
public int VehicleId { set; get; }
public Vehicle Vehicle { set; get; }
///
// Plane properties
///
}
Upvotes: 1
Reputation: 79
The TPH/TPC conventions can be defined based on the DbSet<>
that you define in your DbContext
. For example, instead of declaring a DbSet<>
per derived type T
, you only declare a DbSet<>
for each abstract type. You can then query abstract classes individually with their corresponding DbSet<>
or all abstract classes with the DbSet<>
of the base abstract type.
The base abstract class will have to have at least one field defined so Code-First Migrations will generate a table for the type. The most logical field to define would be the PK. However, a migration of the current data won't work because of the PK collisions between the abstract classes (as you stated in a comment).
Another possibility is that Entity Framework will properly query all the abstract types when you query a DbSet<>
of the base abstract type even if there is no table in the database for the base abstract type (because the base abstract type has no fields defined). However, I haven't run into this scenario before so I can't say with certainty whether it will work or not.
Upvotes: 0
Reputation: 4997
The problem is that all of these items actually share a small commonality but there is no way to gather all of these items into a collection.
Perhaps you could have types in each hierarchy implement a common interface? Since each hierarchy is already a separate table, it doesn't seem like you would gain much -- and it seems like it would not be worth the hassle -- by adding a common base class.
Upvotes: 0