Compartilhar via


Gerenciamento de Conexão

Esta página descreve o comportamento do Entity Framework em relação à passagem de conexões para o contexto e a funcionalidade da API Database.Connection.Open().

Passando conexões para o contexto

Comportamento para EF5 e versões anteriores

Existem dois construtores que aceitam conexões:

public DbContext(DbConnection existingConnection, bool contextOwnsConnection)
public DbContext(DbConnection existingConnection, DbCompiledModel model, bool contextOwnsConnection)

É possível usá-los, mas você tem que contornar algumas limitações:

  1. Se você passar uma conexão aberta para qualquer um deles, na primeira vez que a estrutura tentar usá-lo, um InvalidOperationException será lançado dizendo que não é possível reabrir uma conexão já aberta.
  2. O sinalizador contextOwnsConnection é interpretado para significar se a conexão de armazenamento subjacente deve ou não ser descartada quando o contexto é descartado. Mas, independentemente dessa configuração, a conexão de armazenamento é sempre fechada quando o contexto é descartado. Portanto, se você tiver mais de um DbContext com a mesma conexão, qualquer contexto descartado primeiro fechará a conexão (da mesma forma, se você tiver misturado uma conexão de ADO.NET existente com um DbContext, dbContext sempre fechará a conexão quando ela for descartada).

É possível contornar a primeira limitação acima passando uma conexão fechada e executando apenas o código que a abriria depois que todos os contextos fossem criados:

using System.Collections.Generic;
using System.Data.Common;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.EntityClient;
using System.Linq;

namespace ConnectionManagementExamples
{
    class ConnectionManagementExampleEF5
    {         
        public static void TwoDbContextsOneConnection()
        {
            using (var context1 = new BloggingContext())
            {
                var conn =
                    ((EntityConnection)  
                        ((IObjectContextAdapter)context1).ObjectContext.Connection)  
                            .StoreConnection;

                using (var context2 = new BloggingContext(conn, contextOwnsConnection: false))
                {
                    context2.Database.ExecuteSqlCommand(
                        @"UPDATE Blogs SET Rating = 5" +
                        " WHERE Name LIKE '%Entity Framework%'");

                    var query = context1.Posts.Where(p => p.Blog.Rating > 5);
                    foreach (var post in query)
                    {
                        post.Title += "[Cool Blog]";
                    }
                    context1.SaveChanges();
                }
            }
        }
    }
}

A segunda limitação significa apenas que você precisa se abster de descartar qualquer um de seus objetos DbContext até que você esteja pronto para que a conexão seja fechada.

Comportamento no EF6 e versões futuras

No EF6 e em versões futuras, o DbContext tem os mesmos dois construtores, mas não requer mais que a conexão passada para o construtor seja fechada quando for recebida. Então isso agora é possível:

using System.Collections.Generic;
using System.Data.Entity;
using System.Data.SqlClient;
using System.Linq;
using System.Transactions;

namespace ConnectionManagementExamples
{
    class ConnectionManagementExample
    {
        public static void PassingAnOpenConnection()
        {
            using (var conn = new SqlConnection("{connectionString}"))
            {
                conn.Open();

                var sqlCommand = new SqlCommand();
                sqlCommand.Connection = conn;
                sqlCommand.CommandText =
                    @"UPDATE Blogs SET Rating = 5" +
                     " WHERE Name LIKE '%Entity Framework%'";
                sqlCommand.ExecuteNonQuery();

                using (var context = new BloggingContext(conn, contextOwnsConnection: false))
                {
                    var query = context.Posts.Where(p => p.Blog.Rating > 5);
                    foreach (var post in query)
                    {
                        post.Title += "[Cool Blog]";
                    }
                    context.SaveChanges();
                }

                var sqlCommand2 = new SqlCommand();
                sqlCommand2.Connection = conn;
                sqlCommand2.CommandText =
                    @"UPDATE Blogs SET Rating = 7" +
                     " WHERE Name LIKE '%Entity Framework Rocks%'";
                sqlCommand2.ExecuteNonQuery();
            }
        }
    }
}

Além disso, o sinalizador contextOwnsConnection agora controla se a conexão é fechada e descartada quando o DbContext é descartado. Assim, no exemplo acima, a conexão não é fechada quando o contexto é descartado (linha 32) como teria sido nas versões anteriores do EF, mas sim quando a conexão em si é descartada (linha 40).

É claro que ainda é possível para o DbContext assumir o controle da conexão (basta definir contextOwnsConnection como true ou usar um dos outros construtores) se você assim desejar.

Observação

Há algumas considerações adicionais ao usar transações com esse novo modelo. Para obter detalhes, consulte Trabalhando com transações.

Database.Connection.Open()

Comportamento para EF5 e versões anteriores

No EF5 e em versões anteriores, há um bug como o ObjectContext.Connection.State não foi atualizado para refletir o verdadeiro estado da conexão de armazenamento subjacente. Por exemplo, se você executou o código a seguir, poderá ser retornado o status Fechado mesmo que, na verdade, a conexão de armazenamento subjacente seja Aberta.

((IObjectContextAdapter)context).ObjectContext.Connection.State

Separadamente, se você abrir a conexão de banco de dados chamando Database.Connection.Open(), ela estará aberta até a próxima vez que você executar uma consulta ou chamar qualquer coisa que exija uma conexão de banco de dados (por exemplo, SaveChanges()) mas depois disso a conexão do armazenamento subjacente será fechada. O contexto reabrirá e fechará novamente a conexão sempre que outra operação de banco de dados for necessária:

using System;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.EntityClient;

namespace ConnectionManagementExamples
{
    public class DatabaseOpenConnectionBehaviorEF5
    {
        public static void DatabaseOpenConnectionBehavior()
        {
            using (var context = new BloggingContext())
            {
                // At this point the underlying store connection is closed

                context.Database.Connection.Open();

                // Now the underlying store connection is open  
                // (though ObjectContext.Connection.State will report closed)

                var blog = new Blog { /* Blog’s properties */ };
                context.Blogs.Add(blog);

                // The underlying store connection is still open  

                context.SaveChanges();

                // After SaveChanges() the underlying store connection is closed  
                // Each SaveChanges() / query etc now opens and immediately closes
                // the underlying store connection

                blog = new Blog { /* Blog’s properties */ };
                context.Blogs.Add(blog);
                context.SaveChanges();
            }
        }
    }
}

Comportamento no EF6 e versões futuras

Para o EF6 e versões futuras, adotamos a abordagem de que, se o código de chamada optar por abrir a conexão chamando o contexto. Database.Connection.Open() então ele tem uma boa razão para fazer isso e a estrutura assumirá que deseja controle sobre a abertura e o fechamento da conexão e não fechará mais a conexão automaticamente.

Observação

Isso pode potencialmente levar a conexões que estão abertas por um longo tempo, então use com cuidado.

Também atualizamos o código para que ObjectContext.Connection.State agora mantenha o controle do estado da conexão subjacente corretamente.

using System;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Core.EntityClient;
using System.Data.Entity.Infrastructure;

namespace ConnectionManagementExamples
{
    internal class DatabaseOpenConnectionBehaviorEF6
    {
        public static void DatabaseOpenConnectionBehavior()
        {
            using (var context = new BloggingContext())
            {
                // At this point the underlying store connection is closed

                context.Database.Connection.Open();

                // Now the underlying store connection is open and the
                // ObjectContext.Connection.State correctly reports open too

                var blog = new Blog { /* Blog’s properties */ };
                context.Blogs.Add(blog);
                context.SaveChanges();

                // The underlying store connection remains open for the next operation  

                blog = new Blog { /* Blog’s properties */ };
                context.Blogs.Add(blog);
                context.SaveChanges();

                // The underlying store connection is still open

           } // The context is disposed – so now the underlying store connection is closed
        }
    }
}