Duplicate ForeignKey when using inheritance - c#

I have created these classes in order to generate the database model via EntityFramework 6 code-first approach:
public class Vehicle
{
public long Id { get; set; }
public long ResponsiblePersonId { get; set; }
}
public class Car: Vehicle {
public int HorsePower { get; set; }
}
public class Bike: Vehicle {
public int FrameSize { get; set; }
}
public class Organisation
{
public Organisation()
{
Cars = new List<Car>();
Bikes = new List<Bikes>();
}
public long Id { get; set; }
public List<Car> Cars { get; set; }
public List<Bike> Bikes { get; set; }
}
So far this seemed right for me.
But unfortunately, the resulting table looks like this:
Id | ResponsiblePersonId | HorsePower | FrameSize | Discriminator | Organisation_Id | Organisation_Id1
Why is the Organisation ForeignKey being generated twice? I expected this table to only have one Organisation_Id column.
Thanks

There are several ways for EF to implement the physical tables for your inheritance hierarchy. The default one, the one you are using, is called Table Per Hierarchy (TPH). It uses only one table for all the derived entities, with one Discriminator column to specify the type of entity which is contained in the record. EF also adds to the table a column for each property that is included in any of the derived entities.
So as the relationship between the derived entities and Organisation is defined at child level (the lists of Car and Bike properties in Organisation entity) EF decides to create a separate column for each child entity type Organisation_Id, and you don't want that.
How to change this? There are several ways:
Don't use TPH. Use instead TPC (Table Per Concrete class). That is, EF creates a separate table for each one of your child entities. How to do this: remove the DbSet<Vehicle> property from your DbContext. If this doesn't make it, set an explicit configuration for the physical table name for each entity derived from Vehicle like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
...
modelBuilder.Entity<Car>().ToTable("Cars");
modelBuilder.Entity<Bike>().ToTable("Bikes");
}
If you need to continue using TPH, I don't know of any way to implementing this that will generate only one OrganisationId column in the database and only one Foreign Key between Vehicle and Organisation. Common sense would say that you might define the Organisation foreign key at the Vehicle base entity level. But then you get errors when generating the migration:
Organisation: FromRole: NavigationProperty 'Organisation' is not
valid. Type 'Car' of FromRole 'Organisation_Cars_Target' in
AssociationType 'Organisation_Cars' must exactly match with the type
'Vehicle' on which this NavigationProperty is declared on.
It seems that when the relationship is defined at base level then EF expects the lists in Organisation to be defined of type Vehicle and not Car or Bike. And this does not fit with your model.
And if you try to define OrganisationId or Organisation properties in your derived classes then you get a different error when generating the migration, because you are not allowed to use the same name for the properties in the different child entities. You can use different names, but then you get two columns again. There is no way to get one column this way either.
So if you stick with TPH, as far as I know, you have to put up with having two columns for your OrganisationId. At least you can name them in a more verbose way with some fluent configurations:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
...
modelBuilder.Entity<Organisation>()
.HasMany(o => o.Bikes)
.WithRequired()
.Map(x => x.MapKey("OrganisationIdBike"));
modelBuilder.Entity<Organisation>()
.HasMany(o => o.Cars)
.WithRequired()
.Map(x => x.MapKey("OrganisationIdCar"));
}
I would recommend you to change to TPC, as with your model the fluent mappings are a bit less complex to write.
For a better understanding of TPH, TPC and TPT (Table Per Type, yet another implementation of inheritance hierarchies) read this post.

Related

Inheriting properties from model - Navigation properties can only participate in a single relationship

I'm trying to share common properties with multiple entities by using multiple levels of inheritance, but I'm running into an error.
Cannot create a relationship between 'User.SupersCreated' and 'Super.CreatedBy' because a relationship already exists between 'User.BasicsCreated' and 'Basic.CreatedBy'. Navigation properties can only participate in a single relationship. If you want to override an existing relationship call 'Ignore' on the navigation 'Super.CreatedBy' first in 'OnModelCreating'.
The structure of my models is as follows.
public class EntityBase
{
public Guid Id { get; set; }
public Guid CreatedById { get; set; }
public User CreatedBy { get; set; }
}
public class Basic: EntityBase
{
public string BasicProperty { get; set; }
}
public class Super : Basic
{
public string SuperProperty { get; set; }
}
public class User : IdentityUser<Guid>
{
public ICollection<Basic> BasicsCreated { get; set; }
public ICollection<Super> SupersCreated { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<User>()
.HasMany(x => x.BasicsCreated)
.WithOne(x => x.CreatedBy);
modelBuilder.Entity<User>()
.HasMany(x => x.SupersCreated)
.WithOne(x => x.CreatedBy);
}
The problem seems to be a result of Super Inheriting from Basic, or at least, the problem goes away when I remove this level of inheritance and make Super inherit from EntityBase (however than I'll lose the properties that exist in Basic).
Can anyone please help me understand why I'm getting this error and what should be done to fix it?
Edit
After considering this some more, I think I'm trying to abuse inheritance to do what it's not intended to do.
The database structure I was hoping to end up with, is:
Even though my Basic and Super tables share the same properties, with Super having it's own additional properties, there's no relationship between Basic data and Super data.
From having a look at Microsoft's tutorial on implementing inheritance, there's two options:
Table per type
Table per hierarchy
Neither of these are what I'm trying to achieve.
Perhaps I should be using interfaces to define the common properties that exist between unrelated entities. It seems like I need to back and re-evaluate my design anyway.
If some of the base classes of the entity is identified as entity (as with your Super and Basic), by default EF Core will try to use one of the database inheritance strategies.
If you don't want that (want to treat is just like non entity base class), then you have to configure that explicitly at the very beginning of the OnModelCreating, e.g. for your sample
modelBuilder.Entity<Super>().HasBaseType((Type)null);
or more generally using a loop similar to this
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
entityType.BaseType = null;
and then define explicitly the entity hierarchy if and where needed.

EF inheritance with table splitting

I'm trying to map two different EF models to the same table SharedTable, let us call them EntityA and EntityB. I made them both extend a base Entity called BaseEntity.
EntityA is defined only with SharedTable fields, EntityB has fields in SharedTable and EntityBTable.
modelBuilder.Entity<BaseEntity>()
.Map<EntityA>(m => m.Requires("IsEntityA").HasValue<bool>(true))
.Map<EntityB>(m => m.Requires("IsEntityA").HasValue<false>(true));
modelBuilder.Configurations.Add(new EntityBMap());
modelBuilder.Configurations.Add(new EntityAMap());
modelBuilder.Configurations.Add(new BaseEntityMap());
The models look like this
public class BaseEntity
{
[Required]
public int Id { get; set; }
public int SharedTableField1 { get; set; }
}
public class EntityA : BaseEntity
{
public int SharedTableField2 { get; set; }
}
public class EntityB : BaseEntity
{
public int EntityBTableField1 { get; set; }
}
The mappings are
public class BaseEntityMap : EntityTypeConfiguration<BaseEntity>
{
public BaseEntityMap()
{
// Primary Key
this.HasKey(t => t.Id);
this.ToTable("SharedTable");
this.Property(t => t.Id).HasColumnName("Id");
this.Property(t => t.SharedTableField1).HasColumnName("SharedTableField1");
}
}
public class EntityAMap : EntityTypeConfiguration<EntityA>
{
public EntityAMap()
{
this.HasKey(t => t.Id);
this.Property(t => t.Id).HasColumnName("Id");
this.ToTable("SharedTable");
this.Property(t => t.SharedTableField2).HasColumnName("SharedTableField2");
}
}
public class EntityBMap : EntityTypeConfiguration<EntityB>
{
public EntityBMap()
{
Map(c =>
{
HasKey(t => t.Id);
Property(t => t.Id).HasColumnName("Id");
c.Properties(t => new
{
t.SharedTableField2
});
c.ToTable("SharedTable");
});
Map(c =>
{
c.Properties(t => new
{
t.EntityBTableField1
});
c.ToTable("EntityBTable");
});
}
}
The error I get says:
A first chance exception of type 'System.NotSupportedException' occurred in EntityFramework.dll
Additional information: The type 'EntityB' cannot be mapped as defined because it maps inherited properties from types that use entity splitting or another form of inheritance. Either choose a different inheritance mapping strategy so as to not map inherited properties, or change all types in the hierarchy to map inherited properties and to not use splitting.
Any way around this?
The inheritance strategy you've chosen is Table per Type (TPT).
You've got three types: one base type BaseEntity and two derived types EntityA and EntityB. You decided to put them into three separate tables. The BaseEntity properties of both EntityA and EntityB will be put in one table. EntityA and EntityB each have a foreign key to their base properties in the BaseEntity table.
Whether this inheritance strategy is the best for your problem depends on whether you will mostly query for "BaseEntities that ..." or for "EntityA that ..." and "EntityTyB that ..."
Could it be that using Table per concrete class (TPC) would be more suitable for your problem?
If you choose TPT inheritance strategy, then for every query "EntityA with some base class properties that ..." will need a join.
If you'd use TPC this join would not be needed. However TPC has the disadvantage that a Concat between the two tables is required whenever you ask for "BaseEntities that ...".
So it depends what kind of queries you will do most often which inheritance strategy is the best for your needs.
If you want to stick to strategy TPT, it seems that you don't build your model correctly.
You don't want anyone to store BaseEntity objects on its own. If you'd allow that, it wouldn't be inheritance, but one-to-zero-or-one relation: every EntityA belongs to one BaseEntity, and every BaseEntity has zero or one 'EntityA. This is not what you want: everyBaseEntityhas exactly either oneEntityAor one 'EntityB, and every 'EntityA / EntityB has exactly one 'BaseEntity`
As you don't want to storeBaseEntity objects without the derived class, 'BaseEntity` class ought to be declared abstract, as in the given link for TPT.
In the class definitions of EntityA en EntityB, don't mention the foreign key to the BaseEntity table. Again, see the given link to the TPT
I think the abstract base class and the lack of foreign keys are the
key information to let entity framework know that you chose
inheritance strategy TPT.
In your model building, only mention the table names and if needed the column names. Don't mention the foreign keys
When I built your model like this, entity framework created the three tables like TPT. any extra fluent API nor attributes were needed. Even though I didn't mention the foreign keys, Entity Framework knew they were needed as a polymorphic association. Again see the link to TPT
By the way, is the [Required] on your integers useful? Even if I wanted to, I couldn't give this integers a null value. Didn't you mean [Key]? As you are following the entity framework code first conventions even that is not necessary.

Entity Framework - Multiple 1 to 0..1 relationships using the same Key

I've read as many posts as I can on this topic but none of the solutions I have tried seem to work. I have an existing database and created a new Code First From Existing Database project.
I have a base table called Thing. Every object has a record in this table using Id as the Unique Primary Key. Each other object inherits from this but they use the same Id in the child tables without using a new Identity column in the sub tables. Effectively giving each 'Thing' a unique Id:
public class Thing
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Car
{
public int Id { get; set; }
//other properties
}
public class Person
{
public int Id { get; set; }
//other properties
}
public class Color
{
public int Id { get; set; }
//other properties
}
Every new record first creates an item in 'Thing' and then using that Id value creates a new record in its respective table, creating multiple 1 to 0..1 relationships where the Id field on the derived tables is also the FK to Thing.
Thing 1 to 0..1 Car
Thing 1 to 0..1 Person
Thing 1 to 0..1 Color
and so on
I have tried many different Data Annotation and Fluent API combinations but it always comes back to the same error:
'Unable to retrieve metadata for Model.Car'. Unable to determine the principal end of association between the types 'Model.Thing' and 'Model.Car'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.'
I did manage to get past this error by using virtual with the inverse annotation and setting the Id field to be Key and ForeignKey, but then the message jumps to Person. If you then set it up the same as Car the message reverts back to Car.
It seems I could go back and create a normal Foreign Key to each child table, but that is a lot of work and I am sure it is possible to get this working somehow. Preferably using fluent API.
If you are going to use Data Annotations, you need to declare the PK of the dependent entity as FK too:
public class Thing
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Car Car{get;set;}
}
public class Car
{
[Key,ForeignKey("Thing")]
public int ThingId { get; set; }
//other properties
public virtual Thing Thing{get;set;}
}
And if you are going to use Fluent Api (remove the attributes from your model), the configuration would be like this:
modelBuilder.Entity<Car>().HasRequired(c=>c.Thing).WithOptional(t=>t.Thing);
Based on the multiplicity that is specified, it only makes sense for Thing to be the principal and Car to be the dependent, since a Thing can exist without a Car but a Car must have a Thing.
As you can see you don't need to specify that ThingId is the FK of this relationship.This is because of Entity Framework’s requirement that the primary key of the dependent be used as the foreign key. Since there is no choice, Code First will just infer this for you.
Update
Reading again your question I think you are trying to create a hierarchy. In that case you could use the Table per Type (TPT) approach.

Define Many Tables to One Table relationship in Code First approach

I am in the process of building up a data model in Entity Framework using the Code First approach, but one part has me a bit stumped. The title on this question may be a bit confusing, so I will explain my problem in detail. The length of this post may be daunting, but I think it's a fairly straightforward problem.
I have one model defined like this:
public class KeyValuePair
{
[Key]
[MaxLength(128)]
[Column(Order = 0)]
public virtual string OwnerId { get; set; }
[Key]
[MaxLength(128)]
[Column(Order = 1)]
public virtual string Key { get; set; }
public virtual string Value { get; set; }
}
My intent is for this to just define a generic table for storing key-value properties on other entities in the system. I am using GUIDs for all of my Ids, so OwnerId should uniquely refer to one entity in the system, and the pair (OwnerId, Key) should uniquely identify one property on one entity.
In other words, I want to allow multiple tables in my system to have a One->Many relationship to this KeyValuePair table.
So for example, if I wanted to store the height of a Person who has the ID b4fc3e9a-2081-4989-b016-08ddd9f73db0, I would store a row in this table as:
OwnerId = "b4fc3e9a-2081-4989-b016-08ddd9f73db0"
Key = "Height"
Value = "70 in."
So now I want to define navigation properties from the parent entities to this table, like (to take the Person example):
public class Person
{
[Key]
public virtual string Id { get; set; }
public virtual string Name { get; set; }
// I want this to be a navigation property
public ICollection<KeyValuePair> Properties { get; set; }
}
But I'm not sure how do define the relationship between Person and KeyValuePair so that Entity Framework knows that it should look up the Person's properties by matching the Person's Id against the KeyValuePairs' OwnerId. I can't define a foreign key in the KeyValuePair model, because the OwnerId is going to refer to Ids in several different tables.
It looks like I can do the following to define a relationship from Person to KeyValuePair in OnModelCreating:
modelBuilder.Entity<Person>()
.HasMany(p => p.Properties).WithMany().Map(mp =>
{
mp.MapLeftKey("Id");
mp.MapRightKey("OwnerId", "Key");
mp.ToTable("PersonDetail");
});
Or I could even give the KeyValuePairs their own unique IDs, get rid of OwnerId, and do this:
modelBuilder.Entity<Person>()
.HasMany(p => p.Properties).WithMany().Map(mp =>
{
mp.MapLeftKey("Id");
mp.MapRightKey("Id");
mp.ToTable("PersonDetail");
});
But both of these approaches involve the creation of an intermediary table to link the Person and KeyValuePair tables, and that seems like excessive overhead in terms of bloating my database schema and requiring more expensive JOINs to query the data.
So is there a way to define the relationship such that I don't need to involve intermediary tables? Am I going about this database design the wrong way?
Side note: For anyone wondering why I am using this approach to define properties on my entities rather than simply adding fixed properties to the data model, I am using fixed properties in the data model where applicable, but the application I am building requires the ability to define custom properties at runtime. I also think this question is applicable to other potential scenarios where multiple tables have a One->Many relationship to a shared table.
The only way I can think of doing it (and I'll admit, this is not the best of ideas, but it will do what you're asking) would be to have any classes that need to have this relationship with KeyValuePair implement an abstract class that contains the fully implemented navigational property, as well as the ID field. By "fully implemented" I don't mean an actual, mapped relationship; I mean that it should use a DbContext to go out to the KeyValuePair table and actually grab the relevant properties given the ID.
Something like this:
public abstract class HasKeyValuePairs
{
[Key]
public virtual string Id { get; set; }
[NotMapped]
public ICollection<KeyValuePair> Properties
{
get
{
using(var db = new DbContext())
{
return db.KeyValuePairs.Where(kvp => kvp.OwnerID == this.ID);
}
}
}
}
Assuming you're using Lazy Loading (given that you're using the virtual keyword), there shouldn't be much extra overhead to doing it like this, since EF would have to go back to the database anyway to pick up the properties if you ever called for them. You might need to have that return a List just to avoid any potential ContextDisposedException later on in your code, but that at least will get you up and running.

Entity framework map entity to multiple tables in cascade

I'm facing a problem using EF.
I have the following situation:
From this database schema i'd like to generate the following entity by merge tables data:
// Purchases
public class Purchase
{
//Fields related to Purchases
public int IdPurchase { get; set; }
public string CodPurchase { get; set; }
public int IdCustomer { get; set; }
public decimal Total { get; set; }
//Fields related to Customers table
public string CodCustomer { get; protected set; }
public string CompanyTitle { get; protected set; }
public string CodType { get; protected set; }
//Fields related to CustomersType table
public string DescrType { get; protected set; }
}
As you can see, in my context i don't want 3 separated entities for each table. I want a single one with the fields related to all tables. All fields of Customers and CustomersType tables must be readonly (so i've set the relative setters protected) and the others must be editables so that EF can track changes. In particular, i'd like to have the ability to change the "IdCustomer" field and let EF to automatically update "CodCustomer", "CompanyTitle", "DescrType"....and so on by doing cross table select.
To do that, i wrote this configuration class:
internal class PurchaseConfiguration : EntityTypeConfiguration<Purchase>
{
public PurchaseConfiguration(string schema = "dbo")
{
ToTable(schema + ".Purchases");
HasKey(x => x.IdPurchase);
Property(x => x.IdPurchase).HasColumnName("IdPurchase").IsRequired().HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Property(x => x.IdCustomer).HasColumnName("IdCustomer").IsRequired();
Property(x => x.Total).HasColumnName("Total").IsRequired().HasPrecision(19, 4);
Map(mc =>
{
mc.Properties(n => new
{
n.CodCustomer,
n.CompanyTitle,
n.CodType
});
mc.ToTable("Customers");
});
Map(mc =>
{
mc.Properties(n => new
{
n.DescrType,
});
mc.ToTable("CustomersType");
});
}
}
I've tested it but it doesn't work as expected. I always get this message:
Properties for type 'Purchase' can only be mapped once. The non-key
property 'CodCustomer' is mapped more than once. Ensure the
Properties method specifies each non-key property only once.
Maybe there's something wrong or i forget something (for example the join fields of Map<> that i don't know where to specify them).
How can i accomplish in the correct way this task?
I don't want to have "Customers" and "CustomersType" DBSets in my context.
Is there a way to avoid it?
I even thought to add into the "IdCustomer" setter a custom query to update manually "Customers" and "CustomersType" related fields, but i don't want to do that for 2 reasons:
I don't have any DbConnection avaiable into the "Purchases" class, so i can't create a DbCommand to read data from DB.
I want entity class to be persistent-ignorant
EF seems to be a powerfull tool that can do these sort of things and i don't want to reinvent the wheel by writing custom procedures.
I've uploaded the example C# source and the tables CREATE scripts (MS SQLServer) here.
All entities are autogenerated by the "EF reverse POCO generator" T4 template (the T4 template is disabled, to activate it set CustomTool = TextTemplatingFileGenerator).
Do not forget to update the ConnectionString in the app.config.
Thanks in advance.
Not the right mapping
I'm afraid the bad news is that this mapping is not possible with this table structure. What you're trying to achieve here is known as entity splitting. However, entity splitting requires 1:1 associations, because sets of records in the involved tables represent one entity. With this mapping, you can't have a Customer belonging to more than one Purchase. That would mean that you could modify multiple Purchase entities by modifying a Customer property of only one of them.
Maybe the news isn't that bad, because I think you actually want to have 1-n associations. But then you can't have these "flattened" properties in Purchase.
As an alternative you could create delegated properties like so:
public string CodCustomer
{
get { return this.Customer.CodCustomer; }
set { this.Customer.CodCustomer = value; }
}
You'd have to Include() Customers and CustomersTypes when you fetch Purchases.
Another alternative is to use a tool like AutoMapper to map Purchase to a DTO type having the flattened properties.
But what does the exception tell me?
You map the Purchase entity to the Purchases table. But you don't specify which properties you want to map to this table. So EF assumes that all properties should be mapped to it. So that's the first (implicit) mapping of CodCustomer. The second one is the one in the mc.ToTable statement. (EF only reports the first problem.)
To fix this, you should add a mapping statement for the left-over Purchase properties:
Map(mc =>
{
mc.Properties(n => new
{
n.IdPurchase,
n.CodPurchase,
n.IdCustomer,
n.Total,
});
mc.ToTable("Purchases");
});
By the way, you should also remove the mapping configuration classes of Customer and CustomersType, they're redundant.
But, as said, the database schema doesn't match the required structure. If you try to save a Purchase you will get a foreign key constraint exception. This is because EF expects the following table structure:
Where the columns IdPurchase in Customer and CustomersType are both primary key and foreign key to Purchase. I don't think this is what you had in mind when designing the database.

Categories