我正在使用Fluent NHibernate的AutoMap功能来映射我的实体。我的大多数实体都从基类Entity
继承,它具有属性public IList<Tag> Tags
。
标签位于数据库的单独表中,因此我使用多对多关系。但是Fluent NHibernate为一对多关系创建了映射。
如果类继承自HasManyToMany(...)
,我想编写一个约定来覆盖这些映射以使用Entity
。这可能吗?如何?
约定可以依赖于属性的类型或名称。
一些代码示例:
// 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();
}
答案 0 :(得分:0)
我认为你不能用惯例完成映射。但是,如果要在实体和标记之间保留一个链接表,则可以执行以下操作:
m.AutoMappings.Add(AutoMap.AssemblyOf<Event>(config)
.IncludeBase<Entity>()
.Override<Entity>(map =>
map.HasManyToMany(e => e.Tags)
.Inverse()
.Cascade.SaveUpdate()));
请注意,我已将IgnoreBase<Entity>()
更改为IncludeBase<Entity>()
。这将添加一个Entity表,但会保留一个链接表。通过此映射,您将获得下表DDL:
create table [Entity] (
Id INT IDENTITY NOT NULL,
primary key (Id)
)
create table TagToEntity (
Entity_id INT not null,
Tag_id INT not null
)
create table Event (
Entity_id INT not null,
primary key (Entity_id)
)
create table [Tag] (
Id INT IDENTITY NOT NULL,
TagName NVARCHAR(255) null,
primary key (Id)
)
alter table TagToEntity
add constraint FKD7554554A8C4CA9
foreign key (Tag_id)
references [Tag]
alter table TagToEntity
add constraint FKD75545564C9EC79
foreign key (Entity_id)
references [Entity]
alter table Event
add constraint FKA2FD7DF664C9EC79
foreign key (Entity_id)
references [Entity]
如果您选择为每个子类执行Override<>
,则每个子类将有一个链接表。
答案 1 :(得分:0)
就我而言,我想使用一个属性来表示一个属性,该属性应该参与多对多关系,其中只声明关系的一侧。您可以轻松地将其修改为按其他约定进行映射。
多对多关系由FluentNHibernate.Automapping.Steps.HasManyToManyStep
处理,IAutomappingStep
返回DefaultAutomappingConfiguration
。如果属性发现相关类型的相应属性,则此步骤将仅映射属性(因此必须声明多对多关系的两端)。
我采取的方法是:
HasManyToManyStep
创建一个装饰器类,支持根据属性(或其他约定)的存在检测和映射多对多属性DefaultAutomappingConfiguration
的类到automapping并覆盖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 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;
}
}