2009-03-17 24 views
26

Dato il seguente scenario, voglio mappare la gerarchia dei tipi allo schema del database usando Fluent NHibernate.Mapping dell'erede con Fluent NHibernate

Sto usando NHibernate 2,0


Tipo Gerarchia

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

I Item e SubItem classi sono astratto.


Schema del database

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

See image

Il campo ItemType determina il tipo concreto.

Ogni record nella tabella ConcreteItemX ha un singolo record corrispondente nella tabella Item; allo stesso modo per la tabella ConcreteItemY.

FieldB è sempre nullo se il tipo di articolo è ConcreteItemY.


Il Mapping (finora)

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 non è mappata.


La questione

Come posso mappare la proprietà FieldB della classe SubItem utilizzando Fluent NHibernate?

Esiste un modo per sfruttare lo DiscriminateSubClassesOnColumn utilizzando il campo ItemType?


Addendum

sono in grado di ottenere il risultato desiderato utilizzando un file 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> 

Come compire la mappatura sopra utilizzando Fluent NHibernate?

È possibile combinare la gerarchia table-per-class con la tabella per sottoclasse utilizzando Fluent NHibernate?

+0

Qualsiasi motivo è contrassegnato come wiki della comunità? In ogni caso, potresti chiarire la strategia di mappatura che stai utilizzando? JoinedSubClassPart implica il modello table-per-subclass, ma il dire che gli elementi concreti sono permanenti nella tabella Item implica il pattern table-per-class-hierarchy. –

+0

Non sono sicuro di come modificare l'impostazione wiki della comunità. Ignorando FieldB, posso usare table-per-subclass. La presenza di FieldB mi crea una certa confusione. Sembra essere una miscela delle due strategie. Il tipo ConcreteItemX eredita FieldB da SubItem. FieldB è persistente nella tabella Item. –

risposta

1

Beh, non sono sicuro che sia giusto, ma potrebbe funzionare ... Se qualcuno può farlo in modo più pulito, mi piacerebbe vederlo (seriamente lo farei, questo è un problema interessante) .

Utilizzando le definizioni esatte di classe avete dato, ecco le mappature:

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

Queste mappature produrre due file hbm.xml in questo modo (alcuni dati estranei rimossi per chiarezza):

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

È brutto, ma sembra che potrebbe generare un file di mappatura utilizzabile ed è Fluent! :/ Potresti essere in grado di modificare ulteriormente l'idea per ottenere esattamente ciò che desideri.

1

La riga di codice: if (part.GetType().BaseType.Name == "JoinedSubClassPart1") può essere riscritta come segue:

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

Ecco come ho risolto il mio problema di successione:

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

È quindi possibile aggiungere questo alla tua superclasse mappa costruttore :

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

fantastico, grazie! – vondip

26

So che questo è veramente vecchio, ma io È ora abbastanza semplice impostare fluente per generare la mappatura esatta che si desiderava inizialmente. Da quando ho trovato questo post quando ho cercato la risposta, ho pensato di pubblicarlo.

Basta creare il ClassMap per la classe di base senza alcun riferimento alle vostre sottoclassi:

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

Poi map sottoclasse astratta come questo:

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

mappare le sottoclassi concrete in questo modo:

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

Speriamo che questo aiuti qualcun altro in cerca di questo tipo di mappatura fluente.

+0

Bello! Esattamente quello di cui avevo bisogno. – jweyrich

Problemi correlati