Associações de Nibernate muitos para muitos, tornando as duas extremidades como pai, usando uma entidade de relacionamento no modelo de domínio

StackOverflow https://stackoverflow.com/questions/1928591

Pergunta

Entidades: Team <-> TeamEmployee <-> funcionário

Requisitos:

  • Uma equipe e um funcionário podem existir sem sua contraparte.
  • Na relação Team-TeamEmployee, a equipe é responsável (pai) Usando mais tarde um TeamRepository].
  • Na relação funcionário-teamployeue, o funcionário é responsável (pai) Usando posteriormente um empregaerepository].
  • Duplicatas não são permitidas.
  • Excluir uma equipe exclui todos os funcionários da equipe, se o funcionário não estiver em outra equipe.
  • Excluir um funcionário exclui apenas uma equipe, se a equipe não contiver mais funcionários.

Mapeamento:

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

Criando funcionários e equipes:

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

Depois disso, comprometo as alterações usando transação.Commit (). A primeira coisa estranha é que eu tenho que salvar equipes e funcionários, apenas uma delas (por quê?!). Se eu apenas salvar todas as equipes ou (xor) todos os funcionários, então eu recebo um TransientObjectException:

"O objeto faz referência a uma instância transitória não salva - salve a instância transitória antes de lavar. Tipo: core.Domain.model.Employee, entidade: core.domain.model.employee"

Quando eu salvo todas as equipes e funcionários criados, tudo economiza bem, mas a tabela de relações TeamEmployee tem assoções duplicadas.

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

Então, em vez de 4 relações, existem 8 relações. 4 Relações para o lado esquerdo e 4 relações para o lado direito. : [

O que eu estou errado?

Outras perguntas: Quando excluo uma equipe ou um funcionário, preciso remover a equipe ou o funcionário da lista de empreiteiros do TeamEmploy no modelo de objeto ou Nibernate faz o trabalho para mim (usando session.delete (..))?

Foi útil?

Solução

Você está falando sobre lógica de negócios. Não é o objetivo do Nibernate implementar a lógica de negócios.

O que seu código está fazendo:

Você mapeou duas coleções diferentes de TeamEmployees, um em Team, um em Employee. No seu código, você adiciona itens às duas coleções, criando novas instâncias de TeamEmployee cada vez. Então, por que você espera que o Nibernate não deva armazenar todas essas instâncias distintas?

O que você poderia fazer para consertar:

Você fez TeamEmployee uma entidade (em contraste com um tipo de valor). Para criar uma instância apenas uma vez, você teria que instanciá -la apenas uma vez na memória e reutilizá -la nas duas coleções. Faça isso apenas quando você realmente precisar desta classe no seu modelo de domínio. (Por exemplo, porque contém informações adicionais sobre as relações e é realmente uma entidade própria.)

Se você não precisa da aula, é muito mais fácil mapeá-la como uma relação muitos para muitos (como já proposto por Chris Conway). Como existem duas coleções na memória que devem conter os mesmos dados, você diz a Nibernate para ignorar um deles ao armazenar, usando Inverse.

O pai em ambas as extremidades problema

Não há Pai nas duas extremidades. Eu acho que está claro que nem a equipe nem o funcionário são pais do outro, eles são independentes. Você provavelmente quer dizer que ambos são pais do intermediário TeamEmployee. Eles não podem ser pais (e, portanto, proprietários) da mesma instância. Qualquer um deles é o pai, ou é outra instância independente, o que torna o gerenciamento muito mais complicado (é assim que você o implementou agora). Se você mapeá-lo como uma relação muitos para muitos, será gerenciado pelo Nibernate.

A ser feito pela sua lógica de negócios:

  • armazenando novas equipes e novos funcionários
  • Gerenciando as relações e mantê -las sincronizadas
  • Excluindo equipes e funcionários quando não forem mais usados. (Há explicitamente não Coleção de lixo persistente implementação no Nibernate, por vários motivos.)

Outras dicas

Parece que você precisa de um hasmanytomany em vez de dois mapas Hasmany. Além disso, não há necessidade do TEAMEMPLATEMAPAPELA, a menos que você tenha outra propriedade nessa tabela que precisa ser mapeada. Outra coisa, apenas um lado precisa ter o conjunto Inverse () e, como você está adicionando equipes aos funcionários, acho que precisa fazer do TeamMapa o inverso. Ter o inverso de um lado apenas se livrará das entradas duplicadas no banco de dados.

Talvez algo assim:

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

Usando isso, o Excluir excluirá o teamEmployee do banco de dados para você.

Verificação de saída este tutorial, e especificamente como o mapeamento entre Product e Store está configurado.

O Nhibernate não permite muitos para muitos dos pais nos dois lados.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top