Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
NHibernate mapping-by-code naming convention resembling Fluent's
// NHibernate mapping-by-code naming convention resembling Fluent's
// See the blog post: http://notherdev.blogspot.com/2012/01/mapping-by-code-naming-convention.html
public class ModelMapperWithNamingConventions : ConventionModelMapper
{
public const string ForeignKeyColumnPostfix = "_id";
public const string ManyToManyIntermediateTableInfix = "To";
public const char ElementColumnTrimmedPluralPostfix = 's';
private readonly List<MemberInfo> _ignoredMembers = new List<MemberInfo>();
public ModelMapperWithNamingConventions()
{
BeforeMapManyToOne += (inspector, member, customizer) =>
customizer.Column(member.LocalMember.Name + ForeignKeyColumnPostfix);
BeforeMapManyToMany += (inspector, member, customizer) =>
customizer.Column(member.CollectionElementType().Name + ForeignKeyColumnPostfix);
BeforeMapElement += (inspector, member, customizer) =>
customizer.Column(member.LocalMember.Name.TrimEnd(ElementColumnTrimmedPluralPostfix));
BeforeMapJoinedSubclass += (inspector, type, customizer) =>
customizer.Key(k => k.Column(type.BaseType.Name + ForeignKeyColumnPostfix));
BeforeMapSet += BeforeMappingCollectionConvention;
BeforeMapBag += BeforeMappingCollectionConvention;
BeforeMapList += BeforeMappingCollectionConvention;
BeforeMapIdBag += BeforeMappingCollectionConvention;
BeforeMapMap += BeforeMappingCollectionConvention;
BeforeMapComponent += DisableComponentParentAutomapping;
IsPersistentProperty((m, d) => !_ignoredMembers.Contains(m));
}
private void DisableComponentParentAutomapping(IModelInspector inspector, PropertyPath member, IComponentAttributesMapper customizer)
{
var parentMapping = member.LocalMember.GetPropertyOrFieldType().GetFirstPropertyOfType(member.Owner());
DisableAutomappingFor(parentMapping);
}
private void DisableAutomappingFor(MemberInfo member)
{
if (member != null)
_ignoredMembers.Add(member);
}
private void BeforeMappingCollectionConvention(IModelInspector inspector, PropertyPath member, ICollectionPropertiesMapper customizer)
{
if (inspector.IsManyToMany(member.LocalMember))
customizer.Table(member.ManyToManyIntermediateTableName());
customizer.Key(k => k.Column(DetermineKeyColumnName(inspector, member)));
}
private static string DetermineKeyColumnName(IModelInspector inspector, PropertyPath member)
{
var otherSideProperty = member.OneToManyOtherSideProperty();
if (inspector.IsOneToMany(member.LocalMember) && otherSideProperty != null)
return otherSideProperty.Name + ForeignKeyColumnPostfix;
return member.Owner().Name + ForeignKeyColumnPostfix;
}
}
public static class PropertyPathExtensions
{
public static Type Owner(this PropertyPath member)
{
return member.GetRootMember().DeclaringType;
}
public static Type CollectionElementType(this PropertyPath member)
{
return member.LocalMember.GetPropertyOrFieldType().DetermineCollectionElementOrDictionaryValueType();
}
public static MemberInfo OneToManyOtherSideProperty(this PropertyPath member)
{
return member.CollectionElementType().GetFirstPropertyOfType(member.Owner());
}
public static string ManyToManyIntermediateTableName(this PropertyPath member)
{
return String.Join(
ModelMapperWithNamingConventions.ManyToManyIntermediateTableInfix,
member.ManyToManySidesNames().OrderBy(x => x)
);
}
private static IEnumerable<string> ManyToManySidesNames(this PropertyPath member)
{
yield return member.Owner().Name;
yield return member.CollectionElementType().Name;
}
}
Owner

NOtherDev commented Jan 6, 2012

See the blog post here for the description: http://notherdev.blogspot.com/2012/01/mapping-by-code-naming-convention.html
Feel free to use and/or modify the code.

Lukkian commented Jan 8, 2012

Tank you!

Just out of curiosity, what was your reasoning behind the following customization?

BeforeMapElement += (inspector, member, customizer) => 
    customizer.Column(member.LocalMember.Name.TrimEnd(ElementColumnTrimmedPluralPostfix));

Not sure if I can think of any use cases off the top of my head, but would love to be proven wrong :-)

Also, not sure what DisableComponentParentAutomapping does - I tested my component (both as a fild and collection of components) with this turned on and off and didn't notice much difference in the database.

Owner

NOtherDev commented Jul 13, 2012

@davidcie: This is probably useful when you have an entity Company with collection of elements, i.e. strings with names in property called Names. Without that, convention would generate a table for that strings with foreign key "Company_id" and value column called "Names". I prefer to have that column named "Name", as each row contains single name.

@NOtherDev That was my initial guess too, but from testing NHibernate didn't do that. (Tested with a collection of strings on a parent element mapped as Component().) Unless I'm testing too hastily and didn't check thorougly enough that is. (Thanks for coming back so quickly!)

Owner

NOtherDev commented Jul 13, 2012

Components are handled by BeforeMapComponent. This is for elements, single-column value types collections mapped with Element mapping. See here: http://notherdev.blogspot.com/2012/01/mapping-by-code-onetomany-and-other.html for some clues how it differ to components (multi-column value types).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment