Estrutura de entidade e pool de conexão

268

Recentemente, comecei a usar o Entity Framework 4.0 no meu aplicativo .NET 4.0 e estou curioso sobre algumas coisas relacionadas ao pool.

  1. O pool de conexões como eu sei é gerenciado pelo provedor de dados ADO.NET, no meu caso, o do MS SQL Server. Isso se aplica quando você instancia um novo contexto de entidades ( ObjectContext), ou seja, sem parâmetros new MyDatabaseModelEntities()?

  2. Quais são as vantagens e desvantagens de a) criar um contexto de entidades globais para o aplicativo (ou seja, uma instância estática) ou b) criar e expor um contexto de entidades para cada operação / método, com um usingbloco.

  3. Quaisquer outras recomendações, práticas recomendadas ou abordagens comuns para determinados cenários que eu deveria conhecer?

Noldorin
fonte

Respostas:

369
  1. O pool de conexões é tratado como em qualquer outro aplicativo ADO.NET. A conexão de entidade ainda usa a conexão tradicional do banco de dados com a seqüência de conexão tradicional. Acredito que você pode desativar o pool de conexões na cadeia de conexão se não quiser usá-lo. (leia mais sobre o pool de conexões do SQL Server (ADO.NET) )
  2. Nunca use o contexto global. O ObjectContext implementa internamente vários padrões, incluindo Mapa de Identidade e Unidade de Trabalho. O impacto do uso do contexto global é diferente por tipo de aplicativo.
  3. Para aplicativos da Web, use um contexto único por solicitação. Para serviços da Web, use um contexto único por chamada. No aplicativo WinForms ou WPF, use o contexto único por formulário ou por apresentador. Pode haver alguns requisitos especiais que não permitirão usar essa abordagem, mas na maioria das situações isso é suficiente.

Se você deseja saber qual impacto possui um contexto de objeto único para o aplicativo WPF / WinForm, consulte este artigo . É sobre o NHibernate Session, mas a idéia é a mesma.

Editar:

Quando você usa EF, por padrão, carrega cada entidade apenas uma vez por contexto. A primeira consulta cria instace de entidade e a armazena internamente. Qualquer consulta subsequente que exija entidade com a mesma chave retorna essa instância armazenada. Se os valores no armazenamento de dados foram alterados, você ainda recebe a entidade com valores da consulta inicial. Isso é chamado de padrão de mapa de identidade . Você pode forçar o contexto do objeto a recarregar a entidade, mas ele recarregará uma única instância compartilhada.

Quaisquer alterações feitas na entidade não serão mantidas até você chamar SaveChangeso contexto. Você pode fazer alterações em várias entidades e armazená-las de uma só vez. Isso é chamado de padrão de Unidade de Trabalho . Você não pode dizer seletivamente qual entidade anexa modificada deseja salvar.

Combine esses dois padrões e você verá alguns efeitos interessantes. Você tem apenas uma instância da entidade para todo o aplicativo. Quaisquer alterações na entidade afetam todo o aplicativo, mesmo que as alterações ainda não sejam persistidas (confirmadas). Na maioria das vezes, isso não é o que você deseja. Suponha que você tenha um formulário de edição no aplicativo WPF. Você está trabalhando com a entidade e decide cancelar edições complexas (alteração de valores, adição de entidades relacionadas, remoção de outras entidades relacionadas etc.). Mas a entidade já está modificada em contexto compartilhado. O que você vai fazer? Dica: não conheço nenhuma CancelChanges ou UndoChanges emObjectContext .

Acho que não precisamos discutir o cenário do servidor. O simples compartilhamento de uma única entidade entre várias solicitações HTTP ou chamadas de serviço da Web torna seu aplicativo inútil. Qualquer solicitação pode apenas dispararSaveChanges e salvar dados parciais de outra solicitação, porque você está compartilhando uma única unidade de trabalho entre todas elas. Isso também terá outro problema - o contexto e qualquer manipulação com entidades no contexto ou uma conexão de banco de dados usada pelo contexto não é segura para threads.

Mesmo para um aplicativo somente leitura, um contexto global não é uma boa opção, porque você provavelmente deseja novos dados sempre que consultar o aplicativo.

Ladislav Mrnka
fonte
Obrigado pela sua resposta. Talvez você possa explicar por que é ruim usar um único contexto global? Isso dificulta o acesso paralelo, com certeza, mas o que mais ...?
Noldorin
Ok, está muito mais claro agora, obrigado. Apenas para confirmar, embora um contexto global nunca seja realmente apropriado, um único contexto para uma "caixa de diálogo de edição" pode ser o caminho certo? Em outras situações, como serviços da Web e ASP.NET, os contextos dentro dos métodos fazem mais sentido. Sobre correto?
Noldorin
Peguei seu conselho e tirei o singelton. Agora eu recebo outro erro: stackoverflow.com/questions/14795899/…
Elad Benda
Entendo que a implementação do padrão da Unidade de Trabalho e o encapsulamento do DbContext devem separar a lógica comercial e as operações do banco de dados. Não consigo entender como implementar o padrão Unidade de trabalho e usar o TransactionScope apenas para algumas operações.
Rudolf Dvoracek
4
@RudolfDvoracek: Facilmente. TransactionScopenão pertence à unidade de trabalho, pertence à sua lógica de negócios porque a própria lógica define a transação. A unidade de trabalho define apenas o que deve ser mantido em conjunto, enquanto o escopo da transação permite o uso da persistência da unidade de trabalho várias vezes na mesma transação.
Ladislav Mrnka
70

De acordo com Daniel Simmons:

Crie uma nova instância ObjectContext em uma instrução Using para cada método de serviço, para que seja descartada antes do retorno do método. Esta etapa é crítica para a escalabilidade do seu serviço. Ele garante que as conexões com o banco de dados não sejam mantidas abertas nas chamadas de serviço e que o estado temporário usado por uma operação específica seja coletado como lixo quando essa operação terminar. O Entity Framework armazena em cache automaticamente os metadados e outras informações necessárias no domínio do aplicativo e o ADO.NET agrupa as conexões com o banco de dados, portanto, recriar o contexto sempre que é uma operação rápida.

Isto é de seu artigo abrangente aqui:

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

Acredito que esse conselho se estende às solicitações HTTP, portanto seria válido para o ASP.NET. Um aplicativo state-fat-client, como um aplicativo WPF, pode ser o único caso de um contexto "compartilhado".

Dave Swersky
fonte
Obrigado, essa é uma citação muito informativa lá. No entanto, ainda estou pensando se um contexto compartilhado (global) seria apropriado mesmo para um aplicativo WPF cliente ou algo assim. Existe alguma vantagem mesmo neste caso?
Noldorin
Não haveria vantagem em um contexto global em um aplicativo WPF, mas provavelmente também não haveria prejuízo significativo. Se você implementar um contexto global, poderá ser necessário gerenciar manualmente as conexões com o banco de dados (fechamento explícito da conexão) em casos de altas taxas de solicitação.
Dave Swersky
1
Certo; Então, essencialmente, nunca posso realmente dar errado usando vários contextos temporários (desde que eu saiba que o pool de conexões está acontecendo)? ... Se você estivesse usando um único contexto global, a conexão na teoria não poderia cair em um ponto aleatório no tempo?
Noldorin
1
@Nolodrin: Eu não acho que a conexão caia "aleatoriamente" ... o risco é que as conexões possam ser mantidas abertas por muito tempo e saturar o pool de conexões.
Dave Swersky
1
Implementar ObjectContext / DbContext IDisposable, portanto, deve ser aberto pelo menor tempo razoável, é a minha opinião.
precisa
12

De acordo com a documentação do EF6 (4,5 também): https://msdn.microsoft.com/en-us/data/hh949853#9

9.3 Contexto por solicitação

Os contextos do Entity Framework devem ser usados ​​como instâncias de curta duração para fornecer a melhor experiência de desempenho . Espera-se que os contextos tenham vida curta e sejam descartados e, como tal, foram implementados para serem muito leves e reutilizarem metadados sempre que possível. Em cenários da Web, é importante manter isso em mente e não ter um contexto por mais do que a duração de uma única solicitação. Da mesma forma, em cenários que não são da Web, o contexto deve ser descartado com base no seu entendimento dos diferentes níveis de armazenamento em cache no Entity Framework. De um modo geral, deve-se evitar ter uma instância de contexto ao longo da vida do aplicativo, bem como contextos por thread e contextos estáticos.

Raj Rao
fonte
2
Sei que essa resposta está aqui há um tempo, mas devo dizer que isso me salvou uma tonelada de dor de cabeça. Manteve o erro "Conexão em pool" ao usar EF com Oracle e não conseguiu descobrir o porquê. Eu havia configurado o dbContext como uma variável de classe, instanciando-o na criação. Mudá-lo para criar o contexto conforme necessário corrigiu todos os males do meu mundo. Obrigado!
Fletchius
1

O código abaixo ajudou meu objeto a ser atualizado com novos valores de banco de dados. O comando Entry (object) .Reload () força o objeto a recuperar valores do banco de dados

GM_MEMBERS member = DatabaseObjectContext.GM_MEMBERS.FirstOrDefault(p => p.Username == username && p.ApplicationName == this.ApplicationName);
DatabaseObjectContext.Entry(member).Reload();
HGMamaci
fonte
assim como para coleções (código VB):CType(myContext, IObjectContextAdapter).ObjectContext.Refresh(RefreshMode.StoreWins,myCustomers)
Ivan Ferrer Villa