none
Asp.Net MVC EntityFramework Exibir Relacionamento RRS feed

  • Pergunta

  • Amigos, estou desenvolvendo uma aplicação em Asp.Net MVC utilizando o ORM EntityFramework Code First. Antes de expor a minha duvida gostaria de informar que já vasculhei a internet em busca de uma solução, mas não consegui a resposta da forma que necessito.
    Vamos lá, vou tentar ser o mais claro possível para facilitar a compreensão do projeto. Ainda estou no inicio deste projeto mas estou patinando a mais de 3 dias na questão do retorno das informações relacionadas para a minha view Detalhes e Edição. Neste momento possuo 3 entidades (Cliente + ClienteEndereco + ClienteFone) e as mesmas foram criadas da seguinte forma:

    Classes das Entidades:
    Cliente
        public class Cliente
        {
            public int IdCliente { get; set; }
            public string Nome { get; set; }

            //Relacionamento Pai/Filho definindo 1:N
            public virtual ICollection<ClienteEndereco> Enderecos { get; set; }
            public virtual ICollection<ClienteFone> Fones { get; set; }
        }

    ClienteEndereco
        public class ClienteEndereco
        {
            public int IdEnderco { get; set; }
            public string Logradouro { get; set; }

            public string Complemento { get; set; }
            public string Bairro { get; set; }
            public string Cidade { get; set; }
            public string Estado { get; set; }
            public string Cep { get; set; }

            //Relacionamento Pai/Filho
            public int IdCliente { get; set; }
            public virtual Cliente Clientes { get; set; }
        }

    ClienteFone
       public class ClienteFone
        {
            public int IdFone { get; set; }
            public string TipoFone { get; set; }
            public string NumFone { get; set; }

            //Relacionamento Pai/Filho
            public int IdCliente { get; set; }
            public virtual Cliente Clientes { get; set; }
        }

    ViewModelCliente
        public class CadastroClienteViewModel
        {
            public Cliente TabCliente { get; set; }
            public ClienteEndereco TabClienteEndereco { get; set; }
            public ClienteFone TabClienteFone { get; set; }
        }
    Classe Contexto:
        public class Contexto : DbContext
        {
            public Contexto() : base("DbProjeto")
            {
                Configuration.LazyLoadingEnabled = false;
                Configuration.ProxyCreationEnabled = false;
            }

            //Tabela Cadastro Cliente
            public DbSet<Cliente> TabelaCliente { get; set; }

            //Tabela Cadastro Cliente Endereco
            public DbSet<ClienteEndereco> TabelaEndereco { get; set; }

            //Tabela Cadastro Cliente Fone
            public DbSet<ClienteFone> TabelaFone { get; set; }

            protected override void OnModelCreating(DbModelBuilder modelBuilder)
            {
                // ####################### Definições do Banco #######################
                //Remove a Pluralização das Tabelas
                modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();

                //Desabilitar a Deleção em Cascata Um Para Muitos
                modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();

                //Desabilitar a Deleção em Cascata Muitos Para Muitos
                modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();

                //Toda propriedade do tipo string na entidade POCO seja configurado como VARCHAR no DB
                modelBuilder.Properties<string>().Configure(p => p.HasColumnType("varchar"));

                /*Toda propriedade do tipo string na entidade POCO seja configurado como VARCHAR tamanho máximo de (300) no banco de dados */
                modelBuilder.Properties<string>().Configure(p => p.HasMaxLength(300));

                // ####################### Mapeamento das Tabelas #######################
                modelBuilder.Configurations.Add(new MapTabelaCliente());
                modelBuilder.Configurations.Add(new MapTabelaClienteEndereco());
                modelBuilder.Configurations.Add(new MapTabelaFones());
            }
        }

    Mapeamento das Tabelas:
    Cliente
        public class MapTabelaCliente : EntityTypeConfiguration<Cliente>
        {
            public MapTabelaCliente()
            {
                /*O método ToTable define qual o nome que será dado a tabela no banco de dados*/
                ToTable("TabCliente");

                //Definição da chave primária
                HasKey(x => x.IdCliente);

                Property(x => x.IdCliente).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity).HasColumnName("IdCliente").HasColumnOrder(0);
                Property(x => x.Nome).HasMaxLength(60).HasColumnName("Nome").HasColumnOrder(1);
            }
        }

    ClienteEndereco
        public class MapTabelaClienteEndereco : EntityTypeConfiguration<ClienteEndereco>
        {
            public MapTabelaClienteEndereco()
            {
                /*O método ToTable define qual o nome que será dado a tabela no banco de dados*/
                ToTable("TabClienteEndereco");

                //Definição da chave primária
                HasKey(x => x.IdEnderco);

                Property(x => x.IdEnderco).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity).HasColumnName("IdEnderco").HasColumnOrder(0);


                Property(x => x.Logradouro).HasMaxLength(50).HasColumnName("Logradouro").HasColumnOrder(10);
                Property(x => x.Complemento).HasMaxLength(25).HasColumnName("Complemento").HasColumnOrder(12);
                Property(x => x.Bairro).HasMaxLength(35).HasColumnName("Bairro").HasColumnOrder(13);
                Property(x => x.Cidade).HasMaxLength(40).HasColumnName("Cidade").HasColumnOrder(14);
                Property(x => x.Estado).HasMaxLength(2).HasColumnName("Estado").HasColumnOrder(15);
                Property(x => x.Cep).HasMaxLength(10).HasColumnName("Cep").HasColumnOrder(16);

                // ####################### Relacionamentos #######################
                //Relacionamento Tipo: Um para Muitos
                HasRequired(x => x.Clientes).WithMany(x => x.Enderecos).HasForeignKey(x => x.IdCliente);

            }
        }

    ClienteFone
        public class MapTabelaFones : EntityTypeConfiguration<ClienteFone>
        {
            public MapTabelaFones()
            {
                /*O método ToTable define qual o nome que será dado a tabela no banco de dados*/
                ToTable("TabClienteFones");

                //Definição da chave primária
                HasKey(x => x.IdFone);

                Property(x => x.IdFone).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity).HasColumnName("IdFone").HasColumnOrder(0);


                Property(x => x.TipoFone).HasMaxLength(50).HasColumnName("TipoFone").HasColumnOrder(10);
                Property(x => x.NumFone).HasMaxLength(12).HasColumnName("NumFone").HasColumnOrder(12);

                // ####################### Relacionamentos #######################
                //Relacionamento Tipo: Um para Muitos
                HasRequired(x => x.Clientes).WithMany(x => x.Fones).HasForeignKey(x => x.IdCliente);
            }
        }
    }


    Controller ==> Salvar Cadastro

            [HttpPost]
            [ValidateAntiForgeryToken]
            public ActionResult SalvarCliente(CadastroClienteViewModel cadCliente)
            {
                var ctx = new ConexaoDb();
                if (ModelState.IsValid)
                {

                    var c = new Cliente();
                    var e = new ClienteEndereco();
                    var f = new ClienteFone();

                    c = cadCliente.TabCliente;
                    e = cadCliente.TabClienteEndereco;
                    f = cadCliente.TabClienteFone;

                    ctx.Set<Cliente>().Add(c);
                    ctx.Set<ClienteEndereco>().Add(e);
                    ctx.Set<ClienteFone>().Add(f);
                    ctx.SaveChanges();

                }
                return View(cadCliente);
            }

    Em resumo, na view cadastro de cliente esta tudo OK e já estou até conseguindo salvar nas 3 tabelas de uma unica vez. Como vocês podem ter observado, a classe Cliente possui o relacionamento do tipo 1:N com as classes ClienteEndereco e ClienteFone.
    O meu problema esta em recuperar através de uma query unica (não sei se é possível) a informação deste cadastro de tal forma que eu possa trabalhar as informações do cliente tanto na controller ou exibi-las nas views Edição e Detalhes, pois quando digito por exemplo cliente.nome (tabela pai) este esta ok, mas se digito cliente.logradouro ou outro campo qualquer de uma das tabelas "filho" exemplo cliente.logradouro o sistema não identifica estes campos no intelicense. Lembrando que sempre necessitarei da tabela Cliente como forma padrão de exibir todos os dados de cada cliente.

    Neste momento estou utilizando a seguinte query para retornar os dados de um determinado cliente: var retCli = ctx.TabelaCliente.Include(x => x.Enderecos).Include(x => x.Fones).FirstOrDefault(x => x.IdCliente == 8);

    Me parece que esta tudo certo com a query, pois ao passar o mouse por cima da variável retCli  a mesma me exibe as informações das 3 tabelas.

    Onde estou errando? Lembrando que cada Cliente poderá fornecer quantos fones e endereços forem necessários devido ao negocio empregado neste projeto.

    segunda-feira, 26 de outubro de 2015 17:10

Respostas

  • Kleber, não sei se entendi certo, mas para pegar os "filhos" da entidade pai, como se trata de uma lista, deve se informar um index ou acessar dentro de um lado de repetição como foreach, for e etc...veja

    Cliente.Logradouro[0].NomeCampo;

    ou assim em um laço de repetição

    foreach (var item in cliente.logradouro)
    {
       item.NomeCampoFilho;
    }


    segunda-feira, 26 de outubro de 2015 17:22
  • Ele está dando esse erro por conta da interface ICollection, se você quiser a única ou a primeira da lista de Endereços, use LINQ

    ficaria assim:

    var cliEndereco= cliente.Enderecos.FirstOrDefault().Logradouro;
    
    //por id
    var cliEndereco= cliente.Enderecos.FirstOrDefault(x=>x.IdEnderco == 10).Logradouro;


    segunda-feira, 26 de outubro de 2015 18:23

Todas as Respostas

  • Kleber, não sei se entendi certo, mas para pegar os "filhos" da entidade pai, como se trata de uma lista, deve se informar um index ou acessar dentro de um lado de repetição como foreach, for e etc...veja

    Cliente.Logradouro[0].NomeCampo;

    ou assim em um laço de repetição

    foreach (var item in cliente.logradouro)
    {
       item.NomeCampoFilho;
    }


    segunda-feira, 26 de outubro de 2015 17:22
  • Olá Diego,

    É da forma que você compreendeu mesmo! Com base na sua resposta utilizei as duas formas apresentadas da seguinte forma:

                var ctx = new ConexaoDb();
                //Recuperar dados do cliente
                var cliente= ctx.TabelaCliente.Include(x => x.Enderecos).Include(x => x.Fones).FirstOrDefault(x => x.IdCliente == 8);

                foreach (var item in cliente.Enderecos)
                {
                    var n = item.Bairro;
                }

    Neste caso esta ok!!! (já resolveu 50%) mas acho que a outra forma que você demonstrou seria a mais conveniente neste momento, pois a principio somente um endereço e um telefone será gravado nas tabelas . Não fiz o relacionamento 1:1 devido justamente saber que futuramente será possível a inclusão de mais um registro para as tabelas informadas. Acontece que ao informar:

    var cliEndereco= cliente.Enderecos[0].Logradouro;

    Uma mensagem de erro é apresentada "Error    2    Cannot apply indexing with [] to an expression of type 'System.Collections.Generic.ICollection<mvc.Models.ClienteEndereco>' "

    Me parece que falta pouco :)

    segunda-feira, 26 de outubro de 2015 18:13
  • Ele está dando esse erro por conta da interface ICollection, se você quiser a única ou a primeira da lista de Endereços, use LINQ

    ficaria assim:

    var cliEndereco= cliente.Enderecos.FirstOrDefault().Logradouro;
    
    //por id
    var cliEndereco= cliente.Enderecos.FirstOrDefault(x=>x.IdEnderco == 10).Logradouro;


    segunda-feira, 26 de outubro de 2015 18:23
  • Diego,


    Gostaria de agradecer pela ajuda fornecida. Acredito que muitos desenvolvedores assim como eu que migraram do ADO para o Entity passaram por diversas dificuldades na implantação deste ORM. No meu caso em particular posso dizer que uma simples dica sua "var cliEndereco= cliente.Enderecos.FirstOrDefault(x=>x.IdEnderco == 10).Logradouro;" me fez entender algo muito maior, a questão talvez seja a famosa "ABSTRAÇÃO", ou seja, tudo muda quando você olha da forma correta para um determinado assunto. Neste exato momento estou conseguindo trabalhar com muito mais fluidez no projeto e até implantando outras melhorias como por exemplo o IoC e AutoMapper.

    Mais uma vez muito obrigado pela força!!!!!!!!!!!

    terça-feira, 27 de outubro de 2015 13:48
  • Opa Kleber, que nada...

    todos que usam EF hoje um dia tiveram as mesmas dúvidas, ajudando a gente aprende também!

    Abraço!

    terça-feira, 27 de outubro de 2015 14:44