Skip to content

Instantly share code, notes, and snippets.

@NOtherDev
Created January 6, 2012 10:17
Show Gist options
  • Save NOtherDev/1569982 to your computer and use it in GitHub Desktop.
Save NOtherDev/1569982 to your computer and use it in GitHub Desktop.
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;
}
}
@NOtherDev
Copy link
Author

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.

@luis-fss
Copy link

luis-fss commented Jan 8, 2012

Tank you!

@davidcie
Copy link

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.

@NOtherDev
Copy link
Author

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

@davidcie
Copy link

@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!)

@NOtherDev
Copy link
Author

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