2011-07-25 49 views
2

我使用Fluent NHibernate的AutoMap功能映射我的實體。我的大多數實體繼承自基類Entity,其中有一個屬性public IList<Tag> Tags流利的NHibernate:按照慣例映射HasManyToMany

標籤位於數據庫的單獨表格中,因此我使用多對多關係。但流利的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

其繼承策略你使用?每個具體類的表,每個類的表或每個子表的表? – Firo

+0

我不確定,哪一個確切。也許一個例子可以解釋:我有'Event:Entity'映射到名爲'Event'的表,其中包含'Event'和基類'Entity'的所有屬性。 – davehauser

+0

這將是每個具體類的表。我只知道每個子類的Override <>',這不是你想要的或者大量使用反射來調用覆蓋動態過濾類型 – Firo

回答

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>()。這將添加一個實體表,但會保留一個鏈接表。有了這個地圖,你會得到如下表DDL:如果讓你選擇做一個Override<>每個子類

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] 

,你將有每個子類一個鏈接表。

+0

感謝您的建議。我有一個現有的數據庫,我想要我的實體映射到。我有一個每個子類的鏈接表。至於每個子類的Override <>',這正是我試圖避免的:-) – davehauser

+0

對不起......我想你是想避免這種情況,但我想我會發布。試圖使用我的解決方案與現有的數據庫會導致身份問題。試圖與一個約定映射將不起作用,因爲FNH將無法找出關係 - 標籤需要一個實體集合(這將失敗B/C實體未映射)。我會再考慮一下,看看我能不能拿出別的東西。 – codeprogression

+0

所以你說,我想做什麼是不可能的?真是無賴! :-) 有沒有約定說'如果屬性是IList 比使用多對多的關係'?...可能 – davehauser

0

在我的情況下,我想用一個屬性來表示一個屬性,該屬性應該參與一個多對多的關係,其中只有關係的一方被聲明。你可以很容易地修改這個以按照其他約定映射。

多對多關係由FluentNHibernate.Automapping.Steps.HasManyToManyStep處理,由DefaultAutomappingConfiguration返回。如果該步驟發現相關類型的相應屬性,則該步驟將只映射屬性(因此必須聲明多對多關係的兩端)。

我已經採用的方法是:

  • HasManyToManyStep支持檢測和映射創建裝飾類多對許多基於屬性的存在(或一些其他約定)屬性
  • 創建時並覆蓋GetMappingStepsDefaultAutomappingConfiguration派生的類,包裝的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; 
    } 
}