-
-
Save NOtherDev/1569982 to your computer and use it in GitHub Desktop.
// 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; | |
} | |
} |
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.
@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!)
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).
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.