В моем случае я хотел использовать атрибут, чтобы указать свойство, которое должно участвовать в отношении «многие ко многим», где объявлена только одна сторона отношения. Вы можете легко изменить это, чтобы сопоставить с другими соглашениями.
Отношения «многие ко многим» обрабатываются FluentNHibernate.Automapping.Steps.HasManyToManyStep
, IAutomappingStep
возвращается DefaultAutomappingConfiguration
. Этот шаг отобразит свойство только в том случае, если он обнаружит соответствующее свойство связанного типа (поэтому необходимо объявить оба конца отношения «многие ко многим»).
Я выбрал следующий подход:
- Создание класса декоратора для
HasManyToManyStep
, который поддерживает обнаружение и отображение свойств многие-ко-многим на основе наличия атрибута (или какого-либо другого соглашения)
- Создайте класс, производный от
DefaultAutomappingConfiguration
до того момента, когда автонастроит и переопределит GetMappingSteps
, обернув любой экземпляр HasManyToManyStep
декоратором
Вот декоратор, который сначала пытается использовать функциональность HasManyToManyStep
по умолчанию. В противном случае, если для члена определено HasManyToManyAttribute
, это также создаст связь. Код, используемый для создания отношения, почти идентичен коду, используемому HasManyToManyStep
- просто без ссылки на другую сторону отношения.
class ExplicitHasManyToManyStep : IAutomappingStep
{
readonly IAutomappingConfiguration Configuration;
readonly IAutomappingStep DefaultManyToManyStep;
public ExplicitHasManyToManyStep(IAutomappingConfiguration configuration, IAutomappingStep defaultManyToManyStep)
{
Configuration = configuration;
DefaultManyToManyStep = defaultManyToManyStep;
}
#region Implementation of IAutomappingStep
public bool ShouldMap(Member member)
{
if (DefaultManyToManyStep.ShouldMap(member))
{
return true;
}
//modify this statement to check for other attributes or conventions
return member.MemberInfo.IsDefined(typeof(HasManyToManyAttribute), true);
}
public void Map(ClassMappingBase classMap, Member member)
{
if (DefaultManyToManyStep.ShouldMap(member))
{
DefaultManyToManyStep.Map(classMap, member);
return;
}
var Collection = CreateManyToMany(classMap, member);
classMap.AddCollection(Collection);
}
#endregion
CollectionMapping CreateManyToMany(ClassMappingBase classMap, Member member)
{
var ParentType = classMap.Type;
var ChildType = member.PropertyType.GetGenericArguments()[0];
var Collection = CollectionMapping.For(CollectionTypeResolver.Resolve(member));
Collection.ContainingEntityType = ParentType;
Collection.Set(x => x.Name, Layer.Defaults, member.Name);
Collection.Set(x => x.Relationship, Layer.Defaults, CreateManyToMany(member, ParentType, ChildType));
Collection.Set(x => x.ChildType, Layer.Defaults, ChildType);
Collection.Member = member;
SetDefaultAccess(member, Collection);
SetKey(member, classMap, Collection);
return Collection;
}
void SetDefaultAccess(Member member, CollectionMapping mapping)
{
var ResolvedAccess = MemberAccessResolver.Resolve(member);
if (ResolvedAccess != Access.Property && ResolvedAccess != Access.Unset)
{
mapping.Set(x => x.Access, Layer.Defaults, ResolvedAccess.ToString());
}
if (member.IsProperty && !member.CanWrite)
{
mapping.Set(x => x.Access, Layer.Defaults, Configuration.GetAccessStrategyForReadOnlyProperty(member).ToString());
}
}
static ICollectionRelationshipMapping CreateManyToMany(Member member, Type parentType, Type childType)
{
var ColumnMapping = new ColumnMapping();
ColumnMapping.Set(x => x.Name, Layer.Defaults, childType.Name + "_id");
var Mapping = new ManyToManyMapping {ContainingEntityType = parentType};
Mapping.Set(x => x.Class, Layer.Defaults, new FluentNHibernate.MappingModel.TypeReference(childType));
Mapping.Set(x => x.ParentType, Layer.Defaults, parentType);
Mapping.Set(x => x.ChildType, Layer.Defaults, childType);
Mapping.AddColumn(Layer.Defaults, ColumnMapping);
return Mapping;
}
static void SetKey(Member property, ClassMappingBase classMap, CollectionMapping mapping)
{
var ColumnName = property.DeclaringType.Name + "_id";
var ColumnMapping = new ColumnMapping();
ColumnMapping.Set(x => x.Name, Layer.Defaults, ColumnName);
var Key = new KeyMapping {ContainingEntityType = classMap.Type};
Key.AddColumn(Layer.Defaults, ColumnMapping);
mapping.Set(x => x.Key, Layer.Defaults, Key);
}
}
HasManyToManyAttribute
класс, потому что нет другого соглашения, на которое я могу легко положиться в моем случае:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class HasManyToManyAttribute : Attribute
{
}
Класс конфигурации, полученный из DefaultMappingConfiguration
class:
class AutomappingConfiguration : DefaultAutomappingConfiguration
{
public override IEnumerable<IAutomappingStep> GetMappingSteps(AutoMapper mapper, IConventionFinder conventionFinder)
{
return base.GetMappingSteps(mapper, conventionFinder).Select(GetDecoratedStep);
}
IAutomappingStep GetDecoratedStep(IAutomappingStep step)
{
if (step is HasManyToManyStep)
{
return new ExplicitHasManyToManyStep(this, step);
}
return step;
}
}