Anúncios

Construindo camadas de acesso a dados – Parte III – Repositórios Genéricos (Pantheon)

O padrão Repository é muito utilizado em abordagens que fazem uso de ferramentas de mapeamento objeto relacional (ORM). O objetivo deste pattern é reunir comandos de acesso a dados em uma classe que por meio dos recursos de generics faça as operações de inserção, atualização, exclusão e recuperação de dados.

clip_image002

A vantagem do uso deste pattern é o ganho de produtividade no desenvolvimento da camada de acesso a dados, pois boa parte do código necessário para sua construção já está encapsulado no repositório genérico. Neste post existe um detalhamento maior sobre o pattern Repository: https://ferhenriquef.com/2013/03/05/construindo-camadas-de-acesso-a-dados/

Geralmente tal abordagem gera muitas dúvidas e discussões nos fóruns. Por conta disso, resolvi começar um novo projeto cuja proposta é criar uma solução que auxilie na composição de repositórios genéricos com o Entity Framework.

Criei no GitHub um projeto chamado Pantheon Repository (https://github.com/FerHenrique/PantheonRepository/) que contém a versão Alpha deste repositório genérico. Após isso, tive a ajuda dos meus colegas Maicon Guerra e Guilherme Andrade no refinamento deste projeto. Passamos a última semana realizando testes e refinando a arquitetura deste repositório, com o intuito de fornecer um repositório que funcione em diferentes cenários.

clip_image004

A estrutura do repositório

Atualmente a estrutura do repositório Pantheon está disponível da seguinte forma:

public interface IRepository<TEntity> where TEntity : class
{
    void Delete(TEntity entity);

    IEnumerable<TEntity> GetAll(params string[] includeProperties);

    IEnumerable<TEntity> Query(Func<TEntity, bool> query);

    IEnumerable<TEntity> Query(Func<TEntity, bool> query, params string[] includeProperties);

    TEntity Save(IEnumerable<TEntity> entities);

    IEnumerable<TEntity> Save(IEnumerable<TEntity> entities, bool saveNestedProperties);

    TEntity Save(TEntity entity);

    TEntity Save(TEntity entity, bool saveNestedProperties);
}

O objetivo é expandir essa estrutura e fornecer novos recursos e métodos ao longo do tempo.

Formas de utilização

O Pantheon Repository pode ser utilizado de três formas diferentes:

1 – Como data helper, encapsulando o comportamento das operações de CRUD.

clip_image005

2 – Como classe base para criação de repositórios especializados.

clip_image007

3 – Fazendo uso de uma classe factory para criação de instâncias

clip_image008

Como funciona?

O funcionamento do repositório genérico não tem muitos segredos. Basicamente, a parte mais complexa está centralizada no método “Save()”, que realiza a inclusão e a atualização das entidades e de suas propriedades encadeadas, sendo elas outras entidades ou lista de entidades.

Internamente foi construído um conjunto de regras que coordenam o status das entidades, evitando que possíveis duplicações de dados sejam geradas na base de dados.

Os métodos de exclusão e recuperação de dados são mais simples, não exigem tantas regras de negócio como o método “Save()”.

Bugs!

É possível que existam erros. Peço que ao encontrar um bug tente reporta-lo, pois vamos trabalhar para corrigi-lo (mas se quiser fazer o download dos fontes e corrigi-lo, sinta-se em casa). Realizamos testes com diferentes cenários, mas ainda assim é possível que existam erro em cenários que não testamos.

Futuras atualizações

Espero conseguir lançar novas atualizações em tempos curtos de tempo. Espero ainda essa semana fazer o upload de novos exemplos, mais comentários no código, diagramas de classes e detalhes maiores do seu formato de implementação.

Share!

O projeto está disponível no caminho: https://github.com/FerHenrique/PantheonRepository/

clip_image010

Propostas, comentários, discussões, sugestões e e-mails são muito bem vindos!!!

Realmente espero que esse projeto seja útil para a comunidade.

Obrigado!


Por

MSc. Fernando Henrique Inocêncio Borba Ferreira

Microsoft Most Valuable Professional – Visual C#

Anúncios

29 Responses to Construindo camadas de acesso a dados – Parte III – Repositórios Genéricos (Pantheon)

  1. Yan Justino says:

    Sempre surpreendendo, né amigo! Excelente repositório!!!!

  2. Como eu lhe disse antes, fiquei super feliz de ver o meu mestre colocando algo no Github. Como prometido acabei de forkar o projeto. Logo faço qualquer pull request. Muito sho, mestre!

    • Vlw Pri!

      Sempre gostei de abordagens open e o GitHub é a ferramenta (empurrão) que faltava.

      Acredito que ainda exista mto a fazer com este repositório, mas também acredito que ele pode funcionar como base para demais projetos nessa linha.

      Vamos evoluí-lo!

      Vlw por postar!

  3. Francisco Berrocal says:

    Não seria válido implementar a interface IDisposable, e no override dar um dispose no contexto?

    • Olá Francisco,
      Tudo beleza?

      Nesta implementação do repositório genérico não existe uma variável de contexto ao nível da classe. Todas são criadas no nível do método. Desta forma não preciso da interface IDisposable.
      Fiz desta maneira por três motivos:
      – Me certificar de que o contexto sempre será fechado
      – Garantir que nenhum desenvolvedor vai esquecer de invocar o Dispose
      – Simplificar o uso do repositório genérico.

      []s e obrigado por postar!

  4. Marlon Tiedt says:

    Olá Fernando, estou acompanhando os seus post sobre o assunto e surgiu uma dúvida:
    Esta forma de implementação pode ser usada para EF?

    Pois assim, nos projeto que ando fazendo, eu crio um DbContext, e crio meus Models da forma normal, e faço as regras usando DataAnnottations.

    Esta forma não estaria isolado?

    Parabéns pelos posts…

    abs

    • Olá Marlon,
      Tudo bem?

      Sim, utilizando DataAnnotations vc estará dependendo do EF. Este é o principal motivo pelo qual eu apenas uso Fluent API. Essa dependência não me atrai, e o Fluent API pode ser criado na camada de acesso a dados sem que o modelo dependa do EF.

      Obrigado por postar!
      []s!

      • marlontiedt says:

        Fernando, minha grande dúvida é, qual seria a minha maior vantagem de fazer a forma acima que você postou, de usar o DBSet diretamente. Pois em cada controller meu, eu faço:

        private ERPContext db = new ERPContext();

        e depois:

        db.Empresas.Add(_classe);
        db.SaveChanges();

        fazendo isto, será que está errado?

        Além disto, criei Ext para listar as classes conforme minha necessidades, como o exemplo abaixo:

        public static class UsuarioExt
        {
        public static IEnumerable ToListPorLogin(this DbSet entities, string aLogin)
        {
        return entities.Where(String.Format(“Login == \”{0}\””, aLogin));
        }
        }

        Estou no caminho certo ou estou fumando legal? 🙂

        abs

  5. Olá Marlon,
    Deste modo não esta errado. Mas a vantagem de utilizar um repositório (ou uma camada de acesso a dados) é reunir todos os comandos que acessarão a base de dados em um único ponto, de forma que quando for preciso mudar de tecnologia de acesso a dados, corrigir um bug, ou reaproveitar código já construído, vc fará isso de uma maneira mto mais simples. Pois toda sua lógica estará encapsulada em uma única camada.

    []s!

  6. Reuber says:

    Olá Fernando. Estou fazendo uso do Pantheon como repositório genérico. Porém estou com uma dúvida: como faço para nomear o meu banco de Dados? Com o teste do Visual Studio Summit 2013, o exemplo cria um banco chamado: PlaygroundVisualStudioSummit2013.Data.DataContext – Gostaria de colocar um nome mais sugestivo. Usando o Pantheon como faço essa implementação?

    • Olá Reuber,
      Neste caso vc deve indicar uma connection string para o seu DbContext.

      Provavelmente vc possui uma classe que herda de DbContext. No construtor dessa classe vc deve chamar o construtor da classe DbContext, passando a sua connection string ou o alias utilizado no seu web.config (ou app.config) para nomear sua connection string.

      Em suma vc deve fazer algo como apresentado abaixo:
      public class XPTO : DbContext {

      public XPTO() : base(“sua connection string ou o alias registrado no web.config ou ap.config”) {

      }
      }

      O EF vai seguir a nomenclatura descrita na sua connection string para nomear o banco de dados.

      []s!

      • Reuber says:

        Ok, fiz via app.config e deu certo. Em um sistema anterior eu utilizava sobrescrever o método DataContext como:

        public DataContext()
        : base(“Name=NomeBD”)
        {
        }

  7. Rafael Parana says:

    Fernando,

    Estou tentando implementar o repositório genérico no meu projeto, porem me deparei com um problema. no caso dos campos ComplexType o repositório trata o mesmo como sendo uma tabela a parte e tenta incluir a informação. Existe alguma forma de filtrar esses casos?

    • Olá Rafael,
      Não sei como resolver essa questão de maneira genérica. Vou pesquisar, implementar no Pantheon e lhe aviso. Obrigado por comentar. Deixe seu e-mail, quando eu tiver a resposta lhe avisarei mais rápido.
      Obrigado por postar.
      []s!

      • Rafael Parana says:

        Fernando,

        no meu caso consegui resolver filtrando o namespace.

        private bool IsEntityProperty(PropertyInfo property)
        {
        return property.PropertyType.IsClass &&
        property.PropertyType != typeof(string) &&
        property.PropertyType != typeof(byte[]) && !property.PropertyType.Namespace.StartsWith(“CR.Model.ComplexType”);
        }

  8. Rafael Parana says:

    Fernando,

    Encontrei outro problema que talvez esteja na forma que estou incluindo um objeto novo.

    tenho na minha base as seguintes tabelas.

    Entidade {ID, Identificação, nome}
    Usuários {ID, Login, Senha, Entidade, Grupo}
    Grupo {ID, Nome, Descricao, Empresa}
    Empresas {ID, Entidade}

    a minha empresa já possui os dados na TB entidade.

    Quando tenho que adicionar um usuário e não possuo um grupo eu estou adicionando tudo de uma vez. EX.

    g = new Grupo();
    g.Nome = “Administradores do Sistema”;
    g.Descricao = “Este grupo permite acesso a todos os recursos disponiveis no sistema.”;
    g.Empresas.Add(Empresa);
    g.Permissao = p;

    u = new Usuario();
    u.Login = “Administrador”;
    u.Senha = “Sup0rt3”;
    u.Grupo = g;

    obj = new Entidade();
    obj.Nome = “Administrador do Sistema”;
    obj.Identificacao = “0000×0000”;

    obj.DadosUsuario = u;

    controller = new EntidadeController();
    controller.Salvar(obj);

    porem, no Pantheon Repository ele adiciona o grupo, usuário e entidade setando o state como added. mais ele não seta a empresa que esta na tabela grupo como Unchanged e acaba adicionando uma segunda entidade. oque ocasiona o erro posi o ID fica sendo duplicado.

    sabe alguma forma que posso fazer? ou adiciono meus objetos um a um?

    • Oi Rafael,

      Nesse caso eu acho melhor adicionar um de casa vez.
      O relacionamento entre Grupo e Empresa é N:N? Melhor adicionar um de cada vez.

      []s!

      • Rafael Parana says:

        o relacionamento na verdade é 1:1.

        mais estou adicionando agora um de cada vez para ver como se comporta.

        Obrigado.

  9. Boa tarde,
    Onde eu encontro os slides da sua palestra sobre o Pantheon?

  10. Rubia says:

    Boa Tarde Fernando,
    Estou criando um repositório genérico apenas com os métodos CRUD.
    Para metodos mais complexos irei criar um repositorio especifico para a entidade.
    Esta implementação esta correta ?

    Obrigada

  11. Fábio Lima says:

    Oi Fernando, obrigado pelo retorno.

    Baixei o Pantheon e quando fui dar o build ele veio com estas duas dlls faltando:

    Metadata file ‘F:\PantheonRepository-master\GenericRepository\GenericRepository\bin\Debug\Pantheon.dll’ could not be found

    Metadata file ‘F:\PantheonRepository-master\GenericRepository\SaleData\bin\Debug\SaleData.dll’ could not be found

    Pode ser que eu tenha baixado errado, tem boas chances, mas eu não tenho como saber.

    Fábio

    • Olá Fábio,
      Tudo bem?

      Baixei os fontes que estão no GitHub e tive problemas apenas com referências para Dlls do EntityFramework.dll. Cujo problema foi resolvido ao ajustar o caminho para onde as Dlls apontavam.

      Tente gerar o build de cada projeto separadamente, e não como um todo.
      Pode ser que isso esteja lhe atrapalhando.

      Obrigado.
      []s!

  12. Wivison Silva says:

    Olá Fernando,

    Sou iniciante em ORM e comecei estudando pelo EF e estou gostando bastante.
    Através das minhas pesquisas achei seu blog, e está sendo de imensa ajuda no meu aprendizado. Obg!!!

    Sobre o Pantheon, tenho duas questões:

    – Na identificação da PK, olhando o código vi que ele verifica de boa as chaves compostas, porém na hora do tratamento ele só pega a primeira propriedade do array e sempre trata como inteiro (comparando com ‘0’), para verificar qual será a ação que será realizada no BD. Como resolver casos que não se enquadrem nessa verificação e o fato de ignorar as outras propriedades da chave composta?

    – No método Query, mesmo passando o predicado na hora que o EF faz a consulta no banco de dados ele não gera a cláusula WHERE no Sql Server, trazendo todos os registro do BD e depois ele realiza o filtro. Pesquisando, achei a solução que foi trocando o “Func query” para “Expression<Func> query”. A partir daí ele começou a mandar a cláusula WHERE para o Sql Server. O que achas disso?

    abraços

    • Oi Wivison,
      Obrigado pelos comentários.

      – Não costumo trabalhar com chaves compostas. Vc poderia me detalhar o cenário no qual está trabalhando? Podemos pensar em algo para adequar o repositório a esse cenário. Vc tem alguma sugestão?

      – Obrigado por avisar, não havia notado isso. Já corrigi o problema. Mto obrigado!!!!

      []s!

  13. Pingback: payday loans stores near me

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: