nhibernate mapping types fluent polymorphism

Mapeo de Herencias con Fluido NHibernate



mapping types (4)

Dado el siguiente escenario, quiero asignar la jerarquía de tipos al esquema de base de datos utilizando Fluent NHibernate.

Estoy usando NHibernate 2.0

Tipo de jerarquía

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

Ver imagen

Las clases Item y SubItem son abstractas.

Esquema de base de datos

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

Ver imagen

El campo ItemType determina el tipo concreto.

Cada registro en la tabla ConcreteItemX tiene un solo registro correspondiente en la tabla Item ; Lo mismo ocurre con la mesa ConcreteItemY .

FieldB siempre es nulo si el tipo de elemento es ConcreteItemY .

El Mapeo (hasta ahora)

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 no está asignado.

La pregunta

¿Cómo FieldB propiedad FieldB de la clase SubItem utilizando SubItem NHibernate?

¿Hay alguna forma en que pueda aprovechar DiscriminateSubClassesOnColumn usando el campo ItemType ?

Apéndice

Puedo lograr el resultado deseado usando un archivo 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>

¿Cómo puedo realizar el mapeo anterior utilizando Fluent NHibernate?

¿Es posible mezclar la jerarquía de tabla por clase con la tabla por subclase utilizando Fluent NHibernate?


Así es como resolví mi problema de herencia:

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

Luego puede agregar esto a su constructor de mapa de superclase:

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


Bueno, no estoy seguro de que esté bien, pero podría funcionar ... Si alguien puede hacerlo de forma más limpia, me encantaría verlo (en serio, lo haría; este es un problema interesante).

Usando las definiciones de clase exactas que dio, aquí están las asignaciones:

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

Esos mapeos producen dos archivos hbm.xml así (algunos datos extraños eliminados para mayor claridad):

<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 feo, pero parece que podría generar un archivo de mapeo utilizable y ¡es fluido! : / Es posible que pueda modificar la idea un poco más para obtener exactamente lo que desea.


La línea de código: if (part.GetType().BaseType.Name == "JoinedSubClassPart1") se puede reescribir de la siguiente manera:

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


Sé que esto es realmente antiguo, pero ahora es bastante simple configurar con fluidez para generar el mapa exacto que inicialmente deseaba. Desde que me topé con esta publicación al buscar la respuesta, pensé que la publicaría.

Simplemente crea su ClassMap para la clase base sin ninguna referencia a sus subclases:

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

Luego mapea tu subclase abstracta como esta:

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

Luego mapea tus subclases concretas de esta manera:

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

Esperemos que esto ayude a alguien más a buscar este tipo de mapeo con fluidez.