2009-03-17 4 views
26

Учитывая следующий сценарий, я хочу сопоставить иерархию типов с базой данных с использованием Fluent NHibernate.Наследование с использованием Fluent NHibernate

Я использую NHibernate 2.0


Тип иерархии

public abstract class Item 
{ 
    public virtual int ItemId { get; set; } 
    public virtual string ItemType { get; set; } 
    public virtual string FieldA { get; set; } 
} 

public abstract class SubItem : Item 
{ 
    public virtual string FieldB { get; set; } 
} 

public class ConcreteItemX : SubItem 
{ 
    public virtual string FieldC { get; set; } 
} 

public class ConcreteItemY : Item 
{ 
    public virtual string FieldD { get; set; } 
} 

See image

The Item и SubItem классы являются абстрактными.


Схема базы данных

 
+----------+ +---------------+ +---------------+ 
| Item  | | ConcreteItemX | | ConcreteItemY | 
+==========+ +===============+ +===============+ 
| ItemId | | ItemId  | | ItemId  | 
| ItemType | | FieldC  | | FieldD  | 
| FieldA | +---------------+ +---------------+ 
| FieldB | 
+----------+ 

See image

ItemType поле определяет конкретный тип.

Каждая запись в таблице ConcreteItemX имеет одну соответствующую запись в таблице Item; аналогично для таблицы ConcreteItemY.

FieldB всегда имеет значение null, если тип предмета: ConcreteItemY.


Отображение (до сих пор)

public class ItemMap : ClassMap<Item> 
{ 
    public ItemMap() 
    { 
     WithTable("Item"); 
     Id(x => x.ItemId, "ItemId"); 
     Map(x => x.FieldA, "FieldA"); 

     JoinedSubClass<ConcreteItemX>("ItemId", MapConcreteItemX); 
     JoinedSubClass<ConcreteItemY>("ItemId", MapConcreteItemY); 
    } 

    private static void MapConcreteItemX(JoinedSubClassPart<ConcreteItemX> part) 
    { 
     part.WithTableName("ConcreteItemX"); 
     part.Map(x => x.FieldC, "FieldC"); 
    } 

    private static void MapConcreteItemY(JoinedSubClassPart<ConcreteItemY> part) 
    { 
     part.WithTableName("ConcreteItemX"); 
     part.Map(x => x.FieldD, "FieldD"); 
    } 
} 

FieldB не отображается.


Вопрос

Как отобразить FieldB свойство SubItem класса с помощью Fluent NHibernate?

Есть ли способ, которым я могу использовать DiscriminateSubClassesOnColumn, используя поле ItemType?


Добавление

Я смог добиться желаемого результата с помощью файла hbm.xml:

<class name="Item" table="Item"> 

    <id name="ItemId" type="Int32" column="ItemId"> 
    <generator class="native"/> 
    </id> 

    <discriminator column="ItemType" type="string"/> 

    <property name="FieldA" column="FieldA"/> 

    <subclass name="ConcreteItemX" discriminator-value="ConcreteItemX"> 
    <!-- Note the FieldB mapping here --> 
    <property name="FieldB" column="FieldB"/> 
    <join table="ConcreteItemX"> 
     <key column="ItemId"/> 
     <property name="FieldC" column="FieldC"/> 
    </join> 
    </subclass> 

    <subclass name="ConcreteItemY" discriminator-value="ConcreteItemY"> 
    <join table="ConcreteItemY"> 
     <key column="ItemId"/> 
     <property name="FieldD" column="FieldD"/> 
    </join> 
    </subclass> 

</class> 

Как выполнить выше отображение с помощью Fluent NHibernate?

Можно ли смешивать иерархию таблицы за класс с таблицей на подкласс с использованием Fluent NHibernate?

+0

Любые причины, по которым отмечается wiki сообщества? В любом случае, можете ли вы уточнить стратегию сопоставления, которую используете? SessionSubClassPart подразумевает шаблон table-per-subclass, но при условии, что конкретные элементы сохраняются в таблице Item, подразумевается шаблон table-per-class-hierarchy. –

+0

Я не уверен, как изменить настройку вики сообщества. Игнорирование FieldB, я могу использовать таблицу за подкласс. Наличие FieldB вызывает у меня некоторую путаницу. Кажется, это смесь двух стратегий. Тип ConcreteItemX наследует FieldB от SubItem. FieldB сохраняется в таблице Item. – 2009-03-18 05:06:24

ответ

1

Ну, я не уверен, что это совершенно правильно, но это может сработать ... Если кто-нибудь сможет сделать это более чисто, я бы хотел его увидеть (серьезно, я бы хотел, это интересная проблема) ,

Используя точные определения класса вы дали, вот отображения:

public class ItemMap : ClassMap<Item> 
{ 
    public ItemMap() 
    { 
     Id(x => x.ItemId); 
     Map(x => x.ItemType); 
     Map(x => x.FieldA); 

     AddPart(new ConcreteItemYMap()); 
    } 
} 

public class SubItemMap : ClassMap<SubItem> 
{ 
    public SubItemMap() 
    { 
     WithTable("Item"); 

     // Get the base map and "inherit" the mapping parts 
     ItemMap baseMap = new ItemMap(); 
     foreach (IMappingPart part in baseMap.Parts) 
     { 
      // Skip any sub class parts... yes this is ugly 
      // Side note to anyone reading this that might know: 
      // Can you use GetType().IsSubClassOf($GenericClass$) 
      // without actually specifying the generic argument such 
      // that it will return true for all subclasses, regardless 
      // of the generic type? 
      if (part.GetType().BaseType.Name == "JoinedSubClassPart`1") 
       continue; 
      AddPart(part); 
     } 
     Map(x => x.FieldB); 
     AddPart(new ConcreteItemXMap()); 
    } 
} 

public class ConcreteItemXMap : JoinedSubClassPart<ConcreteItemX> 
{ 
    public ConcreteItemXMap() 
     : base("ItemId") 
    { 
     WithTableName("ConcreteItemX"); 
     Map(x => x.FieldC); 
    } 
} 

public class ConcreteItemYMap : JoinedSubClassPart<ConcreteItemY> 
{ 
    public ConcreteItemYMap() 
     : base("ItemId") 
    { 
     WithTableName("ConcreteItemY"); 
     Map(x => x.FieldD); 
    } 
} 

Эти отображения производят два hbm.xml файлов, как так (удалены некоторые посторонние данные для ясности):

<class name="Item" table="`Item`"> 
    <id name="ItemId" column="ItemId" type="Int32"> 
     <generator class="identity" /> 
    </id> 
    <property name="FieldA" type="String"> 
     <column name="FieldA" /> 
    </property> 
    <property name="ItemType" type="String"> 
     <column name="ItemType" /> 
    </property> 
    <joined-subclass name="ConcreteItemY" table="ConcreteItemY"> 
     <key column="ItemId" /> 
     <property name="FieldD"> 
     <column name="FieldD" /> 
     </property> 
    </joined-subclass> 
    </class> 

    <class name="SubItem" table="Item"> 
    <id name="ItemId" column="ItemId" type="Int32"> 
     <generator class="identity" /> 
    </id> 
    <property name="FieldB" type="String"> 
     <column name="FieldB" /> 
    </property> 
    <property name="ItemType" type="String"> 
     <column name="ItemType" /> 
    </property> 
    <property name="FieldA" type="String"> 
     <column name="FieldA" /> 
    </property> 
    <joined-subclass name="ConcreteItemX" table="ConcreteItemX"> 
     <key column="ItemId" /> 
     <property name="FieldC"> 
     <column name="FieldC" /> 
     </property> 
    </joined-subclass> 
    </class> 

Это уродливо, но похоже, что он может генерировать полезный файл сопоставления, и он свободен! :/ Возможно, вы сможете настроить эту идею еще раз, чтобы получить именно то, что вы хотите.

1

Строка кода: if (part.GetType().BaseType.Name == "JoinedSubClassPart1") можно переписать следующим образом:

part.GetType().BaseType.IsGenericType && part.GetType().BaseType.GetGenericTypeDefinition() == typeof(JoinedSubClassPart<>) 
1

Это как я решил мою проблему наследования:

public static class DataObjectBaseExtension 
{ 
    public static void DefaultMap<T>(this ClassMap<T> DDL) where T : IUserAuditable 
    { 
     DDL.Map(p => p.AddedUser).Column("AddedUser"); 
     DDL.Map(p => p.UpdatedUser).Column("UpdatedUser"); 
    } 
} 

Вы можете добавить это к вашему суперкласса карте конструктора :

internal class PatientMap : ClassMap<Patient> 
{ 
    public PatientMap() 
    { 
     Id(p => p.GUID).Column("GUID"); 
     Map(p => p.LocalIdentifier).Not.Nullable(); 
     Map(p => p.DateOfBirth).Not.Nullable(); 
     References(p => p.Sex).Column("RVSexGUID"); 
     References(p => p.Ethnicity).Column("RVEthnicityGUID"); 

     this.DefaultMap(); 
    } 


} 
+0

отлично, спасибо! – vondip

26

Я знаю, что это действительно старый, но я t теперь довольно просто настроить свободно, чтобы генерировать точное сопоставление, которое вы изначально хотели. Поскольку я столкнулся с этим сообщением при поиске ответа, я подумал, что опубликую его.

Вы просто создать ClassMap для базового класса без каких-либо ссылок на ваши подклассы:

public class ItemMap : ClassMap<Item> 
{ 
    public ItemMap() 
    { 
     this.Table("Item"); 
     this.DiscriminateSubClassesOnColumn("ItemType"); 
     this.Id(x => x.ItemId, "ItemId"); 
     this.Map(x => x.FieldA, "FieldA"); 
    } 
} 

Затем карте свой абстрактный подкласс, как это:

public class SubItemMap: SubclassMap<SubItemMap> 
{ 
    public SubItemMap() 
    { 
     this.Map(x => x.FieldB); 
    } 
} 

Затем сопоставить конкретные подклассы следующим образом:

public class ConcreteItemXMap : SubclassMap<ConcreteItemX> 
{ 
    public ConcretItemXMap() 
    { 
     this.Join("ConcreteItemX", x => 
     { 
      x.KeyColumn("ItemID"); 
      x.Map("FieldC") 
     }); 
    } 
} 

Надеюсь, это поможет кому-то еще в поиске этого типа отображения с плавным.

+0

Ницца! Именно то, что мне нужно. – jweyrich