问题描述
我使用Fluent NHibernate的AutoMap功能映射我的实体。我的大部分实体继承自一个基类实体
,它有一个属性 public IList< Tag>标签
。
标签位于数据库的一个单独的表格中,所以我使用了多对多的关系。但是,流利的NHibernate为一对多的关系创建映射。
我想写一个约定来覆盖这些映射使用 HasManyToMany (...)
如果类继承自 Entity
。这是可能的,怎么样?
约定可以依赖于属性的类型或名称
代码示例:
//实体
公共类实体
{
public virtual int Id {get;组; }
// ...其他一些属性
公共虚拟IList< Tag> {get;组; }
}
public class Tag
{
public virtual int Id {get;组; }
公共虚拟字符串TagName {get;组; }
}
public class事件:实体
{
// ...一些属性
}
// Fluent NHibernate配置
public static ISessionFactory CreateSessionFactory()
{
var config = new CustomAutomappingConfiguration();
return FULLntly.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString(c => c.FromConnectionStringWithKey(Sql)))
.Mappings(m =>
$ b m.AutoMappings.Add(AutoMap.AssemblyOf< Event>(config)
.IgnoreBase< Entity>()
.Conventions.Add< CustomForeignKeyConvention>()
();
.BuildSessionFactory();
}
在我的情况下,我想使用一个属性来表示一个属性,应该参与一个多对多的关系,只有一方关系被声明,你可以很容易地修改它以便按照其他约定映射。
多对多关系由 FluentNHibernate.Automapping.Steps处理.HasManyToManyStep
,一个 IAutomappingStep
由 DefaultAutomappingConfiguration
返回。这个步骤只会映射一个属性,如果它发现相关类型的相应属性(所以多对多关系的两端都必须声明)。
我采取的方法是:
$ b $ ul
为 HasManyToManyStep
创建一个装饰器类,它支持基于属性(或其他约定)的存在来检测和映射多对多属性。
DefaultAutomappingConfiguration $>派生的类c $ c>到并覆盖 GetMappingSteps
,用装饰器包装 HasManyToManyStep
的任何实例。
这里是装饰器,它首先尝试使用默认的 HasManyToManyStep
功能。否则,如果为成员定义 HasManyToManyAttribute
,它也将创建关系。用于创建关系的代码几乎与 - 只是没有引用关系的另一面。
class ExplicitHasManyToManyStep:IAutomappingStep
{
readonly IAutomappingConfiguration Configuration;
只读IAutomappingStep DefaultManyToManyStep;
public ExplicitHasManyToManyStep(IAutomappingConfiguration配置,IAutomappingStep defaultManyToManyStep)
{
Configuration = configuration;
DefaultManyToManyStep = defaultManyToManyStep;
#region实现IAutomappingStep
$ b $ public bool应用映射(成员成员)
{
if(DefaultManyToManyStep.ShouldMap(member) )
{
return true;
//修改此语句以检查其他属性或约定
return member.MemberInfo.IsDefined(typeof(HasManyToManyAttribute),true);
$ b $ 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);
返回Collection;
void SetDefaultAccess(Member member,CollectionMapping映射)
{
var ResolvedAccess = MemberAccessResolver.Resolve(member); (ResolvedAccess!= Access.Property&& ResolvedAccess!= Access.Unset)
{
mapping.Set(x => x.Access,Layer。默认值,ResolvedAccess.ToString());
}
if(member.IsProperty&!member.CanWrite)
{
mapping.Set(x => x.Access,Layer。默认值,Configuration.GetAccessStrategyForReadOnlyProperty(member).ToString());
静态ICollectionRelationshipMapping CreateManyToMany(成员成员,类型parentType,类型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);
返回映射;
static void SetKey(Member属性,ClassMappingBase类映射,CollectionMapping映射)
{
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);
$ / code $ / pre
$ b $ HasManyToManyAttribute
类,因为没有其他的约定我可以轻易地依靠在我的情况:
$ b $ pre $ [AttributeUsage(AttributeTargets.Property ,AllowMultiple = false)]
public class HasManyToManyAttribute:Attribute
{
}
从 DefaultMappingConfiguration
类派生的配置类:
类AutomappingConfiguration :DefaultAutomappingConfiguration
{
public override IEnumerable< IAutomappingStep> GetMappingSteps(AutoMapper mapper,IConventionFinder convention convention)
{
return base.GetMappingSteps(mapper,conventionFinder).Select(GetDecoratedStep);
$ b $ IAutomappingStep GetDecoratedStep(IAutomappingStep步骤)
$(步骤为HasManyToManyStep)
返回新的ExplicitHasManyToManyStep(this,step );
}
返回步骤;
}
}
I'm using Fluent NHibernate's AutoMap feature to map my entities. Most of my entities inherit from a base class Entity
which has a property public IList<Tag> Tags
.
The tags are in a separate table in the database, so I use a many-to-many relation. But Fluent NHibernate creates mappings for a one-to-many relation.
I'd like to write a convention to override these mappings to use HasManyToMany(...)
if the class inherits from Entity
. Is this possible and how?
The convention could either rely on the property's type or its name.
Some code for illustration:
// entities
public class Entity
{
public virtual int Id { get; set; }
// ... some other properties
public virtual IList<Tag> { get; set; }
}
public class Tag
{
public virtual int Id { get; set; }
public virtual string TagName { get; set; }
}
public class Event : Entity
{
// ... some properties
}
// Fluent NHibernate configuration
public static ISessionFactory CreateSessionFactory()
{
var config = new CustomAutomappingConfiguration();
return Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString(c => c.FromConnectionStringWithKey("Sql")))
.Mappings(m =>
{
m.AutoMappings.Add(AutoMap.AssemblyOf<Event>(config)
.IgnoreBase<Entity>()
.Conventions.Add<CustomForeignKeyConvention>()
.Conventions.Add<CustomManyToManyTableNameConvention>();
})
.BuildSessionFactory();
}
解决方案 In my case, I wanted to use an attribute to indicate a property that should participate in a many-to-many relationship where only one side of the relationship is declared. You could easily modify this to map by other conventions.
Many-to-many relationships are handled by FluentNHibernate.Automapping.Steps.HasManyToManyStep
, an IAutomappingStep
returned by the DefaultAutomappingConfiguration
. This step will only map a property if it discovers a corresponding property of the related type (so both ends of the many-to-many relationship have to be declared).
The approach I've taken is to:
- Create a decorator class for
HasManyToManyStep
that supports detecting and mapping many-to-many properties based on the presence of an attribute (or some other convention) - Create a class derived from
DefaultAutomappingConfiguration
to when automapping and override GetMappingSteps
, wrapping any instance of HasManyToManyStep
with the decorator
Here's the decorator, which tries to use the default HasManyToManyStep
functionality first. Otherwise, if HasManyToManyAttribute
is defined for the member, it will also create the relationship. The code used to create the relationship is nearly identical to the code used by HasManyToManyStep
- just without reference to the other side of the relationship.
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
class, because there is no other convention I can easily rely on in my case:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class HasManyToManyAttribute : Attribute
{
}
Configuration class derived from 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;
}
}
这篇关于流利的NHibernate:按惯例映射HasManyToMany的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!