Lesser-Known NHibernate Features: Mapping By Convention
Did you know that NHibernate, like other O/RMs out there, allows you to map classes by convention? Yes, it’s true… Let me show you how!
First, you need to create an instance of the appropriately-named ConventionModelMapper:
var mapper = new ConventionModelMapper();
Next, you need to tell it where is the assembly (or assemblies) containing the entities you want to map:
var mappings = mapper.CompileMappingFor(typeof(MyEntity).Assembly.GetExportedTypes());
Finally, you need to add the generated mappings to a Configuration instance:
cfg.AddMapping(mappings);
That’s all it takes! Really!
Granted, this is very convenient, but we don’t know much what is happening inside; for example, what id generation strategy is it using? By default, it uses native, which means it will use the native strategy of the database engine currently being used – identity for SQL Server and MySQL, sequence for Oracle and PostgreSQL, etc. If you wish to override this, you certainly can:
mapper.BeforeMapClass += (modelInspector, type, classCustomizer) =>
{
classCustomizer.Id(x =>
{
x.Generator(Generators.HighLow);
});
};
This tells the mapper to use the high-low strategy for all entities.
What if you want to change the default naming of columns and tables? Well, you have two options:
- Provide an handler for the BeforeMapClass or BeforeMapProperty events and in it change the name of the physical object:
mapper.BeforeMapClass += (modelInspector, type, classCustomizer) =>
{
classCustomizer.Table(this.GetTableName(type.Name));
};
mapper.BeforeMapProperty += (modelInspector, member, propertyCustomizer) =>
{
propertyCustomizer.Column(this.GetColumnName(member.LocalMember.Name));
};
- Provide your own implementation of INamingStrategy to NHibernate:
public class CustomNamingStrategy : INamingStrategy
{
public String ClassToTableName(String className)
{
return className;
}
public String ColumnName(String columnName)
{
return columnName;
}
public String LogicalColumnName(String columnName, String propertyName)
{
return columnName;
}
public String PropertyToColumnName(String propertyName)
{
return propertyName;
}
public String PropertyToTableName(String className, String propertyName)
{
return propertyName;
}
public String TableName(String tableName)
{
return tableName;
}
}
cfg.SetNamingStrategy(new CustomNamingStrategy());
In general, you can trust NHibernate’s judgement, but if you wish, you can override other aspects of the mapping by providing handlers to the many events of ConventionModelMapper. Say, for example, that you want to exclude (or include) only certain classes from an assembly, you can provide an handler for the IsEntity event:
mapper.IsEntity += (type, @default, assemblyName) =>
{
return typeof(IEntity).IsAssignableFrom(type);
};
Or you want to configure a collection as a set rather than a bag:
mapper.IsBag += (member, @default) =>
{
return false;
};
mapper.IsSet += (member, @default) =>
{
return true;
};
Or even set a collection as not lazy and use inner join fetching:
mapper.BeforeMapSet += (modelInspector, member, propertyCustomizer) =>
{
propertyCustomizer.Lazy(CollectionLazy.NoLazy);
propertyCustomizer.Fetch(CollectionFetchMode.Join);
};