Quantos Include eu posso usar no ObjectSet em EntityFramework para manter o desempenho?

Eu estou usando a seguinte consulta LINQ para minha página de perfil:

var userData = from u in db.Users .Include("UserSkills.Skill") .Include("UserIdeas.IdeaThings") .Include("UserInterests.Interest") .Include("UserMessengers.Messenger") .Include("UserFriends.User.UserSkills.Skill") .Include("UserFriends1.User1.UserSkills.Skill") .Include("UserFriends.User.UserIdeas") .Include("UserFriends1.User1.UserIdeas") where u.UserId == userId select u; 

Ele tem um gráfico de objects longos e usa muitos includes. Está funcionando perfeitamente agora, mas quando o site tem muitos usuários, isso afetará muito o desempenho?

Devo fazer isso de alguma outra forma?

    Uma consulta com includes retorna um único conjunto de resultados e o número de inclusões afeta como o grande dataset é transferido do servidor de database para o servidor da web. Exemplo:

    Suponha que tenhamos uma entidade Customer (Id, Name, Address) e uma entidade Order (Id, CustomerId, Date) . Agora queremos consultar um cliente com seus pedidos:

     var customer = context.Customers .Include("Orders") .SingleOrDefault(c => c.Id == 1); 

    O dataset resultante terá a seguinte estrutura:

      Id | Name | Address | OrderId | CustomerId | Date --------------------------------------------------- 1 | A | XYZ | 1 | 1 | 1.1. 1 | A | XYZ | 2 | 1 | 2.1. 

    Isso significa que os dados do Cutomers são repetidos para cada Order . Agora vamos estender o exemplo com outras entidades – ‘OrderLine (Id, OrderId, ProductId, Quantity) and Product (Id, Name) `. Agora, queremos consultar um cliente com seus pedidos, linhas de pedidos e produtos:

     var customer = context.Customers .Include("Orders.OrderLines.Product") .SingleOrDefault(c => c.Id == 1); 

    O dataset resultante terá a seguinte estrutura:

      Id | Name | Address | OrderId | CustomerId | Date | OrderLineId | LOrderId | LProductId | Quantity | ProductId | ProductName ------------------------------------------------------------------------------------------------------------------------------ 1 | A | XYZ | 1 | 1 | 1.1. | 1 | 1 | 1 | 5 | 1 | AA 1 | A | XYZ | 1 | 1 | 1.1. | 2 | 1 | 2 | 2 | 2 | BB 1 | A | XYZ | 2 | 1 | 2.1. | 3 | 2 | 1 | 4 | 1 | AA 1 | A | XYZ | 2 | 1 | 2.1. | 4 | 2 | 3 | 6 | 3 | CC 

    Como você pode ver, os dados se tornam muito duplicados. Geralmente, cada uma inclui uma propriedade de navegação de referência ( Product no exemplo) adicionará novas colunas e cada uma includeá uma propriedade de navegação de coleção ( Orders e OrderLines no exemplo) adicionará novas colunas e duplicará linhas já criadas para cada linha na coleção incluída .

    Isso significa que seu exemplo pode facilmente ter centenas de colunas e milhares de linhas, o que é uma grande quantidade de dados para transferir. A abordagem correta é criar testes de desempenho e, se o resultado não atender às suas expectativas, você poderá modificar sua consulta e carregar propriedades de navegação separadamente por suas próprias consultas ou pelo método LoadProperty .

    Exemplo de consultas separadas:

     var customer = context.Customers .Include("Orders") .SingleOrDefault(c => c.Id == 1); var orderLines = context.OrderLines .Include("Product") .Where(l => l.Order.Customer.Id == 1) .ToList(); 

    Exemplo de LoadProperty :

     var customer = context.Customers .SingleOrDefault(c => c.Id == 1); context.LoadProperty(customer, c => c.Orders); 

    Além disso, você deve sempre carregar apenas os dados realmente necessários.

    Edit: Acabei de criar proposta no Data UserVoice para suportar adicional ansioso estratégia de carregamento onde ansiosos dados carregados seriam passados ​​no conjunto de resultados adicionais (criado por consulta separada dentro do mesmo database ida e volta). Se você achar essa melhoria interessante, não se esqueça de votar na proposta.

    Você pode melhorar o desempenho de muitas inclusões criando duas ou mais solicitações de dados pequenos a partir da base de dados, como abaixo.

    De acordo com a minha experiência, só pode dar no máximo 2 inclui por consulta como abaixo.Mais do que isso vai dar um desempenho muito ruim.

     var userData = from u in db.Users .Include("UserSkills.Skill") .Include("UserIdeas.IdeaThings") .FirstOrDefault(); userData = from u in db.Users .Include("UserFriends.User.UserSkills.Skill") .Include("UserFriends1.User1.UserSkills.Skill") .FirstOrDefault(); 

    Acima trará um pequeno dataset do database usando mais viagens para o database.

    Eu escrevi um post no blog acima disso usando minha própria experiência.

    Espero que isso ajude você.

    Sim vai. Evite usar Incluir se ele expandir várias linhas de detalhes em uma linha da tabela principal.

    Eu acredito que o EF converte a consulta em uma grande associação em vez de várias consultas. Portanto, você acabará duplicando os dados da tabela principal em todas as linhas da tabela de detalhes.

    Por exemplo: Mestre -> Detalhes. Digamos, mestre tem 100 linhas, detalhes tem 5000 linhas (50 para cada mestre).

    Se você preguiçar os detalhes, retornará 100 linhas (tamanho: mestre) + 5000 linhas (tamanho: detalhes).

    Se você usar .Include (“Detalhes”), retornará 5000 linhas (tamanho: mestre + detalhes). Essencialmente, a parte principal é duplicada mais de 50 vezes.

    Ele se multiplica se você include várias tabelas.

    Verifique o SQL gerado pelo EF.

    Eu recomendo que você faça testes de carga e meça o desempenho do site sob estresse. Se você estiver executando consultas complexas em cada solicitação, considere o armazenamento em cache de alguns resultados.

    O resultado da inclusão pode mudar: depende da entidade que chama o método include.

    Como o exemplo proposto por Ladislav Mrnka, suponha que tenhamos uma entidade

    Cliente (Id, Nome, Endereço)

    esse mapa para esta tabela:

     Id | Name | Address ----------------------- C1 | Paul | XYZ 

    e uma entidade Order (Id, CustomerId, Total)

    esse mapa para esta tabela:

     Id | CustomerId | Total ----------------------- O1 | C1 | 10.00 O2 | C1 | 13.00 

    A relação é um cliente para muitas ordens


    Exemplo 1: Cliente => Pedidos

     var customer = context.Customers .Include("Orders") .SingleOrDefault(c => c.Id == "C1"); 

    Linq será traduzido em uma consulta sql muito complexa.

    Nesse caso, a consulta produzirá dois registros e as informações sobre o cliente serão replicadas.

      Customer.Id | Customer.Name | Order.Id | Order.Total ----------------------------------------------------------- C1 | Paul | O1 | 10.00 C1 | Paul | O2 | 13.00 

    Exemplo 2: Pedido => Cliente

     var order = context.Orders .Include("Customers") .SingleOrDefault(c => c.Id == "O1"); 

    Linq será traduzido em um simples sql Join.

    Nesse caso, a consulta produzirá apenas um registro sem duplicação de informações:

      Order.Id | Order.Total | Customer.Id | Customer.Name ----------------------------------------------------------- O1 | 10.00 | C1 | Paul