Entity Framework – Melhores práticas em busca de performance

Separei neste post algumas dicas de como tornar suas aplicações com Entity Framework mais performáticas. As dicas abaixo se resumem a um grupo de boas práticas que costumo aplicar em meus projetos. Algumas desta dicas não se aplicam apenas ao Entity Framework, mas se aplicam a outras ferramentas ORM ou técnicas de modelagem de sua camada de acesso a dados.

Tornando sua aplicação mais performática

1 – Não deixe a instância de seus DbContexts ativa por muito tempo.

O DbContext é uma classe responsável por manipular registros do banco de dados e gerenciar o ciclo de vida das instâncias destes dados quando mapeados em memória. Por conta destas características, o EF faz uso de recursos como: cache de entidades, controle de instâncias, controle de alterações, versionamento de alterações em memória, entre outros. Assim, podemos notar que uma simples instância de um DbContext tem muito trabalho a fazer e muitos dados a gerenciar.

Quando não controlamos corretamente o ciclo de vida destas instâncias acabamos por consumir mais recursos do que o preciso, pois dados ficam em memória mais tempo do que o necessário até que o GC faça o seu trabalho. Outro ponto negativo é que instância de DbContext que perdurem por muito tempo em memória podem utilizar dados desatualizados que não condizem com os dados que existem na base de dados naquele instante.

Por estes motivos sempre que precisamos fazer uso de uma instância de um DbContext criamos essa instância dentro de um bloco USING, ou invocamos o método “Dispose()” após o fim do seu uso. Assim asseguramos que o ciclo de vida da instância do DbContext se restrinja a um limitado bloco de código e que os recursos consumidos não serão utilizados por mais tempo do que o necessário.

Mais sobre o assunto pode ser encontrado neste outro post: DbContext e o ObjectContext – Não deixe seus dados em memória

2 – Desabilite o Change Tracking quando não for utilizá-lo

Conforme citado anteriormente, o DbContext mantém uma referência para as instância mapeadas com o intuito de identificar qualquer possível alteração em qualquer uma de suas propriedades. Mas existem cenários nos quais não precisamos utilizar este recurso. Quando precisamos retornar um conjunto de dados por meio de um serviço ou quando apenas queremos apresentar estes dados em um controle (DataGrid, GridView, ListView, ComboBox, DropDownList, etc).

Para não fazer uso dos recursos de rastreamento de alterações, basta fazer uso do método “AsNoTracking()” em suas consultas, conforme o exemplo abaixo. Com o uso do “AsNoTracking()” acabamos por não mapear as alterações de instâncias de objetos que não sofrerão alterações, pois serão utilizados apenas para exibição ao usuário.

using (var context = new ProgramContext()) {

    var query = from q1 in context.FlightAttendants
                where q1.Company.Id == 1
                select q1;

    var result = query.AsNoTracking().ToList();
}

Mais dados sobre este assunto podem ser encontrados neste outro post: Entity Framework – Desabilitando o tracking de entidades

3 – Cogite a substituição de queries muito complexas por stored procedures

O trabalho exercido pelo Entity Framework para conversão de queries LINQ para código SQL é muito bom. Este trabalho não é trivial e exige muito esforço. Mas mesmo assim, ainda prefiro criar stored procedures quando necessito executar uma consulta muito complexa. Stored procedures são procedimentos armazenados, compilados e da confiança do banco de dados, cujo código não foi criado dinamicamente, o que exigiu atenção do desenvolvedor para cria-lo da melhor forma.

Quando criamos uma stored procedure são acionados quatro procedimentos, sendo eles: verificação, normalização, compilação e otimização. Esses quatro procedimentos englobam tarefas como: avaliação da sintaxe para possível detecção de erros; validação se os objetos referenciados no código (tabelas, views, functions, etc) realmente existem no banco de dados; invoque de processos de otimização para melhoria de consultas, inclusões e atualizações; criação de um plano de execução para a stored procedure; e tantos outros procedimentos.

Pode-se notar que existe muito esforço antes da execução de uma stored procedure. Boa parte desses procedimentos são executados para as demais queries, mas a vantagem das stored procedures é que estes procedimentos já estão prontos, ao contrários das queries enviadas para o banco de dados. As consultas puramente enviadas para o banco de dados precisam passar por quase todos os procedimentos que já foram executados na criação da stored procure. Além disso, dependendo da complexidade da query, o código construído por nós pode ser melhor que o código gerado automaticamente pelo Entity Framework.

4 – Tome cuidado com os operadores que utiliza!

Quando construímos nossas consultas, sejam elas feitas com sintaxe LINQ ou com Queryable Methods, acabamos por utiliza diferentes operadores como: Count(), ToList(), ToArray(), ToSequence(), First(), FirstOrDefault(), Last() e LastOrDefault(). Muito não sabem, mas estes métodos forçam a execução das queries, o que significa que, se mal utilizados, estes operadores podem acarretar em grandes problemas de performance e uso inadequado de processamento e memória.

Veja o exemplo abaixo. Ele apresenta uma query bastante simples que faz um JOIN entre duas entidades de um mesmo contexto. Esse cenário é bastante simples e comum de ser encontrado em qualquer sistema. Mas se notarmos o uso dos operadores ToList() veremos que eles são utilizados dentro da query. O mau posicionamento destes operadores dentro da query afeta diretamente o modo como ela será executada.

using (var context = new ProgramContext()) {

    var query = from p in context.Pilots.ToList()
                join c in context.FlightCompanies.ToList() on p.CompanyId equals c.Id
                select new { Pilot = p.Name, Company = c.Name };

    var result = query;
}

Ao invés do join entre as entidades ser executado no banco de dados ele será executado em memória, exigindo que todos os registros de “Pilots” e “FlightCompanies” sejam carregados em memória (mesmo que não exista nenhuma relação entre eles), para que então, os dados sejam relacionados.

Erroneamente pode-se imaginar que isso não faz diferença alguma, mas isso faz toda a diferença entre um sistema rápido e performático, ao invés de um sistema lento e que consome muito mais memória do que o necessário.

O modo correto de executar a query é o modo apresentado abaixo:

using (var context = new ProgramContext()) {

    var query = from p in context.Pilots
                join c in context.FlightCompanies on p.CompanyId equals c.Id
                select new { Pilot = p.Name, Company = c.Name };

    var result = query.ToList();
}

Outro erro comum, que já vi ser executado, é o uso do operador AsEnumerable() sem que ele realmente seja necessário. Esse erro é apresentado abaixo:

using (var context = new ProgramContext()) {

    var query = from p in context.Pilots
                join c in context.FlightCompanies on p.CompanyId equals c.Id
                select new { Pilot = p.Name, Company = c.Name };

    var result = query.AsEnumerable().ToList();
}

O uso do método AsEnumerable() realmente não faz sentido nessa query, pois o retorno da query já implementa a interface IQueryable<T>, que por sua vez implementa a interface IEnumerable<T>, que é complementada pelo extension method Enumerable.ToList<TSource>. Ele pode realmente ser removido da query e irá melhorar a performance, pois será um método a menos a ser executado sobre todo o conjunto retornado pelo banco de dados. O processador de seu servidor agradece.

Outro erro é forçar o banco de dados retornar mais registros do que o necessário. A query abaixo demonstra este cenário no qual todo um conjunto de registros é retornado, mas apenas um registro é utilizado.

using (var context = new ProgramContext()) {

    var query = (from p in context.Pilots
                join c in context.FlightCompanies on p.CompanyId equals c.Id
                select new { Pilot = p.Name, Company = c.Name }).ToList();

    var result = query.First();
}

Note que a query é construída, todos os registros são retornados, mas apenas o primeiro é utilizado, pois após a execução do operador ToList() invocamos o operador First(). Esse cenário é bastante ruim, pois mais registros do que o necessário foram trafegados entre o banco de dados e a aplicação, estes registros foram mapeados pelo Entity Framework (mais consumo de memória e processamento) e após isso utilizamos apenas o primeiro deles (comportamento resultante da execução do operador First()).

Esse erro não é pior do que o apresentado abaixo. No exemplo abaixo a query é executada, todos os registros retornados, mapeados em memória e então filtrados e ordenados em memória.

using (var context = new ProgramContext()) {

    var query = (from p in context.Pilots
                join c in context.FlightCompanies on p.CompanyId equals c.Id
                select new { Pilot = p.Name, Company = c.Name }).ToList();

    var result = query.Where(x => x.Pilot.StartsWith("A")).OrderBy(x => x.Pilot);
}

Essa prática é bastante ruim, pois novamente mais registros do que o necessário foram mapeados em memória. O correto seria o uso do código abaixo, no qual todos os critérios de filtro e ordenação estão no banco de dados.

using (var context = new ProgramContext()) {

    var query = from p in context.Pilots
                join c in context.FlightCompanies on p.CompanyId equals c.Id
                where p.Name.StartsWith("A")
                orderby p.Name
                select new { Pilot = p.Name, Company = c.Name };

    var result = query.ToList();
}

Podemos notar que o uso correto dos operadores realmente faz diferença na performance das queries e nos consumos de memória e processamento do sistema.

5 – Substitua operações consecutivas idênticas por uma simples operação sempre que possível

Na minha opinião, duas coisas que não combinam são laços de repetição e acesso a bancos de dados. E quando necessária a sua combinação então que seja feita com bastante cautela. Operações que acessam uma base de dados não são triviais. Tais operações são complexas, consomem muitos recursos e envolvem muitas variáveis, então, que sejam utilizadas com atenção.

Certa vez encontrei algo como o código abaixo:

private void InsertIfNonExists(Employe[] employees) {

    foreach (Employe employe in employees) {

        using (var context = new ProgramContext()) {

            var r = context
                        .Employees
                            .FirstOrDefault(e => e.CompanyEmail == employe.CompanyEmail);

            if (r == null) {
                context.Employees.Add(employe);
                context.SaveChanges();
            }
        }
    }
}

Parece um código bastante inofensivo: uma array de entidades, um laço iterando os itens deste array, uma validação para averiguar se já existe um usuário com o mesmo e-mail no base de dados, e por fim, uma operação de inclusão do usuário na base de dados.

Supondo que este array contenha 150 instâncias de Employe. Então, são esperadas 150 iterações. Para cada iteração a criação de um novo contexto. E para cada iteração são esperadas duas operações no banco de dados: uma validação e uma inclusão. Desta forma, teremos 300 operações com o banco de dados. Algo que agora não parece tão inofensivo. Pois bem, esse cenário realmente aconteceu. O tempo e os recursos gastos com essa operação foram exorbitantes e o que parecia inofensivo causava deadlock nas demais operações com o banco de dados.

Para resolução desse cenário foi preciso fazer a refatoração do código, adaptando-o para executar a menor quantidade de iterações possível com o banco de dados. Por fim, o código ficou como o bloco apresentado abaixo:

private void InsertIfNonExists(Employe[] employees) {

    var emails = employees.Select(e => e.CompanyEmail);

    using (var context = new ProgramContext()) {

        var emailsOnDb = context.Employees.Where(e => emails.Contains(e.CompanyEmail))
                                            .Select(e => e.CompanyEmail);

        var employeesOutDb = employees.Where(e => !emailsOnDb.Contains(e.CompanyEmail)).ToList();

        employeesOutDb.ForEach(delegate(Employe e) { context.Employees.Add(e); });
        // AddRange - Disponível no EF6
        //context.Employees.AddRange(employeesOutDb);

        context.SaveChanges(); 
    }
}

Esse novo bloco é mais complexo de ser lido, mas é muito mais performático. Esse bloco inicialmente identifica as instâncias que possuem o e-mail cadastrado na base de dados e logo depois identifica quais as instâncias que não estão cadastradas. Após isso, é feito um laço para incluir as instâncias no DbSet – esse laço é válido, pois não acarreta em comunicação direta com a base de dados – e os dados são persistidos com o método SaveChanges(). Resutado: neste novo modelo acabamos por fazer apenas duas iterações com o banco de dados, ao invés de 2N iterações (dado que N é o número de instâncias de Employe passadas por parâmetro).

Observação: o operador Contains() quando convertido para a linguagem T-SQL transforma-se no operador “IN”. Existe a possibilidade de queda de performance de consultas quando este operador for utilizado em algum campo que não possui características que favoreçam o seu uso. Antes de adotá-lo vale a pena testar seu uso e verificar se o campo possui características que permitam seu uso.

6 – Contextos menores são melhores

É bastante comum encontrar contextos grandes que fazem referência a todas as entidades mantidas pela aplicação. Existem muitos benefícios ao se criar contextos menores, mais compactos, específicos para determinados blocos e/ou tarefas da aplicação.

Pequenos contextos são melhores, pois também permitem que os desenvolvedores definam claramente suas fronteiras de uso, limitando as responsabilidades do contexto. Além disso, o uso de contextos menores favorece a manutenção dos mesmos, visto que contextos menores acarretam em menos efeitos colaterais quando efetuadas manutenções.

Menos é mais

Também existem benefícios de performance relacionados ao uso de contextos menores. A inicialização dos contextos, quando carregados em pela primeira vez, mapeia seus metadados em memória. Tais metadados descrevem as propriedades existentes em cada entidade, suas respectivas colunas nas tabelas, seus relacionamentos e outras características do modelo. Quando utilizamos contextos menores o tempo de mapeamento destes metadados é muito menor e isso interfere diretamente no tempo gasto para inicialização dos contextos.

Com contextos menores também se obtém uma construção mais rápida das queries, pois os mapping views (representações utilizadas para execução de queries e execução de updates) são menores, assim a validação entre o modelo conceitual e o modelo do banco de dados é mais rápida.

Não existe uma regra que limite a quantidade de entidades mapeadas por contexto. Acredito que contexto com até 50 entidades mapeadas são aceitáveis, mas contextos com mais de 100 entidades podem acarretar em problemas de performance. Penso que algo válido é adotar o uso de Bounded Contexts, que nada mais são do que contextos pequenos focados em operações específicas no seu domínio de negócio. Nos Bounded Contexts os contextos chegam a ter de 4 a 9 entidades, apenas o necessário para atender operações específicas no seu domínio de negócio.

Para saber mais sobre Bounded Contexts veja este post: Shrink EF Models with DDD Bounded Contexts

7 – Tome cuidado com Lazy Loading

O Lazy Loading é um excelente recurso quando utilizado da forma correta. Para muitos ele funciona de forma atraente, pois carrega os dados agregados apenas quando precisamos deles (por isso o nome “lazy loading”). Mas esse comportamento pode consumir mais recursos do que o necessário em cenários específicos. Veja o exemplo abaixo:

using (var context = new ProgramContext()) {

    var query = from e in context.Employees
                where e.Company.Name == "AA"
                select e;

    foreach (var employe in query) {
        SendPayment(employe.Id, employe.BankAccount.Number);
    }
}

Note que está sendo passado como parâmetro para o método “SendPayment” o valor de uma propriedade de uma associação da classe Employe. Utilizando Lazy Loading neste exemplo acabaremos por ter um grande número de requisições ao banco de dados, sendo: uma requisição para selecionar as instâncias de Employee; e N requisições (dado o número de registros retornados pela primeira query) para consulta dos dados de BanckAccount. Assim, o resultado final de chamadas ao banco de dados será de N + 1 (dado que N é o número de registros de Employe).

Para reverter este cenário basta fazer uso do operador Include, indicando qual propriedade agregada deve ser retornada na query. A resolução pode ser vista abaixo:

using (var context = new ProgramContext()) {

    var query = from e in context.Employees.Include("BankAccount")
                where e.Company.Name == "AA"
                select e;

    foreach (var employe in query) {
        SendPayment(employe.Id, employe.BankAccount.Number);
    }
}

Dessa maneira apenas uma requisição será feita ao banco de dados, retornando os dados de Employe e os dados de BanckAccount.

8 – Simplifique o retorno de suas consultas apenas com os dados que precisa

Para simplificar o retorno de suas consultas, e diminuir a quantidade de dados trafegados entre a aplicação e o banco de dados, é interessante filtrar apenas os campos que serão utilizados. Dado o exemplo abaixo, é fácil notar que para execução da operação apenas os campos Id e BankAccount.Number de cada Employe são suficientes. Mas, apesar disso, notamos que todos os campos de Employe e BackAccount são retornados.

using (var context = new ProgramContext()) {

    var query = from e in context.Employees.Include("BankAccount")
                where e.Company.Name == "AA"
                select e;

    foreach (var employe in query) {
        SendPayment(employe.Id, employe.BankAccount.Number);
    }
}

Assim, o uso de DTOs (Data Transfer Objects) é bastante útil para desnormalizar os dados e agrupar em apenas uma entidade todos os dados que serão utilizados. Vide o exemplo abaixo:

using (var context = new ProgramContext()) {

    var query = from e in context.Employees
                where e.Company.Name == "AA"
                select new DtoPaymentRequest { 
                                EmployeId = e.Id, 
                                NumberBackAccount = e.BankAccount.Number 
                };

    foreach (var paymentRequest in query) {
        SendPayment(paymentRequest);
    }
}

Mas o uso de DTOs não é obrigatório. Podemos ter o mesmo resultado utilizando tipos anônimos, como o código apresentado abaixo:

using (var context = new ProgramContext()) {

    var query = from e in context.Employees
                where e.Company.Name == "AA"
                select new {
                    EmployeId = e.Id,
                    NumberBackAccount = e.BankAccount.Number
                };

    foreach (var paymentRequest in query) {
        SendPayment(paymentRequest.EmployeId, paymentRequest.NumberBackAccount);
    }
}

Para saber mais sobre Data Transfer Objects, veja este post Construindo camadas de acesso a dados.

9 – Considere utilizar diferentes níveis de isolamento

Para melhorar o desempenho de consultas e reduzir a possibilidade de deadlocks em tabelas é indicada a utilização de níveis de isolamento.

O operador NOLOCK é bastante empregado em queries SQL para ignorar os locks das tabelas e lê-las diretamente. Assim, ignorando o sistema de bloqueios, a performance de consultas é maior e não se corre o risco das operações ficarem “em espera”, aguardo o fim das transações.

O comando NOLOCK não é suportado pelo Entity Framework, isto é, não existe um meio de “ligar ou desligar” o seu uso. Mas podemos alterar o nível de isolamento das transações, o que “no fim das contas”, gera os mesmos resultados. No exemplo abaixo é apresentada essa prática, onde o nível de isolamento da transação é alterado.

using (var context = new ProgramContext()) {

    using (var tran = context.Database.BeginTransaction(System.Data.IsolationLevel.ReadUncommitted)) {

        var query = from q1 in context.FlightAttendants
                    where q1.Company != null && q1.Company.Name == "aa"
                    select q1;

        var result = query.ToList();

        tran.Commit();
    }
}

Abaixo alguns exemplos de níveis de isolamento existentes:

ReadCommitted Os processos de leitura ficam em espera caso existam outras transações de atualização em uma mesma tabela.
ReadUncommitted Não respeita os bloqueios exclusivos das transações permitindo a leitura de dados ainda não commitados. O fato de que dados não commitados possam ser lidos é chamado de "Leitura suja".
Snapshot Respeita os bloqueios das transações, mas os processos de leitura não ficam em espera. Os processos de leitura retornam apenas dados comitados, pois é criada uma versão do dados que possa ser lida enquanto os demais são modificados.

Existem outros níveis de isolamento que podem ser encontrados nessa enumeração. Você pode encontrar mais detalhes sobre os níveis de isolamento disponíveis por meio desse link: http://msdn.microsoft.com/pt-br/library/system.data.isolationlevel(v=vs.90).aspx

10 – Cache

Essa é a dica mais simples: use cache. Sempre que possível faça cache dos seus dados. Aqueles dados que não sofrem alterações constantes e que sempre são requisitados são fortes candidatos a serem mantidos em cache. Esse é o tipo de decisão simples de ser tomada e implementada, cujo resultado pode salvar recursos do servidor de banco de dados e tornar a aplicação mais rápida.

 

Por

MSc. Fernando Henrique Inocêncio Borba Ferreira

Microsoft Most Valuable Professional – Visual C#

Referências

http://msdn.microsoft.com/en-us/magazine/jj883952.aspx

http://www.careerride.com/SQL-Server-stages-when-procedure-or-query-is-executed.aspx

http://msdn.microsoft.com/en-us/library/bb342261.aspx

http://msdn.microsoft.com/en-us/library/bb351562.aspx

https://ferhenriquef.com/2013/02/05/entity-framework-5-internals/

http://www.developerfusion.com/article/1688/sql-server-locks/4/

Anúncios

15 Responses to Entity Framework – Melhores práticas em busca de performance

  1. Parabéns pelo artigo, está bem objetivo e didático!
    Mas, para padronizarmos o tipo de Loading do EF (se é que isso é bom), não seria legal desabilitar o Lazy Loading e forçar que toda a consulta realmente traga todo o dado necessário? O custo/benefício que pederemos não será tão afetado não?

    • Olá Francisco, tudo beleza?
      Acredito que não existe perda, pois ao executar um menor número de consultas acabamos por economizar recursos de ambos servidores, visto que de qualquer forma todas as consultas trariam a mesma quantidade de dados.

      Padronizar o loading do EF tb não é bom, pois podemos exigir um load diferente para cada cenário.

      Acho que os tópicos 3, 7 e 8 são complementares com relação ao retorno dos dados.

      Obrigado por postar.
      []s!

  2. Muito bom, mestre!!

    Gostei muito da número 5. Parece ser a mais simples e que muita gente não presta atenção. Depois tem 1 zilhão de requisições de bobeira.

  3. Alexandre Hogler says:

    Olá Fernando,

    Trabalho com aplicação Winforms e EF5. No começo tentei carregar os contextos apenas quando eram utilizados, com o comando USING. Porém começou a dar muito trabalho fazer sempre o “ATTACH” das propriedades. Em telas onde eu tinha vários relacionamentos 1-N para a mesma entidade, o método de salvar ficava gigantesco.

    No final, optamos por abrir o contexto quando a tela abre e manter ele até o formulário ser fechado. Trabalhando com UnitOfWork acesso todas as tabelas por meio do mesmo contexto e como elas vem do mesmo lugar, não preciso “atachar” tudo depois quando salvar.

    Fica bem mais fácil.

    O maior problema, como você mesmo comentou, é o risco que temos de estar trabalhando com dados desatualizados.

    Resolvemos um problema e criamos outro, é triste.

    • Oi Alexandre,
      Tudo bem?

      Gostei do seu comentário. Realmente é algo bastante trabalhoso quando existem mtos relacionamentos.
      A sorte é que vc trabalha em uma aplicação WinForms e consegue controlar essas instâncias em memória. Se fosse uma aplicação Web isso seria bem mais complicado.

      Obrigado por comentar.
      []s!

  4. Oi Fernando tudo bom?
    Gostei muito do seu post, ta de parabéns!

    Mas queria te fazer uma pergunta, já configurou o EF pra usar o banco postgresql?
    Estou tentando fazer isso e empaquei em alguns erros, até cheguei a abrir essa thread aqui no MSDN mas ainda não consegui resolver o problema.

    http://social.msdn.microsoft.com/Forums/pt-BR/a40df416-b959-40ec-a4cc-706dbb3dfbe5/entity-framework-postgresql-keyvalue-argument-incorrect-in-connectionstring-exception?forum=adoptpt

    Se puder me ajudar vou te agradecer pelo resto da vida kkkk

    Abraçao!

  5. Claudio Maia says:

    Olá…Parabéns pelo artigo…realmente de qualidade excepcional!

    Gostaria de saber sobre o último tópico (Cache), como faz para “cachear” os dados usando o entity…você mencionou mas não demonstrou um exemplo de cache….

    Muito obrigado por compartilhar seu conhecimento!

  6. Rangel says:

    Olá Fernando,

    Parabéns pelo seu artigo…
    Gostaria de uma ajuda, tenho a seguinte situação:

    Estou trabalhando com uma aplicação web, porém também terei aplicações em winform e webservices… além disso tenho na solução as camadas de Negócio (BLL), de Acesso a dados (DAO) que serão utilizadas por todos.

    Fiquei na dúvida em como fazer quando por exemplo eu tenho uma inclusão ou alteração que só poderá ser feita após uma série de outras atualizações (alterações, exclusões, inclusões)…

    Exemplo (operação atômica): Só quero atualizar um novo preço de um produto após ter feito a atualização de todas as tabelas de vendas nas quais esse produto esteja inserido. Caso ocorra algum erro nas atualizações intermediárias quero desfazer tudo e não atualizar.

    As diferentes atualizações (chamadas nas DAOs) estarão definidas na BLL, onde com base nas regras invocará a respectiva DAO que atualizará a tabela referida…

    Se abro um objeto de contexto para cada atualização nas DAOs ele cria uma nova transação, como resolver isso?

    Não gostaria de criar uma Stored Procedure para cada caso desse, a camada de negócio acredito que tenha também essa função…

    Desculpe mas posso não ter sido claro no meu exemplo…

    Desde já agradeço…

  7. Pingback: Veja como melhorar a performance do Entity Framework | I love code

  8. Olá,

    Tenho um Model da seguinte forma

    public class Pessoa
    {
    public int Id { get; set; }
    public string Nome { get; set; }
    public virtual ICollection Enderecos { get; set; }
    public virtual ICollection Contatos { get; set; }
    }

    Versão: EF6

    Pessoa pessoa = db.Pessoa.Find(id);

    Estou usando Find para recuperar.
    Mas existe um questão que não estou conseguindo resolver.

    Enderecos com uma property chamada ATIVO, e a questão é que não consigo trazer somente os endereços que estão ATIVOS. Se tiver alguma sugestão.

    • Olá, Inacio.
      Realmente não tem como fazer isso de forma simples. Se vc usar Eager Loading, Lazy Loading ou Explicit Load, todos os dados relacionados serão carregados.

      Mas, você pode criar meios para filtrar esses dados em memória. Você pode criar uma propriedade readonly chamada ‘EnderecosAtivos’ e encapsular um filtro para os endereços ativos da propriedade Enderecos. E, no seu mapeamento, fazer um IgnoreMap dessa propriedade, a fim de evitar qualquer confusão no banco de dados.

      public class Pessoa
      {
      public int Id { get; set; }
      public string Nome { get; set; }
      public virtual ICollection Enderecos { get; set; }
      public virtual ICollection Contatos { get; set; }
      public IEnumerable EnderecosAtivos { get { return Enderecos.Where(e => e.Ativo); } }
      }

      Veja se ajuda.

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: