Este artigo foi traduzido por máquina.

Pontos de dados

Algumas das minhas coisas favoritas... na entidade quadro 4.2 DbContext

Julie Lerman

Julie Lerman
Antes mesmo de entidade Framework 4.1 foi lançado no início de 2011, os desenvolvedores foram concentrados em apenas metade do que foi dado a nós em que o pacote: O código primeiro. Código primeiro permite expressar seu modelo de dados de entidade usando suas classes de domínio e configurações de código primeiro, uma ótima alternativa para os desenvolvedores que não querem usar o visual designer para definir o modelo. Mas cada bit de código de exemplo que você vê para o uso de entidade Framework (EF) com essas classes e modelos de código primeiro definido é conduzido por uma outra característica muito importante que veio no EF 4.1: a classe DbContext.

A classe ObjectContext é parte do núcleo do EF API do Microsoft.NET Framework 4 e é a classe que permite que você realize consultas, o controle de alterações e atualizar o banco de dados usando as classes com rigidez de tipos que representam seu modelo. A classe DbContext é melhor descrita como um wrapper em torno de ObjectContext que apresenta o mais comumente usado características de ObjectContext, bem como fornece alguns mais simples "atalhos" para tarefas que são freqüentemente usados, mas complicados de código diretamente com ObjectContext.

É minha orientação e da Microsoft que você deve considerar DbContext primeiro quando começando novos projetos usando o EF. Se você encontrar que você ocasionalmente precisa acessar uma lógica mais granular que fornece a classe ObjectContext, há um gancho para obter de uma instância de DbContext a sua ObjectContext subjacente:

var objectContext = (myDbContextInstance as IObjectContextAdapter).ObjectContext

Se você sabe que você estará fazendo trabalho que exija o uso freqüente de ObjectContext recursos diretamente, você pode preferir usar que em vez de DbContext. Mas, em geral, a equipe do EF recomenda que você evite usar ObjectContext diretamente, a menos que você estiver impedido de usar DbContext por algum motivo.

Vou acrescentar a ressalva de que este guia é destinado a novos projetos. Ao trabalhar com a API DbContext, você obter não só o novo slimmer e mais esperto DbContext classe, mas também igualmente melhoradas de classes DbSet e DbQuery (contrapartes ObjectSet e ObjectQuery).

Embora eu sou um grande fã da DbContext, algumas de suas características tornaram-se meus animais de estimação pouco favoritos.

DbSet.Find

Um dos novos métodos na API é DbSet.Find. Isto ajuda com um uso comum de desenvolvedores padrão para acesso a dados: Recuperando um objeto único baseado em sua chave primária.

Com ObjectContext, você teria que criar uma consulta completa e, em seguida, executar a consulta usando um método LINQ, como SingleOrDefault.

Que seria aparência:

var partyHatQuery = from p in context.PartyHats where p.Id == 3 select p;
var partyHatInstance = partyHatQuery.SingleOrDefault();

Você poderia escrever que mais eficientemente com métodos LINQ e um lambda:

var partyHatInstance = context.PartyHats.SingleOrDefault(p => p.Id == 3);

Quantas vezes você ter executadas consultas que executam esta tarefa simples? Você mesmo pode ter abstraído esse código no seu próprio método mais simples.

Este é apenas o que a equipe EF feito para você na API DbContext. Ao trabalhar com DbContext, PartyHats seria um DbSet <PartyHat>, e você pode usar o método DbSet.Find para atingir rapidamente a execução da consulta mesmo com:

context.PartyHats.Find(3)

Este método assume que o valor você fornecer é o valor da chave para a classe que você está procurando — neste caso, PartyHat. EF, em seguida, executará uma consulta SingleOrDefault em seu nome, procurando os dados onde Id é igual ao valor passado — neste caso, 3. Você provavelmente vai passar em uma variável, não um valor real.

Há outro benefício para o método DbSet.Find que você não pode obter com uma consulta. O método Find procurará primeiro na memória para um objeto correspondente que está sendo rastreado pelo contexto. Se for encontrado, EF não incomodará consultando o banco de dados. Isso é muito mais eficiente do que executar uma consulta no banco de dados apenas para jogar fora os resultados da consulta se a instância do objeto já está na memória — uma viagem desperdiçada para o banco de dados que muitos desenvolvedores disparam sem perceber.

Você também pode usar DbSet.Find com chaves compostas. A assinatura de localizar não é tomar um único objeto mas ter uma matriz de parâmetros. Portanto você pode passar em uma lista de valores para representar os valores que compõem a chave.

DbSet.Local

Ao trabalhar com EF, freqüentemente me vi querendo fazer alguma coisa com objetos que já estavam na memória e sendo controladas por um contexto. Lugares típicos para esta lógica são de SaveChanges substituir ou SavingChanges, onde eu vou realizar alguma validação de. (Graças a nova API de validação que está disponível juntamente com DbContext, eu fui capaz de reduzir grande parte desta lógica. Mas eu não discuto validação nesta coluna).

ObjectContext fornecem uma maneira de descobrir os objetos que ele é o acompanhamento, mas a lógica de API para fazer isso é fácil de encontrar, nem fácil de código. Na verdade, no meu livro, "Programming Entity Framework" (o ' Reilly Media, 2010), eu escrevi um conjunto de quatro extensões do método para ajudar a tornar essa tarefa mais simples e mais flexível.

Mais comumente, no entanto, os desenvolvedores não percebem a diferença entre a execução de uma consulta LINQ to Entities no contexto e interagindo com os objetos que o contexto já é controle. Por exemplo, tenho visto muita código onde um desenvolvedor recupera dados usando uma consulta e, em seguida, tenta executar lógica sobre o que está agora a ser gerenciado pela consulta:

var balloons = context.Balloons.Where(b => b.Size == "L").ToList();
 var balloonCount = context.Balloons.Count();

Na verdade, estas são duas consultas distintas. A segunda linha de código executa outra consulta no banco de dados e retorna uma contagem de todos os balões. Normalmente, o que o desenvolvedor tinha a intenção era obter uma contagem dos resultados — ou seja, balões.Contagem.

Se você não tem acessar a uma variável, mas ainda quero descobrir quantos objetos de balão é o acompanhamento de um ObjectContext, há uma maneira de descobrir, mas não é fácil: ObjectContext apresenta um ObjectState­Manager, que tem um método chamado GetObjectStateEntries. Este método requer que você passe em um ou mais enums EntityState (por exemplo, acrescentado, modificado e assim por diante) para que ele saiba quais entradas para retornar. Embora os resultados sejam queryable, filtragem é pesada e mesmo assim o que ele retorna não é suas entidades, mas as ObjectStateEntry instâncias que representam informações de Estado sobre os objetos.

Isso significa que, sem o uso de meus métodos de extensão, código para ajudar a obter a contagem dos balões na memória parece com isto:

objectContext.ObjectStateManager
 .GetObjectStateEntries(EntityState.Added |
                        EntityState.Modified | EntityState.Unchanged)
 .Where(e => e.Entity is Balloon).Count();

Se você quiser capturar esses objetos de balão, não apenas as instâncias de ObjectStateEntry, então você tem que adicionar algum vazamento para retornar os tipos de ObjectStateEntry.Entity como balões:

objectContext.ObjectStateManager
 .GetObjectStateEntries(EntityState.Added |
                        EntityState.Modified | EntityState.Unchanged)
 .Where(e => e.Entity is Balloon)
 .Select(e => e.Entity as Balloon);

Ver esse código pôde fazê-lo a apreciar a nova propriedade DbSet.Local quase tanto quanto eu faço.

Usando DbSet.Local para obter todas as instâncias de balão controladas do contexto, você pode simplesmente chamar:

context.Balloons.Local;

"Local" retorna um ObservableCollection que oferece duas vantagens. A primeira é que é questionável, para que você possa retornar qualquer subconjunto dos balões localmente em cache desejado. O segundo é que seu código (ou componentes, tais como controles de vinculação de dados) podem ouvir e reagir aos objetos sendo adicionados ou removidos do cache.

Além da propriedade detectável e o código reduzido, há duas outras diferenças notáveis entre o uso de DbSet.Local e GetObjectStateEntries. Uma é que Local retorna objetos do DbSet específico apenas, enquanto GetObjectStateEntries retorna entradas independentemente do tipo de objetos que eles representam. A outra diferença é que o Local não vai retornar objetos que o contexto sabe estão marcados como excluídos. Com GetObjectStateEntries, você tem acesso aos objetos acrescentado, modificado, Unchanged e excluídos conforme especificado na lista de parâmetros que você fornecer para o método.

Consultas LINQ NoTracking

Ao discutir o desempenho com os clientes, eu sempre recomendo que aproveitar a capacidade do EF para retornar dados que não precisam ser controlados pelo contexto. Por exemplo, você pode ter dados que necessários para fornecer uma lista suspensa de seleção. Você nunca precisará fazer alterações a esses dados, muito menos persistem no banco de dados. Portanto, é inteligente para evitar o desempenho batida tomada quando EF cria instâncias de ObjectStateEntry para cada objeto é controle, bem como forçando o contexto estar ciente de quaisquer alterações feitas a esses objetos.

Mas com ObjectContext, o apoio de NoTracking só está disponível através da classe ObjectQuery, não do LINQ para consultas de entidades.

Aqui está um exemplo típico de conseguir uma consulta de NoTracking usando um ObjectContext (chamado contexto):

string entitySQL = " SELECT p, p.Filling " +
                           "FROM PartyContext.Pinatas AS p " +
                           "WHERE p.Filling.Description='Candy'";
var query=context.CreateQuery<DbDataRecord>(entitySQL);
query.MergeOption = System.Data.Objects.MergeOption.NoTracking;
var pinatasWithFilling=query.ToList();

Página visitada em piñatas e recheios seria objetos na memória, mas o contexto não teria nenhum conhecimento deles.

No entanto, se você usar a seguinte consulta LINQ to Entities, que retornará um IQueryable, não um ObjectQuery, não haveria nenhuma propriedade MergeOption:

context.Pinatas.Include("Filling")
  .Where(p=>p.Filling.Description=="Candy")

Uma solução é converter a consulta LINQ para um ObjectQuery e, em seguida, definir o MergeOption. Isso não é apenas não óbvio mas também metálico.

Reconhecendo isso, a equipe EF encontrou uma maneira de deixar você ter sua festa bolo e comê-lo, também, com o novo método de extensão AsNoTracking para IQueryables que é parte da API DbContext. Agora pode coloca-lo sobre a minha consulta LINQ:

context.Pinatas.Include("Filling")
  .Where(p=>p.Filling.Description=="Candy")
  .AsNoTracking();

Isso retornará um conjunto de Pinatas e recheios que serão ignorados pelo contexto. EF não vai wastethe esforço de instanciar objetos de DbEntityEntry (a versão de API DbContext de ObjectStateEntry) para cada objeto. Nem perderá o esforço de forçar o contexto para inspecionar aqueles objetos quando DetectChanges é chamado.

É simples de código e muito detectável através do IntelliSense.

Cereja no bolo

Esses três recursos — encontrar, Local e AsNoTracking — não me permitem executar tarefas que não eram possíveis com o ObjectContext. Mas eles me fazem felizes cada vez que eu usá-los. Existem muitas tarefas que a API DbContext simplifica (em comparação com usando o ObjectContext) de codificação que ele tem simplificado meu desenvolvimento de aplicativo um pouco. Eu também retornou ao antigo código de ObjectContext e refatorado para usar DbContext juntamente com o primeiro código e ter sido capaz de reduzir significativamente a quantidade de código em desses aplicativos. Mas para os desenvolvedores que não são tão intimamente familiares com a EF como eu sou, a descoberta de muitos de seus recursos fará uma grande diferença para levantar-se e executar com ele.

Julie Lerman é um MVP da Microsoft.NET mentor e consultor que vive nas montanhas de Vermont. Você pode encontrar sua apresentação sobre acesso a dados e outro Microsoft.NET tópicos em grupos de usuários e conferências em todo o mundo. She blogs at thedatafarm.com e é o autor de "Programming Entity Framework" (2010) e "Programming Entity Framework: Code First"(2011), ambos do ' Reilly Media. Siga ela no Twitter em twitter.com/julielerman.

Graças ao seguinte especialista técnico para revisão deste artigo: Arthur Vickers