Last active
November 20, 2019 08:37
-
-
Save SamerX/e6726b9b0c5ecaf0bfd4d6611603afd9 to your computer and use it in GitHub Desktop.
NHibernate CustomHasManyToManyStep
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class CustomHasManyToManyStep : IAutomappingStep | |
{ | |
private readonly IAutomappingConfiguration cfg; | |
public CustomHasManyToManyStep(IAutomappingConfiguration cfg) | |
{ | |
this.cfg = cfg; | |
} | |
public bool ShouldMap(Member member) | |
{ | |
var type = member.PropertyType; | |
if (type.Namespace != "Iesi.Collections.Generic" && | |
type.Namespace != "System.Collections.Generic") | |
return false; | |
if (type.HasInterface(typeof(IDictionary)) || type.ClosesInterface(typeof(IDictionary<,>)) || type.Closes(typeof(System.Collections.Generic.IDictionary<,>))) | |
return false; | |
//SamerA: Tweak to allow unidirectional ManyToMany mapping along with the Bidirectional | |
return (IsBidirectionalManyToMany(member) || IsUnidirectionalManyToMany(member)); | |
} | |
static Member GetInverseCollectionProperty(Member member) | |
{ | |
var type = member.PropertyType; | |
var expectedInversePropertyType = type.GetGenericTypeDefinition() | |
.MakeGenericType(member.DeclaringType); | |
var argument = type.GetGenericArguments()[0]; | |
return argument.GetProperties() | |
.Select(x => x.ToMember()) | |
.Where(x => x.PropertyType == expectedInversePropertyType && x != member) | |
.FirstOrDefault(); | |
} | |
static bool IsBidirectionalManyToMany(Member member) | |
{ | |
var type = member.PropertyType; | |
var expectedInversePropertyType = type.GetGenericTypeDefinition() | |
.MakeGenericType(member.DeclaringType); | |
var argument = type.GetGenericArguments()[0]; | |
return argument.GetProperties() | |
.Select(x => x.ToMember()) | |
.Any(x => x.PropertyType == expectedInversePropertyType && x != member); | |
} | |
static bool IsUnidirectionalManyToMany(Member member) | |
{ | |
var type = member.PropertyType; | |
var argument = type.GetGenericArguments()[0]; | |
return argument.GetProperties() | |
.Select(x => x.ToMember()).All(x => x.PropertyType != member.DeclaringType && x != member); | |
} | |
public void Map(ClassMappingBase classMap, Member member) | |
{ | |
var inverseProperty = GetInverseCollectionProperty(member); | |
var parentSide = inverseProperty == null ? member.DeclaringType : cfg.GetParentSideForManyToMany(member.DeclaringType, inverseProperty.DeclaringType); | |
var mapping = GetCollection(member); | |
ConfigureModel(member, mapping, classMap, parentSide); | |
classMap.AddCollection(mapping); | |
} | |
static CollectionMapping GetCollection(Member property) | |
{ | |
var collectionType = CollectionTypeResolver.Resolve(property); | |
return CollectionMapping.For(collectionType); | |
} | |
void ConfigureModel(Member member, CollectionMapping mapping, ClassMappingBase classMap, Type parentSide) | |
{ | |
// TODO: Make the child type safer | |
mapping.SetDefaultValue(x => x.Name, member.Name); | |
mapping.Relationship = CreateManyToMany(member, member.PropertyType.GetGenericArguments()[0], classMap.Type); | |
mapping.ContainingEntityType = classMap.Type; | |
mapping.ChildType = member.PropertyType.GetGenericArguments()[0]; | |
mapping.Member = member; | |
SetDefaultAccess(member, mapping); | |
SetKey(member, classMap, mapping); | |
if (parentSide != member.DeclaringType) | |
mapping.Inverse = true; | |
} | |
void SetDefaultAccess(Member member, CollectionMapping mapping) | |
{ | |
var resolvedAccess = MemberAccessResolver.Resolve(member); | |
if (resolvedAccess != Access.Property && resolvedAccess != Access.Unset) | |
{ | |
// if it's a property or unset then we'll just let NH deal with it, otherwise | |
// set the access to be whatever we determined it might be | |
mapping.SetDefaultValue(x => x.Access, resolvedAccess.ToString()); | |
} | |
if (member.IsProperty && !member.CanWrite) | |
mapping.SetDefaultValue(x => x.Access, cfg.GetAccessStrategyForReadOnlyProperty(member).ToString()); | |
} | |
ICollectionRelationshipMapping CreateManyToMany(Member property, Type child, Type parent) | |
{ | |
var mapping = new ManyToManyMapping | |
{ | |
Class = new TypeReference(property.PropertyType.GetGenericArguments()[0]), | |
ContainingEntityType = parent | |
}; | |
mapping.AddDefaultColumn(new ColumnMapping { Name = child.Name + "_id" }); | |
return mapping; | |
} | |
void SetKey(Member property, ClassMappingBase classMap, CollectionMapping mapping) | |
{ | |
var columnName = property.DeclaringType.Name + "_id"; | |
var key = new KeyMapping(); | |
key.ContainingEntityType = classMap.Type; | |
key.AddDefaultColumn(new ColumnMapping { Name = columnName }); | |
mapping.SetDefaultValue(x => x.Key, key); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment