Fluent: Table name different from entity name - c#

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.
}

Related

Duplicate ForeignKey when using inheritance

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.

An Entity with identical table data

Before I elaborate the problem, I'm well aware the database isn't designed conventionally. Sadly, I can't change this particular database due to how it is integrated, so I've got a potential solution but that won't be implemented for several months. In the mean time I need to work around the following:
The problem is I need to build an Entity, this would represent our Accounts. But the problem, our database implements the following structure:
Invoiced Table
Non-Invoiced Table
My Entity, represents the exact same data on those tables, same column names, duplicate under all conditions, except one is invoiced while the other represents non-invoiced customers. But since it isn't one table, with a Flag to indicate invoiced versus non-invoiced, how can my Entity link to both of those tables?
Since both tables represent separate names, I can't use the [Table("...")] or the auto mapping capabilities. I hate asking such a question, but I can't find any documentation on how to handle such an issue.
You could use table-per-concrete class inheritance then define the table names on the derived types:
public abstract class Account
{
// common entity code here
...
}
public class InvoicedAccount : Account {}
public class NonInvoicedAccount: Account {}
public YourContext : DbContext
{
public DbSet<InvoicedAccount> InvoicedAccounts { get; set; }
public DbSet<NonInvoicedAccount> NonInvoicedAccounts { get; set; }
protected override void OnModelCreating( DbModelBuilder modelBuilder )
{
modelBuilder.Entity<InvoicedAccounts>().Map( m =>
{
m.MapInheritedProperties();
m.ToTable( "InvoicedAccountTable" );
} );
modelBuilder.Entity<NonInvoicedAccounts>().Map( m =>
{
m.MapInheritedProperties();
m.ToTable( "NonInvoicedAccountTable" );
} );
}
}

TPC Inheritance Error

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.

Can I combine TPT and TPC inheritance?

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

Fluent NHibernate error: The entity 'ClassMap`1' doesn't have an Id mapped

I'm converting a previous project from using normal NHibernate hbm.xml mappings to Fluent NHibernate. Currently, I'm stuck on what should be one of the last steps to getting this working. I've added a derived class for DefaultAutomappingConfiguration to modify my ID naming convention. The string "Id" is appended to the class name:
public override bool IsId(FluentNHibernate.Member member)
{
return member.Name == member.DeclaringType.Name + "Id";
}
This should make "Agency" have an ID in a field named "AgencyId". Instead, I'm getting this error:
The entity 'ClassMap`1' doesn't have an Id mapped. Use the Id method to map your identity property. For example: Id(x => x.Id).
{Name = "ClassMap`1" FullName = "FluentNHibernate.Mapping.ClassMap`1[[BackendDb.Model.Agency, BackendDb, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]"}
I made a breakpoint on the IsId function to see what's going on:
{Property: Cache}
{Name = "ClassMap`1" FullName = "FluentNHibernate.Mapping.ClassMap`1[[BackendDb.Model.Agency, BackendDb, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]"}
What is this? The object is not something I've created. Every other object passes through this function fine, and the ones I actually wanted to map are returning the proper value.
My Session factory looks something like this:
var cfg = new MapConfig();
return Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008
.ConnectionString(m => m.Server(#".\SqlExpress")
.Database("{some dbname}")
.TrustedConnection()))
.Mappings(m =>
m.AutoMappings
.Add(AutoMap.AssemblyOf<Agency>(cfg))
)
.BuildSessionFactory();
Annoyingly, it seems that this somehow caused the three tables I was testing Fluent NHibernate on in my dev database to be emptied. What the hell?
The sessionfactory is trying to automap all classes in the assembly that contains your Agency class based on this directive: Add(AutoMap.AssemblyOf<Agency>(cfg)). Since you have an AgencyMap in the assembly andClassMap<> does not have an Id property, FNH is throwing an error.
If you want to use ClassMap<> configurations, instead of (or in addition to) declaring an automapping configuration, declare a fluent mapping:
m.FluentMappings.AddFromAssemblyOf<Agency>();
If you don't need AutoMappings, remove the `.AutoMappings.Add' directive.
However, if you want to use AutoMappings, you need to tell FNH what classes you want to map. To handle this, I usually define a marker interface:
public abstract class Entity : IPersistable
{
public virtual int Id { get; set; }
}
public interface IPersistable
{
}
Then, in the class that I derive from DefaultAutomappingConfiguration, I tell FNH to only map the classes that have that interface (you can limit the mapped classes however you see fit):
public class EntityAutoMappingConfiguration : DefaultAutomappingConfiguration
{
public override bool ShouldMap(Type type)
{
return type.GetInterfaces().Contains(typeof (IPersistable));
}
}
To handle the primary key mapping, I create a convention class:
public class PrimaryKeyNamePlusId : IIdConvention
{
public void Apply(IIdentityInstance instance)
{
instance.Column(instance.EntityType.Name+"Id");
}
}
Finally, I configure my SessionFactory to use the configuration/convention classes:
m.AutoMappings.AssemblyOf<Entity>(new EntityAutoMappingConfiguration())
.IgnoreBase<Entity>()
.UseOverridesFromAssemblyOf<Entity>()
.Conventions.AddFromAssemblyOf<Entity>();
You can't use ClassMap in combination with the automapper unless you also configure the automapper to ignore the Entites for which you are using ClassMap and their respective mapping files.
In my case, I happen to use a custom attribute to indicate classes which should be automapped, so I can toss all sorts of garbage I don't want mapped into my .dll without having Fluent try to automap it:
/// <summary>
/// Add this attribute to entity classes which should be automapped by Fluent.
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
class AutomapAttribute : Attribute
{
}
And in my DefaultAutomappingConfiguration override class:
public override bool ShouldMap(Type type)
{
return (type.Namespace == "Data.Entities"
&& type.GetCustomAttributes(typeof(AutomapAttribute), false).Length > 0);
}
Of course, you don't need to check for an attribute if you just keep your automapped entities in a different namespace from other classes.

Categories