1. Introduction
Writing mappings for models in large application is quite boring task. Fortunatelly, Fluent NHibernate provides possibility for automatic mapping creation – so called automappings.
2. Creating database and model
In order to show you, how to configure automappings in Fluent NHibernate, let’s create simple database along with model classes. The database structure is presented in the picture below.
Model classes which represent database tables look this way
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public abstract class ModelBase { public virtual int Id { get; set; } } public class Project : ModelBase { public virtual string Name { get; set; } public virtual User User { get; set; } public virtual IList<Task> Tasks { get; set; } } public class Task : ModelBase { public virtual string Name { get; set; } public virtual Project Project { get; set; } } public class User : ModelBase { public virtual string Name { get; set; } public virtual string Surname { get; set; } } |
3. Automappings configuration
Having our database and models prepared, now we can create and configure SessionFactory to use automappings.
1 2 3 |
var factory = Fluently.Configure().Database(MsSqlConfiguration.MsSql2008.ConnectionString("YourConnectionString")) .Mappings(val => val.AutoMappings.Add(AutoMap.AssemblyOf<Project>)) .BuildSessionFactory(); |
As You can see I used the static AutoMap.AssemblyOf
4. Mapping only specified classes
Mapping all classes from specyfic assembly may not be very useful. That is why, we need to “tell” Automapper which classes should be mapped. It can be achieved by creating custom configuration class which implements IAutomappingConfiguration interface or which is subclass of DefaultAutomappingConfiguration. IAutomappingConfiguration interface has quite a lot of functions, that is why I decided to craete class which inherits from DefaultAutomappingConfiguration and override only one method – ShouldMap
1 2 3 4 5 6 7 |
public class AutomappingConfiguration : DefaultAutomappingConfiguration { public override bool ShouldMap(Type type) { return type.IsSubclassOf(typeof (ModelBase)); } } |
As You can see by overriding ShouldMap function,I specified that only classes which inherit from ModelBase class should be mapped. To use our new configuration, we need to pass an instance of it to AutoMap setup
1 2 3 |
var factory = Fluently.Configure().Database(MsSqlConfiguration.MsSql2008.ConnectionString("YourConnectionString")) .Mappings(val => val.AutoMappings.Add(AutoMap.AssemblyOf<Project>(new DefaultAutomappingConfiguration()))) .BuildSessionFactory(); |
5. Defining custom conventions
It is rather obvious that default automapping conventions may not come along with our database naming conventions. Fortunately we can create custom conventions(which override default ones), and pass them to AutoMap configuration. Here are examples of conventions I use IClassConvention – gives us access to properties and functions which allow us to change a default table name format for our entities.
1 2 3 4 5 6 7 |
public class DefaultTableNameConvention : IClassConvention { public void Apply(IClassInstance instance) { instance.Table(string.Format("{0}{1}", "GL_", instance.EntityType.Name)); } } |
IIdConvention is used for altering default identity conventions.
1 2 3 4 5 6 7 8 |
public class DefaultPrimaryKeyConvention : IIdConvention { public void Apply(IIdentityInstance instance) { instance.Column("Id"); instance.GeneratedBy.Native(); } } |
IPropertyConvention – allows us to modify properties mappings (lazy load, nullability,length etc)
1 2 3 4 5 6 7 |
public class DefaultStringLengthConvention : IPropertyConvention { public void Apply(IPropertyInstance instance) { instance.Length(50); } } |
IReferenceConvention – allows us to modify entities relationship convention
1 2 3 4 5 6 7 8 9 |
public class DefaultReferenceConvention : IReferenceConvention { public void Apply(IManyToOneInstance instance) { instance.Column(string.Format(instance.Class.Name.StartsWith("Id") ? "{1}" : "{0}{1}", "Id", instance.Class.Name)); instance.LazyLoad(); } } |
IHasManyConvention – allow us to modify default has-many relationship convention
1 2 3 4 5 6 7 8 |
public class DefaultHasManyConvention : IHasManyConvention { public void Apply(IOneToManyCollectionInstance instance) { instance.Key.Column(string.Format("{0}{1}", "Id", instance.EntityType.Name)); instance.LazyLoad(); } } |
In order to use new conventions we need to pass them into AutoMap configuration.
1 2 3 4 5 6 7 8 9 10 11 12 |
Fluently.Configure() .Database( MsSqlConfiguration.MsSql2008.ConnectionString("YourConnectionString")) .Mappings(val => val.AutoMappings.Add(AutoMap.AssemblyOf<Project>(new DefaultAutomappingConfiguration()).Conventions.Setup(con => { con.Add<DefaultTableNameConvention>(); con.Add<DefaultPrimaryKeyConvention>(); con.Add<DefaultStringLengthConvention>(); con.Add<DefaultReferenceConvention>(); con.Add<DefaultHasManyConvention>(); }))) .BuildSessionFactory(); |
6. Overriding mappings
Sometimes it is necessary to slightly modify entity mapping. It can be achieved by creating class which implements IAutoMappingOverride interface
1 2 3 4 5 6 7 |
public class ProjectMapping: IAutoMappingOverride<Project> { public void Override(AutoMapping<Project> mapping) { mapping.IgnoreProperty(val=>val.SomeIgnoredProperty); } } |
Override method gives us access to all actions known from fluent mappings. After creating class map, we have to call function UseOverridesFromAssemblyOf in our automapping configuration
1 2 3 4 5 6 7 8 9 10 |
var factory = Fluently.Configure().Database(MsSqlConfiguration.MsSql2008.ConnectionString("YourConnectionString")) .Mappings(val => val.AutoMappings.Add(AutoMap.AssemblyOf<Project>(new DefaultAutomappingConfiguration()).UseOverridesFromAssemblyOf<Project>() .Conventions.Setup(con => { con.Add<DefaultTableNameConvention>(); con.Add<DefaultPrimaryKeyConvention>(); con.Add<DefaultStringLengthConvention>(); con.Add<DefaultReferenceConvention>(); con.Add<DefaultHasManyConvention>(); }))).BuildSessionFactory(); |
7. It works
Here just couple of screens from NHibernateProfiler with basic queries
Source code for this post can be found here
Nice Explanation
Thank You
Hello,
I am getting the following error in fluent Nhibernate:
No persister for: System.Data.Entity.DynamicProxies.Customer_3FF392FD8867C34D66C0BEB65D29B9B13BAFD2651E0DD73A797E67ADE1455B21
Can you please help me to resolve this.
Hi, show me some source code. I'll try to help
Hi,
Where can I download the full source code for this tutorial?
The source code can be found here
https://github.com/tpodolak/Blog/tree/master/NHibernateAutomappings
Hi, Many to One child records update not working. update also inserting records