I have a dependency to several external suppliers of Data. Each supplier have very different data model. I map this to an internal data structure.
For simplicty Lets call the data from Supplier for Item
So I have a Item table which is the base table for my TPT
Basicly it only Holds the Id and the Name
I then have Sub tables that "derive" from the Item table
This works nice with TPT, but for some of the suppliers one sub table is not enough. I want to use TPC for these sub tables otherwise there will be so many joins etc.
Configs looks like this, given name of example data supplier is Foo
public class ItemConfig : EntityTypeConfiguration<Item>
{
public ItemConfig()
{
HasKey(k => k.Id);
ToTable("Item");
}
}
public class FooItemConfig : EntityTypeConfiguration<FooItem>
{
public FooItemConfig()
{
Property(cst => cst.Code) //External Identifier for supplier Foo
.HasMaxLength(50)
.IsRequired();
//Because of TPC this config does not map to a table
}
}
public class ConcreteFooItemConfig : EntityTypeConfiguration<ConcreteFooItem>
{
public ConcreteFooItemConfig()
{
Map(m => m.MapInheritedProperties());
ToTable("ConcreteFooItem");
}
}
Looks pretty straight forward, but I get
Additional information: The type 'ConcreteFooItem' 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.
edit: I can change FooItem to an interface IFooItem and that will work,but its not really an option because I want to be able to do queries on FooItem level from domain logic that is specific for FooItem
Related
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.
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.
I've got an strange problem with TPC inheritance using C# Entity Framework Codefirst and Fluent Api.
I have 3 Classes named Person, Invoice and PeriodicInvoice as you can see below.
Here is a summary of my code:
Invoice class and its configuration class:
public class Invoice : InvoiceBase
{
public Person User { get; set; }
}
public class InvoiceConfig : EntityTypeConfiguration<Invoice>
{
public InvoiceConfig()
{
this.Map(m => { m.MapInheritedProperties(); m.ToTable("Invoices"); });
}
}
PeriodicInvoice class and its configuration:
public class PeriodicInvoice : InvoiceBase
{
// Some extra properties.
}
public class PeriodicInvoiceConfig : EntityTypeConfiguration<PeriodicInvoice>
{
public PeriodicInvoiceConfig()
{
this.Property(x => x.SuspendOnExpire).IsRequired();
this.Map(m => { m.MapInheritedProperties(); m.toTable("PeriodicInvoices"); });
}
}
When I run the code, this error appears:
The association 'Invoice_User' between entity types 'Invoice' and 'Person' is invalid. In a TPC hierarchy independent associations are only allowed on the most derived types.
I know it means that I should include the property User to class PeriodicInvoice and don't use it in class Invoice.
But, Isn't there any other way to solve this problem?
Thanks.
In TPC inheritance you can't have a field in parent class that points to another table because you are trying to point two tables to another table and one table that tries to point to one of these two tables using only one foreign key (and that's impossible!).
I suggest you to use TPT. This link can help you.
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.
I am trying to use the automapping feature of Fluent with nHinbernate to map a class with a different name than the table itself is name.
(This is purely for stylistic reasons we have a class named Foo which contains an object named Bar but the table name is FooBar. We would rather not have a property Foo.FooBar.)
I can't find anything detailing how to give Fluent a clue on this change.
With classmap you can specify the table name in the mapping.
public class BarMap : ClassMap<Bar>
{
public BarMap()
{
Table("FooBar");
}
}
With automap you can override the table name.
.Mappings( m => m.AutoMappings.Add( AutoMap.AssemblyOf<Bar>()
.Override<Bar>( b => {
b.Table("FooBar");
}))
You can also use conventions to affect table naming of all entities.
You can specify the table name in the mapping. So it will look something like this:
public class FooMap : ClassMap<Foo>
{
Table("FooBar");
// Rest of your mapping goes here.
}