[EF6] - mapeamento Many-To-Many com Auto-Relacionamento

304 views
Skip to first unread message

Bruno Fernandes

unread,
Mar 2, 2017, 9:17:21 PM3/2/17
to Dot Net Brasil
Prezados, boa noite!

Estou trabalhando num projeto C# com EF6 utilizando as diretrizes do code first. O contexto do requisito é o seguinte:

O sistema possui um cadastro de clientes. Cada cliente irá pagar um valor mensalmente (ex: R$10,00).
Mas, se o cliente cadastrado indicar novos clientes, ele terá direito à um desconto (de 10% por exemplo);
O sistema de desconto, funciona assim: Cada indicação possui um período de vigência e é cumulativo;
Para cada novo cliente indicado, é concedido o desconto durante o período de 1 ano enquanto o cliente indicado estiver ativo (pagando).

Minhas classes de negócio estão assim:

public class Cliente
{
    public virtual Int32 Id { get; set; }
    public virtual HashSet<Indicacao> Indicacoes { get; set; }
    // outros atributos
}

public class Indicacao : Entidade<Int64>
{
    public virtual Int32 Id { get; set; }
    public virtual Cliente Cliente { get; set; } // Cliente Indicador que receberá o desconto
    public virtual Cliente ClienteIndicado { get; set; }
    public DateTime DataIndicacao { get; set; }
    public DateTime InicioDeVigencia { get; set; }
    public DateTime FinalDeVigencia { get; set; }
    public Decimal PercentualBonus { get; set; }
}

Estou com dificuldade de fazer o mapeamento Muitos pra muitos com auto relacionamento, ou seja, cada Cliente se relaciona com outros Clientes através da indicação. Alguém poderia me ajudar?


Atenciosamente,

Marcus Alexandre Silva

unread,
Mar 3, 2017, 7:22:40 AM3/3/17
to dotn...@googlegroups.com
Já tentou usar IList<Indicacao> ao invéz de HashSet<Indicacao> e marcar todas as propriedades de Indicacao como virtual?

--
==============================
Comunidade de desenvolvedores Dot Net no Brasil
 
Facebook: www.facebook.com/grupodotnetbr
 
WebSite: www.dotnetbr.com
 
E-mail do Grupo: dotn...@googlegroups.com
==============================
---
You received this message because you are subscribed to the Google Groups "DotNet Brasil" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dotnet_br+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Bruno Fernandes

unread,
Mar 3, 2017, 8:28:09 AM3/3/17
to Dot Net Brasil
Ok, Marcus! Vou tentar e depois posto os resultados aqui!

PS: Eu acabei não explanando o que está acontecendo... rsrs! O problema é que na hora de gerar o modelo de dados, a tabela de indicação está sendo criada assim:

CREATE TABLE Indicacao(
Id bigint IDENTITY(1, 1) NOT NULL,
DataIndicacao datetime NOT NULL,
InicioDeVigencia datetime NOT NULL,
FinalDeVigencia datetime NOT NULL,
PercentualBonus decimal(10, 3) NOT NULL,
ClienteId int NOT NULL,
ClienteIndicadoId int NOT NULL,
Cliente_Id1 int NULL, /* <- Esse campo não deveria existir */
CONSTRAINT PK_Indicacao PRIMARY KEY CLUSTERED (Id ASC) ON [PRIMARY];




Atenciosamente,

Gustavo Cruz

unread,
Mar 3, 2017, 9:01:21 AM3/3/17
to dotn...@googlegroups.com
Posta o seu mapping aí pra gente dar uma olhada...

Bruno Fernandes

unread,
Mar 3, 2017, 9:31:30 AM3/3/17
to Dot Net Brasil
Fala Gustavo...

Então... eu estou usando a estratégia de Convenção Sobre Configuração (CoC). Sendo assim, eu não tenho nenhum mapeamento em minha aplicação.

Mas por causa desta situação que está acontecendo, vou precisar mapear essas entidades de forma explicita. O problema é que pela facilidade de usar CoC (e por comodismo de minha parte), eu acabei não aprendendo a mapear as entidades no EF.

Já procurei várias sites no Google que ensinam a mapear entidades muitos pra muitos, mas não achei nada semelhante ao que preciso com esse "auto-relacionamento". Dessa forma, se puderem me ajudar, ou indicar algum link que me ajude à fazer esse mapeamento, seria muito bem vindo...

Desde já agradeço a atenção e o tempo de todos. Obrigado!

Atenciosamente,

Wesley Baldan

unread,
Mar 3, 2017, 10:04:43 AM3/3/17
to dotn...@googlegroups.com
Por sorte, já sofri com esta mesma situação... A solução não tem lógica...mas funciona.... Provavelmente é algum "bug" do EF, mas só funciona mapeando uma das propriedades de Cliente Como Optional.....

public class IndicacaoMap : EntityTypeConfiguration<Indicacao>
    {
        public IndicacaoMap()
        {
            HasOptional(x => x.Cliente).WithMany();
            //HasOptional(x => x.ClienteIndicado).WithMany();
            //HasRequired(x => x.ClienteIndicado).WithMany();
        }
    }

Para entender que só assim funciona, vai brincando com os itens comentados e veja ele zuando toda a criação da tabela.....

Tabela que o EF irá gerar com esta configuracação.....

 CreateTable(
                "dbo.Indicacao",
                c => new
                    {
                        Id = c.Int(nullable: false, identity: true),
                        DataIndicacao = c.DateTime(nullable: false, precision: 7, storeType: "datetime2"),
                        InicioDeVigencia = c.DateTime(nullable: false, precision: 7, storeType: "datetime2"),
                        FinalDeVigencia = c.DateTime(nullable: false, precision: 7, storeType: "datetime2"),
                        PercentualBonus = c.Decimal(nullable: false, precision: 18, scale: 2),
                        ClienteIndicadoId = c.Int(),
                        ClienteId = c.Int(),
                    })
                .PrimaryKey(t => t.Id)
                .ForeignKey("dbo.Cliente", t => t.ClienteIndicadoId)
                .ForeignKey("dbo.Cliente", t => t.ClienteId)
                .Index(t => t.ClienteIndicadoId, name: "IX_ClienteIndicado_Id")
                .Index(t => t.ClienteId, name: "IX_Cliente_Id");
            
            CreateTable(
                "dbo.Cliente",
                c => new
                    {
                        Id = c.Int(nullable: false, identity: true),
                    })
                .PrimaryKey(t => t.Id);

Gustavo Cruz

unread,
Mar 3, 2017, 10:16:40 AM3/3/17
to dotn...@googlegroups.com
Tenho medo do Migrations. Para projetos médios e grandes sempre acaba virando uma bela de uma dor de cabeça....


--

Flávio Alencar

unread,
Mar 3, 2017, 11:00:48 AM3/3/17
to dotn...@googlegroups.com
Acredito que seja o mesmo problema que passei.

Veja esta thread que abri para um projeto acadêmico:


Resumindo, você precisa ter duas propriedades navegacionais na classe Cliente.. 

public virtual HashSet<Indicacao> IndicacoesIndicador { get; set; }
public virtual HashSet<Indicacao> IndicacoesIndicado { get; set; }  

Veja o exemplo no link para ver se resolve. 


Flávio


--
==============================
Comunidade de desenvolvedores Dot Net no Brasil
 
Facebook: www.facebook.com/grupodotnetbr
 
WebSite: www.dotnetbr.com
 
E-mail do Grupo: dotn...@googlegroups.com
==============================
---
You received this message because you are subscribed to the Google Groups "DotNet Brasil" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dotnet_br+...@googlegroups.com.

Bruno Fernandes

unread,
Mar 3, 2017, 11:05:04 AM3/3/17
to Dot Net Brasil
Obrigado pelas contribuições pessoal!

@Wesley​, @Flávio
Vou tentar aplicar vossas dicas e dou notícias em breve!

@Gustavo, 
Este é o primeiro projeto usando CodeFirst / Migrations que estou fazendo junto com um amigo. O projeto está servindo como prova de conceito para aprendizado (até mesmo para o desenvolvimento de outros projetos no futuro).

Nós já assumindo que vamos passar por algumas dificuldades no decorrer do desenvolvimento e manutenção dele. E toda a contribuição que tivermos agora será bem vinda e nos ajudará a direcionar o projeto no caminho certo.

Sua percepção em relação ao Migration é um ponto que não podemos ignorar. Sendo assim, o que sugere como alternativa ao Migration?


Atenciosamente,

Flávio


To unsubscribe from this group and stop receiving emails from it, send an email to dotnet_br+unsubscribe@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--
==============================
Comunidade de desenvolvedores Dot Net no Brasil
 
Facebook: www.facebook.com/grupodotnetbr
 
WebSite: www.dotnetbr.com
 
E-mail do Grupo: dotn...@googlegroups.com
==============================
---
You received this message because you are subscribed to the Google Groups "DotNet Brasil" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dotnet_br+unsubscribe@googlegroups.com.

Wesley Baldan

unread,
Mar 3, 2017, 11:06:52 AM3/3/17
to dotn...@googlegroups.com
Que estranho isto.... O domínio de cliente ficaria bem estranho e feio...

Ats,
Wesley

Em 3 de março de 2017 13:00, Flávio Alencar <flav...@gmail.com> escreveu:
Flávio



To unsubscribe from this group and stop receiving emails from it, send an email to dotnet_br+unsubscribe@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--
==============================
Comunidade de desenvolvedores Dot Net no Brasil
 
Facebook: www.facebook.com/grupodotnetbr
 
WebSite: www.dotnetbr.com
 
E-mail do Grupo: dotn...@googlegroups.com
==============================
---
You received this message because you are subscribed to the Google Groups "DotNet Brasil" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dotnet_br+unsubscribe@googlegroups.com.

Wesley Baldan

unread,
Mar 3, 2017, 11:07:42 AM3/3/17
to dotn...@googlegroups.com
Nunca tive problema com o Migrations...... Até agora só foi solução para mim :-)

Ats,
Wesley

Gustavo Cruz

unread,
Mar 3, 2017, 12:01:41 PM3/3/17
to dotn...@googlegroups.com
Bruno, o problema do migrations é manter

Quando o projeto começa a crescer e as coisas começam a entrar em produção, o migrations torna-se mais dor de cabeça do que solução. Sempre tem aquele "apaga incêndio" que surge de última hora e é ele que faz a coisa sair de linha. E estou falando em cenários enterprise-level, onde você tem múltiplos domínios, contextos diferenciados, enfim, algo maior. Para projetos pequenos e para modelagem inicial pode até ser válido. Mas cada um com sua experiência. No final das contas a gente perdia mais tempo tentando ajustar as migrations (e os conflitos que ele causava mediante a modelagem constantemente) do que se fizesse a bagaça na mão.

Um outro problema que encontramos no Code First, é que ele mistura conceitos que são completamente diferentes no DDD (infra e domain). Quando você começa a trabalhar mais orientado a domínio e usar os conceitos que realmente são a cereja do bolo do DDD (bounded countexts por exemplo), você depara com issues que travam esse processo e te fazem cada vez mais querer não usar esse monte de ferramentas. Já rolaram vários flames no grupo inclusive sobre isso. O importante é sempre ter em mente: Banco de dados é um issue de infra, não de domínio. Quando você enxerga e consegue separar bem esses conceitos, aí começam as complicações (quem usa data-anottations por exemplo sabe do que estou falando).

Mas enfim, se é conceitual e de aprendizado, vá seguindo o caminho que você naturalmente irá achar os obstáculos.

Marcus Alexandre Silva

unread,
Mar 3, 2017, 1:31:24 PM3/3/17
to dotn...@googlegroups.com
Deixa bem claro que voce esta falando das migrations do EF.
Nós aqui utilizamos a migration "FluentMigration" com NHibernate. E vou te falar que é uma mão na roda.

Mark Gonçalves

unread,
Mar 3, 2017, 2:36:22 PM3/3/17
to Dotnet Br
1) Usei o Migrations por um bom tempo, e o grande ponto é organização, não se pode fazer as coisas de qualquer forma.
2) Sobre o mapeamento, estou migrando meus projetos para EF Core e acabei aceitando fazer a classe associativa  na mão, já que o EF Core não tem o mapeamento ManyToMany.

Mark Gonçalves


Algumas das maiores façanhas do mundo foram feitas por pessoas que não eram suficientemente espertas, para saber que elas eram impossíveis.



Bruno Fernandes

unread,
Mar 3, 2017, 2:40:19 PM3/3/17
to Dot Net Brasil
Pessoal, obrigado pelas contribuições!


Atenciosamente,

Gustavo Cruz

unread,
Mar 6, 2017, 4:00:28 PM3/6/17
to dotn...@googlegroups.com
Pra NH eu não conheço...eu parti do pressuposto que ele tava usando o migrations original do EF. 
Pelo menos aqui pra gente no projeto foi mais dor de cabeça do que solução, principalmente porque o modelo é complexo demais e o acoplamento entre as entidades é muito alto.

Charles Wellington de Oliveira Fortes

unread,
Mar 7, 2017, 7:40:43 AM3/7/17
to dotn...@googlegroups.com
To com o Marcus, migration pra mim mesmo em enterprise-level é solução e ajuda de mais.

Quanto a programar pro dominio (DDD), etc. Não vejo as separações + o tratamento do banco como infra um problema, na verdade, acho bem melhor pra lidar e acho que ajuda a manutenção e evolução do projeto.
Charles Fortes

Engenheiro de Software e Professor na Faculdade Pitágoras
Comunidade Técnica Microsoft Belo Horizonte
DotNetRaptors www.100loop.com

Gustavo Cruz

unread,
Mar 7, 2017, 9:43:31 AM3/7/17
to dotn...@googlegroups.com
Depende muito da complexidade e de como você trabalha com o DDD. 

Experimente trabalhar com bounded context's (diga-se de passagem: a segunda parte do blue-book) e aí que os problemas começam. A ideia do Code-first é boa, mas ele inevitavelmente faz com que o desenvolvedor alie a ideia de infra (leia-se: banco de dados) ao domínio, o que é um dos maiores erros do DDD. Quando você começa a separar esses conceitos, você vê que são concerns completamente diferentes. 

O que eu vejo de forma corriqueira acontecer é o dev achar que as entidades dele representam um dado guardado em algum lugar. Isso é um dos maiores erros (pra falar a verdade acho que o mais comum) quando se usa DDD. Em tempo: isso não é errado, só não representa o "todo" do DDD. O code-first é de fato uma mão na roda, mas se num tempo não muito distante eu pudesse escolher não usá-lo (ou usá-lo de forma diferente, talvez isolando-o na infra) eu teria escolhido. E isso não digo isso apenas ao Code-first, refere-se à qualquer ORM.

Tem um grupo muito interessante sobre isso onde já rolaram algumas discussões bem interessantes sobre o uso de ORM aliado ao DDD (e também CQRS).






Bruno Fernandes

unread,
Mar 8, 2017, 7:33:07 PM3/8/17
to Dot Net Brasil
Pessoal, boa noite!

Estou passando para deixar um feedback. Eu consegui resolver a questão seguindo algumas sugestões que vocês deixaram, e basicamente, o que fiz, foi colocar essas linhas na minha classe de contexto:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
   // ... Outras configurações

   var indicacaoBuilder = modelBuilder.Entity<Indicacao>();
   indicacaoBuilder.HasRequired(x => x.Cliente).WithMany();
   indicacaoBuilder.HasRequired(x => x.ClienteIndicado);

   // ... Outras configurações

   base.OnModelCreating(modelBuilder);
}

Obrigado pela ajuda de todos!


Atenciosamente,
Reply all
Reply to author
Forward
0 new messages