Связи NHibernate «многие ко многим», делающие оба конца родительскими, с помощью сущности отношения в модели предметной области - PullRequest
1 голос
/ 18 декабря 2009

Объекты: Команда <-> TeamEmployee <-> Сотрудник

Требования:

  • Команда и Сотрудник могут существовать без своего партнера.
  • В отношении Team-TeamEmployee команда несет ответственность (родитель) [используя позже TeamRepository].
  • В отношении Employee-TeamEmployee Сотрудник несет ответственность (родитель) [используя позже EmployeeRepository].
  • Дубликаты не допускаются.
  • При удалении команды все сотрудники в команде удаляются, если сотрудник не входит в другую команду.
  • При удалении сотрудника удаляется только команда, если в команде больше нет сотрудников.

Отображение:

public class TeamMap : ClassMap<Team>
{
    public TeamMap()
    {
        // identity mapping
        Id(p => p.Id)
            .Column("TeamID")
            .GeneratedBy.Identity();

        // column mapping
        Map(p => p.Name);

        // associations
        HasMany(p => p.TeamEmployees)
            .KeyColumn("TeamID")
            .Inverse()
            .Cascade.SaveUpdate()
            .AsSet()
            .LazyLoad();
    }
}

public class EmployeeMap : ClassMap<Employee>
{
    public EmployeeMap()
    {
        // identifier mapping
        Id(p => p.Id)
            .Column("EmployeeID")
            .GeneratedBy.Identity();

        // column mapping
        Map(p => p.EMail);
        Map(p => p.LastName);
        Map(p => p.FirstName);

        // associations
        HasMany(p => p.TeamEmployees)
            .Inverse()
            .Cascade.SaveUpdate()
            .KeyColumn("EmployeeID")
            .AsSet()
            .LazyLoad();

        HasMany(p => p.LoanedItems)
            .Cascade.SaveUpdate()
            .LazyLoad()
            .KeyColumn("EmployeeID");
    }
}

public class TeamEmployeeMap : ClassMap<TeamEmployee>
{
    public TeamEmployeeMap()
    {
        Id(p => p.Id);

        References(p => p.Employee)
            .Column("EmployeeID")
            .LazyLoad();

        References(p => p.Team)
            .Column("TeamID")
            .LazyLoad();
    }
}

Создание сотрудников и команд:

    var employee1 = new Employee { EMail = "Mail", FirstName = "Firstname", LastName = "Lastname" };
    var team1 = new Team { Name = "Team1" };
    var team2 = new Team { Name = "Team2" };

    employee1.AddTeam(team1);
    employee1.AddTeam(team2);


    var employee2 = new Employee { EMail = "Mail2", FirstName = "Firstname2", LastName = "Lastname2" };
    var team3 = new Team { Name = "Team3" };

    employee2.AddTeam(team3);
    employee2.AddTeam(team1);

    team1.AddEmployee(employee1);
    team1.AddEmployee(employee2);
    team2.AddEmployee(employee1);
    team3.AddEmployee(employee2);

    session.SaveOrUpdate(team1);
    session.SaveOrUpdate(team2);
    session.SaveOrUpdate(team3);

    session.SaveOrUpdate(employee1);
    session.SaveOrUpdate(employee2);

После этого я фиксирую изменения с помощью транзакции.Commit (). Первая странная вещь заключается в том, что я должен сохранить команды и сотрудников вместо одного (почему ?!). Если я сохраню только все команды или (Xor) всех сотрудников, тогда я получу TransientObjectException :

"объект ссылается на несохраненный временный экземпляр - сохранить переходный процесс до промывки. Тип: Core.Domain.Model.Employee, Entity: Core.Domain.Model.Employee "

Когда я сохраняю все созданные Команды и Сотрудники, все отлично сохраняется, НО таблица отношений TeamEmployee имеет дублирующихся ассоциаций .

ID EID TID
1  1   1
2  2   1
3  1   2
4  2   3
5  1   1
6  1   2
7  2   3
8  2   1

Таким образом, вместо 4 отношений есть 8 отношений. 4 отношения для левой стороны и 4 отношения для правой стороны. : [

Что я не прав?

Дополнительные вопросы: Когда я удаляю команду или сотрудника, нужно ли мне удалять команду или сотрудника из списка TeamEmployee в объектной модели или NHibernate выполняет работу за меня (используя session.delete (..) )

Ответы [ 4 ]

2 голосов
/ 02 января 2010

Вы говорите о бизнес-логике. NHibernate не предназначен для реализации бизнес-логики.

Что делает ваш код:

Вы отобразили две разные коллекции TeamEmployee s, одну в Team, одну в Employee. В своем коде вы добавляете элементы в обе коллекции, каждый раз создавая новые экземпляры TeamEmployee. Так почему вы ожидаете, что NHibernate не должен хранить все эти отдельные экземпляры?

Что вы можете сделать, чтобы исправить это:

Вы сделали TeamEmployee сущностью (в отличие от типа значения). Чтобы создать экземпляр только один раз, вам нужно будет создать его экземпляр только один раз в памяти и повторно использовать в обеих коллекциях. Делайте это только тогда, когда вам действительно нужен этот класс в вашей доменной модели. (например, потому что он содержит дополнительную информацию об отношениях и фактически является собственной сущностью.)

Если вам не нужен класс, гораздо проще отобразить его как отношение «многие ко многим» (как уже было предложено Крисом Конвеем ). Поскольку в памяти есть две коллекции, которые, как ожидается, будут содержать одни и те же данные, вы говорите NHibernate игнорировать одну из них при сохранении, используя Inverse.

родительская проблема на обоих концах

На обоих концах нет родителя . Я думаю, что ясно, что ни команда, ни сотрудник не являются родителями другого, они независимы. Вы, вероятно, имеете в виду, что они оба родители среднего уровня TeamEmployee. Они не могут быть родителями (и, следовательно, владельцами) одного и того же экземпляра. Либо один из них является родительским, либо это другой независимый экземпляр, что значительно усложняет управление им (именно так вы реализовали его сейчас). Если вы отобразите его как отношение «многие ко многим», оно будет управляться NHibernate.

Для выполнения вашей бизнес-логики:

  • хранение новых команд и новых сотрудников
  • управление отношениями и их синхронизация
  • удаление команд и сотрудников, когда они больше не используются. (В NHibernate нет явной реализации постоянного сбора мусора по нескольким причинам.)
1 голос
/ 18 декабря 2009

Ознакомьтесь с этим учебным пособием и, в частности, с настройкой сопоставления между Product и Store.

1 голос
/ 18 декабря 2009

Похоже, вам нужно HasManyToMany вместо двух карт HasMany. Кроме того, в TeamEmployeeMap нет необходимости, если в этой таблице нет другого свойства, которое необходимо сопоставить. Другое дело, что только у одной стороны должен быть установлен Inverse (), и, поскольку вы добавляете команды к сотрудникам, я думаю, что вы должны сделать TeamMap обратным. Наличие обратной стороны только на одной стороне избавит от повторяющихся записей в базе данных.

Может быть, что-то вроде этого:

public class TeamMap : ClassMap<Team>
{
    public TeamMap()
    {
        // identity mapping
        Id(p => p.Id)
           .Column("TeamID")
           .GeneratedBy.Identity();

        // column mapping
        Map(p => p.Name);

        // associations
        HasManyToMany(x => x.TeamEmployees)
            .Table("TeamEmployees")
            .ParentKeyColumn("TeamID")
            .ChildKeyColumn("EmployeeID")
            .LazyLoad()
            .Inverse()
            .AsSet();
    }
}

public class EmployeeMap : ClassMap<Employee>
{
    public EmployeeMap()
    {
        // identifier mapping
        Id(p => p.Id)
            .Column("EmployeeID")
            .GeneratedBy.Identity();

        // column mapping
        Map(p => p.EMail);
        Map(p => p.LastName);
        Map(p => p.FirstName);

        // associations
        HasManyToMany(x => x.TeamEmployees)
            .Table("TeamEmployees")
            .ParentKeyColumn("EmployeeID")
            .ChildKeyColumn("TeamID")
            .Cascade.SaveUpdate()
            .LazyLoad()
            .AsSet();

        HasMany(p => p.LoanedItems)
            .Cascade.SaveUpdate()
            .LazyLoad()
            .KeyColumn("EmployeeID");
    }
}

Используя это, удаление удалит TeamEmployee из базы данных для вас.

0 голосов
/ 30 декабря 2009

NHibernate не позволяет многим-многим общаться с родителями на обоих концах.

...