Anúncios

EF Code First – Design Patterns – Asp.Net – Multi-Camadas – E muito trabalho!

Olá!

Na última semana preparei um post especial… Sim, especial! Mas qual o motivo do “especial”? Bem, como todos sabem, passo bastante tempo no fórum do MSDN, e assim adquiro experiência sobre quais técnologias estão utilizando e quais suas principais dúvidas. Desta forma, procuro aqui no blog responder boa parte destas perguntas com posts técnicos.

Diante disso, resolvi montar um exemplo maior, que cubra diversos aspectos e dúvidas que muitas pessoas tem apresentado. Neste post quero falar sobre: Relacionamento 1:N (um-para-muitos), relacionamento N:N (muitos-para-muitos), uso de uma aplicação multi-camadas com Entity Framework Code-First e o uso dos design patterns MVC e DAL.

view

Para fazer o download do exemplo acesse: http://code.msdn.microsoft.com/EF-Code-First-Design-1558305c

O modelo de dados

Neste exemplo vamos fazer uso de um modelo de dados para o cadastro de livros. Onde um livro possui uma única categoria e um ou mais autores associados a uma obra. Nosso modelo será algo como:

Modelo

A arquitetura

Neste exemplo faremos uso de dois design patterns, o MVC (Model-View-Controller) e o DAL (Data Access Layer). Estes dois design patterns são bastante utilizados e conhecidos pelo mercado. O MVC prevê que a sua aplicação possua três camadas, a View, a Model e a Controller. Cada uma destas camadas possui uma responsabilidade diferente. A View é responsável pela apresentação e controle dos componentes visuais de sua aplicação. Como este exemplo corresponde a uma aplicação ASP.Net, a View corresponde as páginas ASPX e todo o seu gerenciamento. A camada Model corresponde as entidades que faremos uso em nossa aplicação, no caso as entidades Book, Category e Author. Além disso, temos a camada Controller, esta camada geralmente é a que gera mais dúvidas. Na camada Controllers teremos o controle de nossa aplicação e a administraçã dos fluxos de dados vindos da View para sua persistência ou execução de lógicas que façam parte do domínio que estamos trabalhando.

O design pattern DAL, promove a separação da sua camada de acesso a dados do restante de sua aplicação. A vantagem do seu uso é termos concentrado em uma única camada todo o processo de persistência dos dados. Outra vantagem é criar uma aplicação independente de uma tecnologia de acessso a dados, para que se for necessário modificar sua plataforma de dados, este projeto seja o mais simples possível, gerando um impacto pequeno em sua aplicação, sem que (como acontece na maiorias das vezes) a aplicação tenha de ser reescrita novamente.

O EF Code First

Este é mais um exemplo de aplicação utilizando o EF Code First. Tenho visto muitas dúvidas sobre o modo como devemos persistir relacionamentos 1:N (um-para-muitos) e N:N (muitos-para-muitos) utilizando o EF. A classe Book deste exemplo possui estes dois relacionamentos, por isso vamos tomá-la como exemplo. Seu código é o seguinte:

public class Book
{
    public int Id { get; set; }

    public string Name { get; set; }

    public int CategoryId { get; set; }

    public Category Category { get; set; }

    public int Year { get; set; }

    public Authors Authors { get; set; }

    public Guid GlobalId { get; set; }

    public Book()
    {
        this.GlobalId = Guid.NewGuid();

        this.Authors = new Authors();
    }
}

– Persistindo relacionamentos 1:N – Podemos notar que a classe Book possui um atributo do tipo Category e um atributo int de nome CategoryId. O uso desta propriedade é indicado quando tivermos este tipo de relacionamento, pois o processo de atualização é muito mais simples e não exige a escrita de código para gerenciar o estado da entidade associada. Veja mais neste post de Diego Vega: http://blogs.msdn.com/b/diego/archive/2010/10/06/self-tracking-entities-applychanges-and-duplicate-entities.aspx

– Persistindo relacionamento N:N – Este relacionamento é mais complexo, e exige que o bloco responsável pela atualização dos registro tenha uma rotina de comparação entre os IDs das entidades que estão na base de dados e as entidades que estão em memória. No nosso exemplo, podemos ver esta cardinalidade ao persistirmos os dados dos autores dos livros.

O bloco de código abaixo apresenta o código necessário para persistirmos a entidade Book com todas os seus diferentes tipos de associações:

public void Save(Model.Book book)
{
    using (DataContext context = new DataContext())
    {                
        if (book.Id == 0)
        {
            context.Entry(book.Category).State = EntityState.Unchanged;
            foreach (var author in book.Authors)
            {
                context.Entry(author).State = EntityState.Unchanged;
            }

            context.Books.Add(book);                    
        }
        else
        {
            var bookToUpdate = context.Books.Include("Category").Include("Authors").Where(b => b.Id == book.Id).Single();

            UpdateBookAuthors(book.Authors.Select(a => a.Id).ToArray(), bookToUpdate, context);

            bookToUpdate.Year = book.Year;
            bookToUpdate.Name = book.Name;
            bookToUpdate.CategoryId = book.Category.Id;
            context.Entry(bookToUpdate).State = EntityState.Modified;    
        }

        context.SaveChanges();
    }
}

private void UpdateBookAuthors(int[] selectedAuthors, Model.Book bookToUpdate, DataContext context)
{
    if (selectedAuthors == null)
    {
        bookToUpdate.Authors = new Model.Authors();
        return;
    }

    var selAuthors = new HashSet<int>(selectedAuthors);
    var bookAuthors = new HashSet<int>(bookToUpdate.Authors.Select(a => a.Id));

    foreach (var author in context.Authors)
    {
        if (selAuthors.Contains(author.Id))
        {
            if (!bookAuthors.Contains(author.Id))
            {
                bookToUpdate.Authors.Add(author);
                context.Entry(author).State = EntityState.Modified;
            }
            else
            {
                context.Entry(author).State = EntityState.Unchanged;
            }
        }
        else
        {
            if (bookAuthors.Contains(author.Id))
            {
                bookToUpdate.Authors.Remove(author);
            }
        }
    }
}

Note no bloco acima que o método UpdateBookAuthors fica responsável por identificar os autores que foram adicionados ou removidos da entidade Book. Note também o uso da propriedade CategoryId recebendo o ID da nova categoria associada ao livro, o uso desta propriedade torna o processo de atualização da entidade associada mais simples, pois não exige o controle do seu status.

Testes Unitários

E por fim, temos um projeto dedicado aos testes unitários da camada Controller. Neste projeto vale a pena ressaltar o uso de classes fake simulando nossas classes de acesso a dados, como se realmente estivessemos persistindo dados em um banco de dados. O uso destas classes, demonstrando a possibilidade de testarmos nossa aplicação, sem estarmos conectados realmente a uma base de dados, apenas reforça o uso de uma camada exclusíva para plataforma de dados.

Obrigado pela atenção e bons estudos!

Por

Fernando Henrique Inocêncio Borba Ferreira

Microsoft Mosta Valuable Professional – Data Platform Development

Anúncios

6 Responses to EF Code First – Design Patterns – Asp.Net – Multi-Camadas – E muito trabalho!

  1. sauslash says:

    Excelente Fernando, muito bom o artigo.

    Tenho lido bastante a respeito do codefirst e sempre vejo o mesmo amarrado ao MVC, na verdade tem muita coisa assim:
    – Repository com Generics + MVC + CodeFirst

    Queria saber se você já viu algo assim:

    – Asp.net Web Forms + Repository com Generics, ja viu um modelo mesclado assim?

    Eu acho que seria muito produtivo usar o conceito do Repository com o Generics para crud e manter o web forms para pequenas aplicações. Ja viu algo parecido?

    Abraços

    • sauslash says:

      Seria algo igual a esse artigo http://geekswithblogs.net/seanfao/archive/2009/12/03/136680.aspx
      Mas qual modelo n-tier usar ?

      • Olá Sauslash,
        Mto legal este link que vc encaminhou.
        Existe um livro chamado Professional ASP.Net Design Patterns. É um excelente livro que fala sobre diversos design patterns, e ele cita o repository. E o legal deste livro é que quase todos os exemplos (por volta de uns 95%) são feitos com ASP.Net Web Forms.

        Vale dar uma olhada neste livro 🙂

        Este exemplo não utiliza o framework ASP.Net MVC, na verdade é um exemplo de uso do MVC construído na mão com Web Forms. Podemos adapta-lo para utilizar um repositório genérico tb…

        []s!

      • sauslash says:

        Opa Fernando obrigado pela dica.

        Sim o link que mandei não usa MVC, inclusive foi o único que encontrei.

        Minha dúvida foi como usa-lo, pensei em uma camada BLL e uma DAL, sendo que na DAL colocaria a parte do repository. Quero criar algo personalizado, simples e fácil de usar. Os modelos atuais principalmente os que usam mvc possuem muitas camadas.

        []s e obrigado denovo.

  2. andremenegassi says:

    Olá Fernando. Muito bom seu post, mas tenho uma dúvida.

    No seu exemplo (disponível em http://code.msdn.microsoft.com/EF-Code-First-Design-1558305c), o MODEL está presente na VIEW, por exemplo na VIEW AddNewAuthor.aspx, onde tem o MODEL AUTHOR.
    Você vê alguma quebra de regra do padrão MVC ao usar o MODEL diretamente na VIEW?
    Obrigado!!!

    • Olá André,
      Tudo bem?

      Sim, de fato no padrão MVC a view tem referência a MODEL. O objetivo da VIEW é exibir os dados da MODEL (a estrutura de classes), desta forma, a VIEW precisa saber qual a estrutura da MODEL e assim, deve referência-la. O oposto não deve ocorrer, isto é, de alguma forma alguma entidade da MODEL saber que existe a VIEW.

      As entidades existentes na MODEL devem ser bastante simplificadas e não devem fazer referências a frameworks externos e não devem conter regras de negócios, apenas manter o estado de suas instâncias.

      Obrigado por postar.
      []s!

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s

%d blogueiros gostam disto: