2009-03-17 13 views
26

Gegeben das folgende Szenario, ich möchte die Typhierarchie mit Fluent NHibernate dem Datenbankschema zuordnen.Vererbung Mapping mit fließendem NHibernate

Ich verwende NHibernate 2,0


Typ Hierarchie

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

Die Item und SubItem Klassen sind abstrakt.


Datenbank-Schema

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

See image

Das ItemType Feld bestimmt den konkreten Typ.

Jeder Datensatz in der ConcreteItemX Tabelle hat einen einzigen entsprechenden Datensatz in der Item Tabelle; ebenso für die ConcreteItemY Tabelle.

FieldB ist immer Null, wenn der Elementtyp ConcreteItemY ist.


Der Mapping (bisher)

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 wird nicht abgebildet.


Die Frage

Wie ordne ich die FieldB Eigenschaft der SubItem Klasse mit Fluent NHibernate?

Gibt es eine Möglichkeit, DiscriminateSubClassesOnColumn mit dem ItemType Feld zu nutzen?


Nachtrag

Ich bin in der Lage, das gewünschte Ergebnis mit einer hbm.xml Datei zu erreichen:

<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> 

Wie kann ich das oben Mapping mit Fluent NHibernate erreichen?

Ist es möglich, Tabelle-pro-Klasse-Hierarchie mit Tabelle-pro-Unterklasse mit Fluent NHibernate zu mischen?

+0

Gibt es einen Grund dafür, dass dies Community-Wiki ist? Könnten Sie auf jeden Fall die von Ihnen verwendete Mapping-Strategie klären? JoinedSubClassPart impliziert das Muster "Tabelle pro Unterklasse", aber das Angeben konkreter Elemente in der Tabelle "Item" impliziert das Muster "Tabelle-pro-Klasse-Hierarchie". –

+0

Ich weiß nicht, wie ich die Community-Wiki-Einstellung ändern soll. Ignorieren von FieldB, kann ich Tabelle-pro-Unterklasse verwenden. Die Anwesenheit von FieldB verursacht mir einige Verwirrung. Es scheint eine Mischung der beiden Strategien zu sein. Der ConcreteItemX-Typ erbt FieldB von SubItem. FieldB wird in der Item-Tabelle beibehalten. –

Antwort

1

Nun, ich bin nicht sicher, ob es richtig ist, aber es könnte funktionieren ... Wenn jemand das sauberer machen kann, würde ich es gerne sehen (ernsthaft, würde ich; das ist ein interessantes Problem) .

die genauen Klassendefinitionen verwenden Sie gab, hier sind die Zuordnungen:

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); 
    } 
} 

Diese Zuordnungen erzeugen zwei hbm.xml Dateien wie so (einige irrelevante Daten aus Gründen der Klarheit entfernt):

<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> 

Es ist hässlich, aber es sieht so aus, als könnte es eine brauchbare Mapping-Datei erzeugen und es ist Fluent! :/ Sie könnten die Idee noch etwas optimieren, um genau das zu bekommen, was Sie wollen.

1

Die Codezeile: if (part.GetType().BaseType.Name == "JoinedSubClassPart1") kann wie folgt neu geschrieben werden:

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

Dies ist, wie ich mein Erbe Problem gelöst:

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"); 
    } 
} 

Sie können diese Karte Konstruktor der Superklasse fügen Sie dann :

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

toll, danke! – vondip

26

Ich weiß, das ist wirklich alt, aber ich Es ist jetzt ziemlich einfach, fließend einzurichten, um die genaue Zuordnung zu erzeugen, die Sie ursprünglich gewünscht haben. Da ich bei der Suche nach der Antwort auf diesen Post stieß, dachte ich, ich würde ihn posten.

Sie haben soeben Ihre ClassMap für die Basisklasse erstellen, ohne jeden Bezug auf Ihre Unterklassen:

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"); 
    } 
} 

dann Ihre abstrakte Unterklasse wie diese Karte:

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

Dann Karte Ihre konkreten Unterklassen wie folgt:

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

Hoffentlich hilft dies jemand anderen auf der Suche nach diesem Typ Mapping mit fließend.

+0

Schön! Genau das, was ich brauchte. – jweyrich