Dynamic Mapping with NHibernate
A while ago I posted how to handle dynamic mapping with Active Record, it was incredibly easy to do, because Active Record has a lot of smarts internally, and output the XML, on top of which NHibernate adds quite a bit of convention over configuration as well. Doing the same using NHibernate directly is possible, but a bit long winded. Here is the sample code, which link all the Employee properties to the correct entity:
Configuration cfg = new Configuration() .AddAssembly(typeof (Employee).Assembly) .AddAssembly(typeof(ScheduledTask).Assembly); Mappings mappings = cfg.CreateMappings(); foreach (PersistentClass persistentClass in mappings.Classes) { if (persistentClass.MappedClass.GetProperty("Employee") == null) continue; Property prop = new Property(); PersistentClass employeeClass = cfg.GetClassMapping(typeof (Employee)); Table table = employeeClass.Table; ManyToOne value = new ManyToOne(table); value.ReferencedEntityName = typeof (Employee).FullName; Column column = new Column("Employee"); value.AddColumn(column); prop.Value = value; prop.Name = "Employee"; prop.PersistentClass = employeeClass; persistentClass.AddProperty(prop); persistentClass.Table.AddColumn(column); persistentClass.Table.CreateForeignKey("FK_EmployeeTo" + persistentClass.MappedClass.Name, new Column[] {column,}, typeof (Employee).FullName); } cfg.BuildSessionFactory(); new SchemaExport(cfg).Execute(true, true, false, true);
As you can see, there is a lot that needs to be done, we have to tell NHibernate a lot of things it would generally be able to figure out on its own. We can shove this to an extension method and get really nice syntax:
public static void MapManyToOne<TEntityInterface, TEntity>(this Configuration cfg) { Mappings mappings = cfg.CreateMappings(); foreach (PersistentClass persistentClass in mappings.Classes) { var propertyNames = new List<string>(); foreach (PropertyInfo property in persistentClass.MappedClass.GetProperties()) { if (property.PropertyType == typeof (TEntityInterface)) { propertyNames.Add(property.Name); } } if (propertyNames.Count == 0) continue; var prop = new Property(); PersistentClass targetClass = cfg.GetClassMapping(typeof (TEntity)); foreach (string propertyName in propertyNames) { Table table = targetClass.Table; var value = new ManyToOne(table); value.ReferencedEntityName = typeof (TEntity).FullName; var column = new Column(propertyName); value.AddColumn(column); prop.Value = value; prop.Name = propertyName; prop.PersistentClass = targetClass; persistentClass.AddProperty(prop); persistentClass.Table.AddColumn(column); string fkName = string.Format("FK_{0}To{1}", propertyName, persistentClass.MappedClass.Name); persistentClass.Table.CreateForeignKey(fkName, new[] {column,}, typeof (TEntity).FullName); } } }
Now we can use this with the following syntax:
cfg.MapManyToOne<IEmployee, Employee>();
Which is much nicer.
Comments
Nicely done.
Is the other post you are referring to this one
http://ayende.com/Blog/archive/2008/01/11/Active-Record-Mapping-Rewriting.aspx
or is was there another?
Yes, that is the post
What version of Nhibernate are you using? I'm using 2.0 Alpha and NHibernate.cfg.Mappings does not have a 'Classes' property...
It has now
Good stuff as always!
What version of Nhibernate are you using? I don't have a 'Classes' property...
Good stuff. So it is possible to create the entities by dynamic xml schema.
Comment preview