Fluido NHibernate: Cómo Mapa M: N muchos-a-muchos con claves compuestas en ambos lados
Pregunta
OK, así que aquí está el problema. Ni siquiera es tan loco como el tipo que quiere asignar m:. N con diferentes cantidades de las columnas en sus PK
No importa lo que haga o cuando miro no parece haber ninguna cadena método que dará lugar a una asignación exitosa de este.
He intentado doblar para arriba en la matriz y las columnas del niño, por ejemplo ParentColumn ( "") ( ") ChildColumn ( "") ChildColumn. ( "") ParentColumn." -. Didnt' creía que fuera a trabajar y tenía razón .
intentado apenas usando ForeignKeyConstraintNames sin suerte. Todavía FNH es el mapeo de un lado a una sola tecla.
public partial class M2M2ParentAMap : ClassMap<M2M2ParentA>
{
public M2M2ParentAMap()
{
Table("`M2M2ParentA`");
Schema("`dbo`");
CompositeId().KeyProperty( x => x.M2M2ParentAId1, "`M2M2ParentAId1`" ).KeyProperty( x => x.M2M2ParentAId2, "`M2M2ParentAId2`" );
HasManyToMany(x => x.M2M2ParentB).Schema("`dbo`")
.ForeignKeyConstraintNames("FK_M2M2Link_M2M2ParentA", "FK_M2M2Link_M2M2ParentB");
}
}
public partial class M2M2ParentBMap : ClassMap<M2M2ParentB>
{
public M2M2ParentBMap()
{
Table("`M2M2ParentB`");
Schema("`dbo`");
CompositeId().KeyProperty( x => x.M2M2ParentBId1, "`M2M2ParentBId1`" ).KeyProperty( x => x.M2M2ParentBId2, "`M2M2ParentBId2`" );
HasManyToMany(x => x.M2M2ParentA)
.Schema("`dbo`").ForeignKeyConstraintNames("FK_M2M2Link_M2M2ParentB", "FK_M2M2Link_M2M2ParentA");
}
}
public partial class M2M2LinkMap : ClassMap<M2M2Link>
{
public M2M2LinkMap()
{
Table("`M2M2Link`");
Schema("`dbo`");
CompositeId()
.KeyProperty( x => x.M2M2ParentA_Id1, "`M2M2ParentA_Id1`" )
.KeyProperty( x => x.M2M2ParentA_Id2, "`M2M2ParentA_Id2`" )
.KeyProperty( x => x.M2M2ParentB_Id1, "`M2M2ParentB_Id1`" )
.KeyProperty( x => x.M2M2ParentB_Id2, "`M2M2ParentB_Id2`" );
References(x => x.M2M2ParentA).Columns("`M2M2ParentA_Id1`","`M2M2ParentA_Id2`").Cascade.All();
References(x => x.M2M2ParentB).Columns("`M2M2ParentB_Id1`","`M2M2ParentB_Id2`").Cascade.All();
}
}
ERROR:
Foreign key (FK_M2M2Link_M2M2ParentB:M2M2ParentAToM2M2ParentB [M2M2ParentB_id])) must have same number of columns as the referenced primary key (M2M2ParentB [M2M2ParentBId1, M2M2ParentBId2])
y
public partial class M2M2ParentAMap : ClassMap<M2M2ParentA>
{
public M2M2ParentAMap()
{
Table("`M2M2ParentA`");
Schema("`dbo`");
CompositeId()
.KeyProperty( x => x.M2M2ParentAId1, "`M2M2ParentAId1`" )
.KeyProperty( x => x.M2M2ParentAId2, "`M2M2ParentAId2`" );
HasManyToMany(x => x.M2M2ParentB)
.Schema("`dbo`")
.Table("`M2M2Link`")
.ParentKeyColumn("`M2M2ParentA_Id1`")
.ParentKeyColumn("`M2M2ParentA_Id2`")
.ChildKeyColumn("`M2M2ParentB_Id1`")
.ChildKeyColumn("`M2M2ParentB_Id2`");
}
}
public partial class M2M2ParentBMap : ClassMap<M2M2ParentB>
{
public M2M2ParentBMap()
{
Table("`M2M2ParentB`");
Schema("`dbo`");
CompositeId()
.KeyProperty( x => x.M2M2ParentBId1, "`M2M2ParentBId1`" )
.KeyProperty( x => x.M2M2ParentBId2, "`M2M2ParentBId2`" );
HasManyToMany(x => x.M2M2ParentA)
.Schema("`dbo`")
.Table("`M2M2Link`")
.ParentKeyColumn("`M2M2ParentB_Id1`")
.ParentKeyColumn("`M2M2ParentB_Id2`")
.ChildKeyColumn("`M2M2ParentA_Id1`")
.ChildKeyColumn("`M2M2ParentA_Id2`");
}
}
public partial class M2M2LinkMap : ClassMap<M2M2Link>
{
public M2M2LinkMap()
{
Table("`M2M2Link`");
Schema("`dbo`");
CompositeId()
.KeyProperty( x => x.M2M2ParentA_Id1, "`M2M2ParentA_Id1`" )
.KeyProperty( x => x.M2M2ParentA_Id2, "`M2M2ParentA_Id2`" )
.KeyProperty( x => x.M2M2ParentB_Id1, "`M2M2ParentB_Id1`" )
.KeyProperty( x => x.M2M2ParentB_Id2, "`M2M2ParentB_Id2`" );
References(x => x.M2M2ParentA)
.Columns("`M2M2ParentA_Id1`","`M2M2ParentA_Id2`").Cascade.All();
References(x => x.M2M2ParentB)
.Columns("`M2M2ParentB_Id1`","`M2M2ParentB_Id2`").Cascade.All();
}
}
ERROR:
Foreign key (FKAB0E07EA57E45AB6:M2M2Link [M2M2ParentB_Id2])) must have same number of columns as the referenced primary key (M2M2ParentB [M2M2ParentBId1, M2M2ParentBId2])
DDL
CREATE TABLE [dbo].[M2M2ParentA] ( [M2M2ParentAId1] [int] NOT NULL,
[M2M2ParentAId2] [int] NOT NULL,
CONSTRAINT [PK_M2M2ParentA] PRIMARY KEY CLUSTERED ( [M2M2ParentAId1] ASC, [M2M2ParentAId2] ASC ) )
CREATE TABLE [dbo].[M2M2ParentB] ( [M2M2ParentBId1] [int] NOT NULL,
[M2M2ParentBId2] [int] NOT NULL,
CONSTRAINT [PK_M2M2ParentB] PRIMARY KEY CLUSTERED ( [M2M2ParentBId1] ASC, [M2M2ParentBId2] ASC ) )
CREATE TABLE [dbo].[M2M2Link] ( [M2M2ParentA_Id1] [int] NOT NULL,
[M2M2ParentA_Id2] [int] NOT NULL,
[M2M2ParentB_Id1] [int] NOT NULL,
[M2M2ParentB_Id2] [int] NOT NULL,
CONSTRAINT [PK_M2M2Link] PRIMARY KEY CLUSTERED ( [M2M2ParentA_Id1] ASC, [M2M2ParentA_Id2] ASC, [M2M2ParentB_Id1] ASC, [M2M2ParentB_Id2] ASC ) )
ALTER TABLE [dbo].[M2M2Link]
WITH CHECK
ADD CONSTRAINT [FK_M2M2Link_M2M2ParentA] FOREIGN KEY ( [M2M2ParentA_Id1], [M2M2ParentA_Id2] ) REFERENCES [dbo].[M2M2ParentA] ( [M2M2ParentAId1],
[M2M2ParentAId2] )
ALTER TABLE [dbo].[M2M2Link]
CHECK CONSTRAINT [FK_M2M2Link_M2M2ParentA]
ALTER TABLE [dbo].[M2M2Link]
WITH CHECK
ADD CONSTRAINT [FK_M2M2Link_M2M2ParentB] FOREIGN KEY ( [M2M2ParentB_Id1], [M2M2ParentB_Id2] ) REFERENCES [dbo].[M2M2ParentB] ( [M2M2ParentBId1],
[M2M2ParentBId2] )
ALTER TABLE [dbo].[M2M2Link]
CHECK CONSTRAINT [FK_M2M2Link_M2M2ParentB]
Actualización: He tratado de crear un tipo de clave personalizada, pero no tuvo éxito.
Su reto, si decide aceptarla:
Código Presente para el mejor trabajar mapeo fluidez usando esta estructura de la tabla, probablemente utilizando un tipo de clave personalizada, y la recompensa es suya.
Cualquier persona?
Solución
Si FluentNHibernate es actualmente incapaz de asignar este, a continuación, puede asignar con un archivo hbm.xml.
I también se utiliza un componente para la Identificación del compuesto de ambas clases. Esto hace que la identidad separada de la entidad, lo que permite session.Get<M2M2ParentA>( new M2M2Id( 1, 2 ))
. Ver esta respuesta para una discusión de las 3 formas de representar composite-id (es el mismo para NHibernate y Hibernate).
<class name="M2M2ParentA" table="M2M2ParentA">
<composite-id name="Id" class="M2M2Id">
<key-property name="Id1" />
<key-property name="Id2" />
</composite-id>
<bag name="BList" table="M2M2Link" lazy="false" fetch="join" >
<key>
<column name="M2M2ParentAId1" />
<column name="M2M2ParentAId2" />
</key>
<many-to-many class="M2M2ParentB" >
<column name="M2M2ParentBId1" />
<column name="M2M2ParentBId2" />
</many-to-many>
</bag>
</class>
<class name="M2M2ParentB" table="M2M2ParentB">
<composite-id name="Id" class="M2M2Id">
<key-property name="Id1" />
<key-property name="Id2" />
</composite-id>
<bag name="AList" table="M2M2Link" lazy="false" fetch="join" inverse="true">
<key>
<column name="M2M2ParentBId1" />
<column name="M2M2ParentBId2" />
</key>
<many-to-many class="M2M2ParentA" >
<column name="M2M2ParentAId1" />
<column name="M2M2ParentAId2" />
</many-to-many>
</bag>
</class>
Y mi versión de sus clases.
public class M2M2ParentA
{
public M2M2ParentA()
{
BList = new List<M2M2ParentB>();
}
public virtual M2M2Id Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<M2M2ParentB> BList { get; set; }
}
public class M2M2ParentB
{
public M2M2ParentB()
{
AList = new List<M2M2ParentA>();
}
public virtual M2M2Id Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<M2M2ParentA> AList { get; set; }
}
public class M2M2Id
{
public M2M2Id() {}
public M2M2Id( int id1, int id2 )
{
Id1 = id1;
Id2 = id2;
}
public virtual int Id1 { get; set; }
public virtual int Id2 { get; set; }
public override int GetHashCode()
{
return Id1.GetHashCode() + Id2.GetHashCode();
}
public override bool Equals( object obj )
{
M2M2Id other = obj as M2M2Id;
return other != null && Id1 == other.Id1 && Id2 == other.Id2;
}
}
Otros consejos
La respuesta a su pregunta es que Fluido no puede manejar esa situación, sin embargo, y se debe asignar por un archivo de .hbm.xml.
La respuesta larga es que en realidad se puede hacer Fluido si las claves compuestas de sus 2 tablas padre en cambio se componen de claves ajenas a 4 mesas abuelos separados (GPA1, GPA2, GPB1, GPB2 - en el ejemplo a continuación). En ese caso, la asignación podría ser algo como esto ... (pero es un poco hackey)
//OBJECTS
public class GPA1
{
public virtual long ID {get;set;}
}
public class GPA2
{
public virtual long ID { get; set; }
}
public class GPB1
{
public virtual long ID { get; set; }
}
public class GPB2
{
public virtual long ID { get; set; }
}
public class M2M2ParentA
{
public virtual GPA1 ID1A { get; set; }
public virtual GPA2 ID2A { get; set; }
}
public class M2M2ParentB
{
public virtual GPB1 ID1B { get; set; }
public virtual GPB2 ID2B { get; set; }
}
public class M2M2Link
{
public virtual M2M2ParentA LinkA { get; set; }
public virtual M2M2ParentB LinkB { get; set; }
public virtual GPA1 ID1A
{
get { return LinkA.ID1A; }
set { LinkA.ID1A = value; }
}
public virtual GPA2 ID2A
{
get { return LinkA.ID2A; }
set { LinkA.ID2A = value; }
}
public virtual GPB1 ID1B
{
get { return LinkB.ID1B; }
set { LinkB.ID1B = value; }
}
public virtual GPB2 ID2B
{
get { return LinkB.ID2B; }
set { LinkB.ID2B = value; }
}
}
//FLUENT MAPPINGS
public class GPA1Map : ClassMap<GPA1>
{
public GPA1Map()
{
Table("GPA1");
Id(x => x.ID, "id_column");
}
}
public class GPA2Map : ClassMap<GPA2>
{
public GPA2Map()
{
Table("GPA1");
Id(x => x.ID, "id_column");
}
}
public class GPB1Map : ClassMap<GPB1>
{
public GPB1Map()
{
Table("GPA1");
Id(x => x.ID, "id_column");
}
}
public class GPB2Map : ClassMap<GPB2>
{
public GPB2Map()
{
Table("GPA1");
Id(x => x.ID, "id_column");
}
}
public class M2M2ParentAMap : ClassMap<M2M2ParentA>
{
public M2M2ParentAMap()
{
Table("M2M2ParentA");
CompositeId()
.KeyReference(x => x.ID1A, "M2M2ParentAId1")
.KeyReference(x => x.ID1A, "M2M2ParentAId2");
}
}
public class M2M2ParentBMap : ClassMap<M2M2ParentB>
{
public M2M2ParentBMap()
{
Table("M2M2ParentB");
CompositeId()
.KeyReference(x => x.ID1B, "M2M2ParentBId1")
.KeyReference(x => x.ID1B, "M2M2ParentBId2");
}
}
public class M2M2LinkMap : ClassMap<M2M2Link>
{
public M2M2LinkMap()
{
Table("M2M2Link");
CompositeId()
.KeyReference(x => x.ID1A, "M2M2ParentA_Id1")
.KeyReference(x => x.ID1B, "M2M2ParentA_Id2")
.KeyReference(x => x.ID2A, "M2M2ParentB_Id1")
.KeyReference(x => x.ID2B, "M2M2ParentB_Id2");
}
}
No debe haber hasMany <> relaciones en cada una de esas clases de mapeo, pero tengo pereza. Si vas a la ruta de un archivo que incluye .hbm.xml, me gustaría recomendar el uso del .ExportTo ( "c: \") la función al configurar el ISessionFactory y que sólo puede editar el archivo de HBM que pone a cabo con fluidez. Es un gran punto de partida.
HTH, -Danno