Łączenie kolekcji z wielu relacji jeden-do-wielu w NHibernate

0

Za pomocą Fluent NHibernate chcę sobie zmapować dosyć prosty obiekt, a mianowicie człowieka. Co najważniejsze, każdy może posiadać matkę i ojca, tak więc cała struktura jest drzewiasta. Struktura tabeli jest bardzo prosta, jest PersonId, FatherId, MotherId i Gender. Dwa klucze obce, z FatherId i MotherId do PersonId.

Chciałbym, żeby mój model miał pojedynczą kolekcję Children (typ IEnumerable<Person> w zupełności wystarczy), w której znajdą się wszystkie dzieci. No, ale jest problem, bo prostą relacją jeden-do-wielu tego się zrobić nie da - relacje i klucze obce mamy dwie. Dlatego teraz w getterze publicznej właściwości zwracam, w zależności od płci dzieci "matki" lub "ojca". Nie dość, że nie podoba mi się ta logika w mojej klasie to na dodatek powstaje drugi problem z prywatnymi właściwościami od tych dwóch kolekcji - Fluent NHibernate (tutaj na nieszczęście) poprzez silne wiązanie wymaga wystawienia ich i tak i tak albo powiązania modelu z mapowaniem. Tego chciałbym uniknąć, no bo nie po to się w to wszystko bawię.

Model:

public enum Gender
{
    Male = 0,
    Female
}

public class Person
{
    protected virtual IEnumerable<Person> FatherChildren { get; set; }
    protected virtual IEnumerable<Person> MotherChildren { get; set; }

    public virtual Guid Id { get; private set; }
    public virtual Person Father { get; set; }
    public virtual Person Mother { get; set; }
    public virtual string FirstName { get; set; }
    public virtual string LastName { get; set; }
    public virtual Gender Gender { get; set; }

    public virtual IEnumerable<Person> Children
    {
        get
        {
            return Gender == Gender.Male ? FatherChildren : MotherChildren;
        }
    }

    public override string ToString()
    {
        return FirstName + " " + LastName;
    }

    public static class Expressions
    {
        public static readonly Expression<Func<Person, IEnumerable<Person>>> FatherChildren = x => x.FatherChildren;
        public static readonly Expression<Func<Person, IEnumerable<Person>>> MotherChildren = x => x.MotherChildren;
    }  
}

Mapowanie:

public class PersonMap : ClassMap<Person>
{
    public PersonMap()
    {
        Table("People");

        Id(x => x.Id)
            .GeneratedBy.Guid();

        References(x => x.Father)
            .Cascade.All();
        References(x => x.Mother)
            .Cascade.All();
            
        HasMany(Person.Expressions.FatherChildren)
            .AsBag()
            .KeyColumn("FatherId")
            .Inverse()
            .Cascade.All();

        HasMany(Person.Expressions.MotherChildren)
            .AsBag()
            .KeyColumn("MotherId")
            .Inverse()
            .Cascade.All();

        Map(x => x.FirstName);
        Map(x => x.LastName);
        Map(x => x.Gender);
    }
}

Czy dałoby się w jakiś sposób połączyć te kolekcje już właśnie na etapie mapowania? Znalazłem kilka pytań o bardzo podobną rzecz zarówno w NHibernate jak i samym Hibernate, ale nikt nie zaproponował innego rozwiązania niż ręczne złączenie tych kolekcji. Niby rozwiązaniem byłoby utworzenie nowej tabeli na "rodzinę", ale też mi się to średnio podoba. Chciałbym, żeby było to jak najprostsze.

1

No w końcu żem wymyślił. Patrzyłem na swój model i nagle mnie olśniło, że przecież NHibernate miało podobny problem z lazy-loadingiem. Po to właściwości są wirtualne, bo NHibernate zwraca nam de facto obiekt, którego klasa dziedziczy po naszym modelu. No i ja mogę zrobić to samo!

Model domenowy zostaje nam taki sam, a tworzymy nowy, który po nim dziedziczy.

public class PersonEntity : Person
{
    public virtual IList<Person> FatherChildren { get; set; }
    public virtual IList<Person> MotherChildren { get; set; }

    public override IEnumerable<Person> Children
    {
        get
        {
            return Gender == Domain.Gender.Male ? FatherChildren : MotherChildren;
        }
    }
}

Odpowiednio zmieniamy nasze mapowania:

public class PersonMap : ClassMap<PersonEntity>
{
    public PersonMap()
    {
        Table("People");

        Id(x => x.Id)
            .GeneratedBy.Guid();
            
        References<PersonEntity>(x => x.Father)
            .Cascade.All();

        References<PersonEntity>(x => x.Mother)
            .Cascade.All();

        HasMany<PersonEntity>(x => x.FatherChildren)
            .KeyColumn("FatherId")
            .Inverse()
            .ReadOnly();

        HasMany<PersonEntity>(x => x.MotherChildren)
            .KeyColumn("MotherId")
            .Inverse()
            .ReadOnly();

        Map(x => x.FirstName);
        Map(x => x.LastName);
        Map(x => x.Gender);
    }
}

I nasze repozytorium:

public Person GetById(Guid id)
{
    using (session.BeginTransaction())
    {
        var entity = session.Get<PersonEntity>(id);

        return entity;
    }
}

Tadam. Mamy zupełnie oddzielony, niezaśmiecony model domenowy!

1 użytkowników online, w tym zalogowanych: 0, gości: 1