Table-per-type inheritance with EF 4.1 Fluent Code First
K

2

8

I have a pretty straight forward set of database tables, like:

Vehicle
 Id
 RegNo

Car
 Id (FK of Vehicle.Id)
 OtherStuff

Bike
 Id (FK of Vehicle.Id)
 MoreStuff

My class model is as you'd expect: with Vehicle being an abstract class, and then Car and Bike being subclasses of it.

I have setup my EF4.1 Code First configuration as follows:

class VehicleConfiguration : EntityTypeConfiguration<Vehicle> {
    public VehicleConfiguration() {
        ToTable("Vehicles");
        Property(x => x.Id);
        Property(x => x.RegNo);
        HasKey(x => x.Id);
    }
}

class CarConfiguration : EntityTypeConfiguration<Car> {
    public CarConfiguration() {
        ToTable("Cars");
        Property(x => x.OtherStuff);
    }
}

class BikeConfiguration : EntityTypeConfiguration<Bike> {
    public BikeConfiguration() {
        ToTable("Bikes");
        Property(x => x.MoreStuff);
    }
}

However I am getting numerous strange exceptions when EF tried to build its model configuration.

Currently it is throwing out this:

System.Data.EntityCommandExecutionException: An error occurred while executing the command definition. See the inner exception for details. ---> System.Data.SqlClient.SqlException: Invalid column name 'Discriminator'.

Where is it getting that column name from? It's not in any of my code or the database itself. It must be some convention that's taking over control. How do I instruct EF to use table-per-type?

If I remove the "abstract" keyword from my Vehicle class (which I did as a sanity test somewhere along the line) then I get a different exception like the following:

(35,10) : error 3032: Problem in mapping fragments starting at lines 30, 35:EntityTypes AcmeCorp.Car, AcmeCorp.Bike are being mapped to the same rows in table Vehicles. Mapping conditions can be used to distinguish the rows that these types are mapped to.

I'm obviously doing something terribly wrong, but what? I've followed the MSDN docs and all the other TPT + EF4.1 articles I can find!

Karen answered 30/6, 2011 at 16:13 Comment(6)
Do you actually add those configuations in OnModelCreating of your derived DbContext? Something like modelBuilder.Configurations.Add(new VehicleConfiguration()); etc. should be there. Somehow it looks as if EF would use default inheritance mapping which is TPH and not TPT. If you do, could you show the class definitions.Bunk
@Slauma, yes the EntityTypeConfiguration's are all being added correctly to the DbContext. Don't worry about that. The class definitions are ridiculously simple, nothing more than: public abstract class Vehicle { public Guid Id { get; set; } public String RegNo { get; set; } } public class Car : Vehicle { public String OtherStuff { get; set;} } public class Bike : Vehicle { public String MoreStuff { get; set; } }Karen
Can you set breakpoints at the 3 ToTable lines in your code to check if you really reach them. Basically ToTable says: "TPT". Without ToTable EF would use TPH. The fact that EF is somehow querying for a column "Discriminator" means that it assumes TPH inheritance. And can you show your full DbContext definition and the code where exactly the exception occurs. The code you are showing right now is correct imo, I believe that the problem is somewhere else. Are you creating a new DB or using an existing one? Do you have any Database.SetInitializer call?Bunk
I think I have figured out what is causing it. I setup a totally separate test project and did the bare minimum of code, which worked as expected. However, as soon as I added a new subclass of Vehicle in the model, but which was neither present as a table in the database nor configured in the EF mappings. EF starts throwing that "Problem in mapping fragments..." exception. My subclass is marked as internal, whereas the others are public, so I don't really see why EF even knows it exists! Is there some way to make EF totally ignore this subclass that is a "special case" for my model?Karen
You can either put the [NotMapped] attribute on the class or use in Fluent API modelBuilder.Ignore<Foo>(); if you want to exclude the class Foo from the EF model.Bunk
I know this is old but I just came accross the issue. This is still an problem in 6.0. When I say problem I mean the very confusing error message about the mapping fragments ..., when the cause at its root is different and causd by a third class. nbevans, you should add your comment as a response and flag it accordingly as the correct one.Disembroil
L
8

Have you read the following article?

It is a 3 part article covering the following approaches

  1. Table per Hierarchy (TPH): Enable polymorphism by denormalizing the SQL schema, and utilize a type discriminator column that holds type information.

  2. Table per Type (TPT): Represent "is a" (inheritance) relationships as "has a" (foreign key) relationships.

  3. Table per Concrete class (TPC): Discard polymorphism and inheritance relationships completely from the SQL schema.

Lovable answered 29/7, 2011 at 22:24 Comment(2)
I don't think this addresses the problem. The code in the question already looks correct according to the corresponding article you linked to.Maggiemaggio
The original poster says they've read all the articles they could find, that 3 part article helped me lots 2 years ago when I where working with EF, Thus I added them (2 years ago) with the question if the original poster had read them. Your correct, it does not address the exact problem, but it does supply a great article that can give a better understanding of inheritance in EFLovable
M
8

When I had this problem, I discovered that I had a subclass that was not mapped. In this example, some possible causes of this are:

  1. An additional subclass exists, such as Bus. This is not mapped.
  2. One of the subclasses, such as Car, is not mapped.

In this case, ensure that every subclass is mapped:

  1. Ensure a mapping exists for the subclass, including a ToTable method call.
  2. Ensure the mapping is actually being applied during OnModelCreating.
  3. If you have trouble tracking this, try using a debugger and setting breakpoints in all of the mapping code.

Alternatively, if the subclass shouldn't be mapped in the first place, ensure that it is ignored by using one of the Ignore method calls.

Maggiemaggio answered 21/10, 2013 at 1:22 Comment(2)
This was my exact problem and it was insanely hard to find because of the way a new inherited type was added to the codebase (stubbed but not in use currently). After a whole day of trying to search for "Why does EF think I am implementing TPH" I find the answer in a 6 year old question :(Rosamariarosamond
@CarrieKendall Looks like you haven't searched the right place. Entity Framework Code First Conventions - Type Discovery section of the documentation contains the following: "If your types participate in an inheritance hierarchy, it is enough to define a DbSet property for the base class, and the derived types will be automatically included, if they are in the same assembly as the base class."Ambagious

© 2022 - 2024 — McMap. All rights reserved.