Свободный NHibernate: как сопоставить M: N многие-ко-многим с составными ключами с обеих сторон

Хорошо, вот в чем проблема. Это даже не так безумно, как парень, который хочет сопоставить m: n с разными счетчиками столбцов в своих PK.

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

Я пробовал удвоить столбцы Parent и Child, например ParentColumn (""). ParentColumn (""). ChildColumn (""). ChildColumn ("") - не думал, что это сработает, и я был прав.

Пробовал просто использовать ForeignKeyConstraintNames, не повезло. Тем не менее, FNH сопоставляет одну сторону одной клавиши.

проблемный домен

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

И

        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]

Обновление: я попытался создать настраиваемый тип ключа, но безуспешно.

Ваша задача, если вы решите ее принять:

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

Кто-нибудь?


person Sky Sanders    schedule 02.02.2010    source источник
comment
Я вижу старую отправку патча в репозиторий кода Google, в которой говорится, что он добавляет ParentKeyColumns и ChildKeyColumns, а также то, что выглядит как прерванная реализация в текущем репозитории github. хммммм   -  person Sky Sanders    schedule 03.02.2010


Ответы (2)


Если FluentNHibernate в настоящее время не может отобразить это, вы можете отобразить его с помощью файла hbm.xml.

Я также использовал компонент для составного идентификатора обоих классов. Это отделяет личность от сущности, что позволяет session.Get<M2M2ParentA>( new M2M2Id( 1, 2 )). См. этот ответ для обсуждения трех способов представления составного идентификатора (это то же самое для NHibernate и 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>

И мой вариант твоих занятий.

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;
    }
}
person Lachlan Roche    schedule 28.02.2010
comment
@Lachlan, я запрошу этот проект и подтвердю сегодня вечером. Благодарность - person Sky Sanders; 28.02.2010
comment
@Lachlan, меня прикрывали другими вещами, но, глядя на ваш код, я уверен, что смогу с ним работать. Огромное спасибо. - person Sky Sanders; 02.03.2010

Короткий ответ на ваш вопрос заключается в том, что Fluent пока не может справиться с этой ситуацией, и вам следует сопоставить ее с файлом .hbm.xml.

Длинный ответ заключается в том, что Fluent CAN действительно может это сделать, если составные ключи ваших 2 родительских таблиц вместо этого состоят из внешних ключей для 4 отдельных родительских таблиц (GPA1, GPA2, GPB1, GPB2 - в примере ниже). В этом случае отображение могло бы выглядеть примерно так ... (но это немного хакерское)

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

В каждом из этих классов сопоставления должны быть отношения HasMany ‹>, но я поленился. Если вы идете по пути включения файла .hbm.xml, я бы рекомендовал использовать функцию .ExportTo ("c: \") при настройке ISessionFactory, и вы можете просто отредактировать файл hbm, который выводит fluent. Это отличная отправная точка.

HTH, -Данно

person Danno    schedule 18.02.2010
comment
@Danno, было бы полезно, если бы вопрос касался отображения надуманной структуры, которая, вероятно, никогда не будет видна в реальном мире, вместо очень возможного, хотя и плохо спроектированного сценария, изложенного в моем вопросе. ;-) - person Sky Sanders; 23.02.2010
comment
@Danno, я мог бы принять этот ответ, если бы вы сделали больше, указав очевидное и представив совершенно несвязанный сценарий. Может быть, разместив hbm, на который вы намекаете? - person Sky Sanders; 23.02.2010