Unable to specify abstract classes in TPH hierarchy in Entity Framework 4 - entity-framework

I have a TPH heirachy along the lines of:
A->B->C->D
A->B->C->E
A->F->G->H
A->F->G->I
I have A as Abstract, and all the other classes are concrete with a single discriminator column.
This works fine, but I want C and G to be abstract also. If I do that, and remove their discriminators from the mapping, I get error 3034 'Two entities with different keys are mapped to the same row'. I cannot see how this statement can be correct, so I assume it's a bug in some way.
Is it possible to do the above?
Lee

Seeing as they are abstract - so there are no entities in the database - have you tried just lying and pretending they do have a discriminator.
This should satisfy the mapping, but since the classes are abstract you still won't be able to create or materialize.
Alex

Related

EF Core HasData Seeding a TPH Entity

I want to use the EF Core HasData method to seed a database with reference data. Two of the models I want to seed follow the Table per Hierarchy (TPH) pattern in the sense that there is a none abstract base type and another none abstract derived type. These models in the database exist in a table named after the base type with a discriminator column. I would like to add data seeding for this table. However I’m struggling to find any guidelines of how to do so in the EF Core 6 documentation.
I have a few related questions:
Do I seed all the data using the HasData method on an EntityTypeBuilder<BaseType> or do I need to split the seeding into one HasData on that class and another on EntityTypeBuilder<DerivedType>?
I understand using TPH will add a shadow discriminator property and that, potentially, I’ll have to add that in the data seeding. Does that mean I have to use anonymous types to specify that property (doesn’t seem very elegant) and if so, can I get the autogenerated discriminator name using a method (typing it manually sounds like a risk as what if EF Core changes the discriminator name convention?)?
Should I be avoiding using TPH on reference tables altogether? Is there something else I should do instead?
After going through my options with trial and error, I have come to the following solution. It may not be perfect, but it deals with all of my concerns.
Do I seed all the data using the HasData method on an EntityTypeBuilder<BaseType> or do I need to split the seeding into one HasData on that class and another on EntityTypeBuilder<DerivedType>?
You have to do it on both classes, attempting otherwise throws on creating the migration:
The seed entity for entity type 'BaseType' cannot be added because the value provided is of a derived type 'DerivedType'. Add the derived seed entities to the corresponding entity type.
I understand using TPH will add a shadow discriminator property and that, potentially, I’ll have to add that in the data seeding. Does that mean I have to use anonymous types to specify that property (doesn’t seem very elegant) and if so, can I get the autogenerated discriminator name using a method (typing it manually sounds like a risk as what if EF Core changes the discriminator name convention?)?
I have added a property to the BaseType class and defined it as the discriminator, so now I can specify the discriminator values directly without using anonymous type and I am in control of the discriminator values. Gert Arnold points out in this other answer why this might not be appropriate. However I have used the following method described in this EF Core Github issue to hopefully mitigate the concerns he raised.
Should I be avoiding using TPH on reference tables altogether? Is there something else I should do instead?
This is still unanswered. But I am feeling a lot more confident it is a good approach now.

Conceptual side cannot be mapped to object side?

I use the Entity Framework with Db-First approach. I used to have a table called Ranking that I mapped to an abstract base class with a few inherited concrete classes. Now I recently removed all the inheritance and choose to just use one concrete class called Ranking.
But since I changed it back I get the following runtime Exception:
Type 'DbModel.Ranking' in conceptual side cannot be mapped to type 'My.Application.Models.Ranking' on the object side. Both the types must be abstract or both must be concrete types.
In my code generation I have set Abstract to false, but I don't know how to change this on the conceptual side.
I even tried deleting the Ranking table from my table designer, and then update it again from the database. This didn't help either.
Anyone an idea?
Found it. There was a partial class defined where I forgot to remove the abstract modifier.
So the generated partial class didn't had the abstract modifier anymore, but the other partial still had.
Pretty stupid after all, but hopefully this prevents some other people wasting half an hour.

Setting up relation to base model which has many classes that inheritate in Code First Entity Framework

I'm creating my db using code-first and entity framework. I'm defining all my models which works great, but now I have a Page class which can contain Blocks.
These Blocks have a base class with ID, BlockType, Position, Name etcetera but there are different types of blocks with different properties which are classes that inherit from this base class. I'm wondering how I can setup my models so the page can have a collection of 'blocks' without storing the custom properties in a comma seperated list or something like that.
Is it possible? And what is the 'good' way to model this..
Thanks in advance!
Entity Framework supports inheritance. There are various strategies to map your object model - base and derived classes - to a database schema. The most important are:
Table per hierarchy (TPH): Base class and all derived classes will be mapped to a single table in the database. Any custom properties of derived classes will appear as separate columns in the table and the table contains a discriminator column to distinguish between the types. EF manages to load the columns needed to materialize a specific type.
Table per type (TPT): The base class has its own table which only contains the base class properties. Every derived entity gets another table that has the additional properties of this type. EF manages to load the properties from the different tables (creating appropriate joins) that contain all properties to materialize a specific type.
TPT is - in my opinion - the cleaner approach to implement inheritance, but it currently (EF <= 4.3) has performance problems compared to TPH due to suboptimal SQL generated by EF. The problem will be solved in EF 5.0. But TPH will still remain the more performant way of mapping because it doesn't need to join multiple tables.
Benefits and drawbacks of the strategies are discussed in detail in the linked blog posts. In the blog you can also find the third (less often used) option - Table per concrete type (TPC).

ADO.NET Entity Framework Hierarchy type Inheritance Error 3032 Workaround?

I would like to implement a Hierarchy Type Inheritance ... i.e. Cars to BMWs ... Cars would have CarTypeID that is associated with CarTypes. I would like CarTypeID to be the discriminator column. Unfortunately, I cannot do this in ADO.NET Entity Framework.
If I would like to keep the association, how am I able to achieve this?
Has anyone come up with a work around?
You can't do this. You must use a different column, as the column which is a constrained FK end and the inheritance discriminator column cannot be the same.
I'm of mixed minds as to whether this is a bad thing or not. I think inheritance is often overused, especially in O/R modeling. If you already have a FK to car type info...

Table per hierarchy inheritance with POCO entities in Entity Framework 4

Our organization is looking to standardize on Entity Framework once v4 comes out. As a result, I am looking at what it would take to migrate our application that uses NHibernate for persistence to EF4 using POCO support. In a couple of places we use single table inheritance (also known as Table Per Hierarchy). I have been unable to get it to work using the following.
Payment (base class [should be abstract but having trouble there as well])
CreditCardPayment (concrete implementation)
ACHPayment (concrete implementation)
CheckPayment (concrete implementation)
Right now, I am mapping them with only the base class properties. All of these classes are in the same namespace. They have a discrimimator that is called PaymentTypeId in the database, so the Payment mapping has a condition of "When PaymentTypeId = 0". Each of the subclasses have the same condition with different values (i.e. CreditCardPayment = 1, etc.).
When I try to load each a list of all payments using DataContext.Payments.ToList() (DataContext inherits from ObjectContext) I am getting the following exception:
"Object mapping could not be found for Type with identity 'DataLayer.DataModel.CreditCardPayment'."
I can't figure out what this means, as the POCO CreditCardPayment class lives in the same namespace as the POCO Payment class does (in fact in the same file).
What am I missing?
This is complaining not about database mapping, but model to CLR mapping.
The EF can't for some reason find your CreditCardPayment class.
Now one possible reason is that you haven't loaded the metadata for it yet.
For example if you have this:
Assembly 1:
- Payment
Assembly 2 references Assembly 1:
- CreditCardPayment extends Payment
Then when you query the EF doesn't know where CreditCardPayment lives.
The way to get around this is with LoadAssembly i.e:
using (DataContext ctx = new DataContext())
{
ctx.MetadataWorkspace.LoadFromAssembly(typeof(CreditCardPayment).Assembly);
// now do your query.
}
You need to tell to LoadFromAssembly every assembly that isn't directly reference by your DataContext class.
Note: typeof(Payment).Assembly is directly referenced because of the IQueryable<Payment> Payments property.
Hope this helps
Alex
Microsoft.
What I didn't represent before (I didn't think it relevant, but it was). Was that CreditCardPayment inherited from an intermediary class named "CreditPayment" and ACHPayment inherited from CashPayment. CreditCardPayment and CashPayment live in the same namespace and file, but were not represented in the EF mapping. Once I added those within the mapping file, everything worked ok.
So, even thought EF does not ever map to one of those types directly, it seems to need to know about them, because it changes the basetype of the CreditCardPayment classes et al. Thank you for your help on this.