Gestione delle connessioni

Questa pagina descrive il comportamento di Entity Framework per quanto riguarda il passaggio di connessioni al contesto e la funzionalità di Database.ConnessioneIon. API Open().

Passaggio di Connessione ions al contesto

Comportamento per EF5 e versioni precedenti

Esistono due costruttori che accettano connessioni:

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

È possibile usare questi elementi, ma è necessario aggirare un paio di limitazioni:

  1. Se si passa una connessione aperta a uno di questi, la prima volta che il framework tenta di usarla viene generata un'eccezione InvalidOperationException che indica che non è possibile riaprire una connessione già aperta.
  2. Il flag contextOwns Connessione ion viene interpretato per indicare se la connessione all'archivio sottostante deve essere eliminata quando il contesto viene eliminato. Tuttavia, indipendentemente da tale impostazione, la connessione all'archivio viene sempre chiusa quando il contesto viene eliminato. Pertanto, se si dispone di più di un Oggetto DbContext con la stessa connessione che viene eliminata prima di tutto, la connessione verrà chiusa (analogamente se si dispone di una connessione ADO.NET esistente con un oggetto DbContext, DbContext chiuderà sempre la connessione quando viene eliminata).

È possibile aggirare la prima limitazione precedente passando una connessione chiusa ed eseguendo solo il codice che verrà aperto dopo la creazione di tutti i contesti:

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();
                }
            }
        }
    }
}

La seconda limitazione significa solo che è necessario evitare di eliminare uno degli oggetti DbContext fino a quando non si è pronti per la chiusura della connessione.

Comportamento in EF6 e versioni future

In EF6 e nelle versioni future dbContext hanno gli stessi due costruttori, ma non richiede più che la connessione passata al costruttore venga chiusa quando viene ricevuta. Quindi questo è ora possibile:

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();
            }
        }
    }
}

Inoltre, il flag contextOwns Connessione ion controlla ora se la connessione è sia chiusa che eliminata quando dbContext viene eliminato. Pertanto, nell'esempio precedente la connessione non viene chiusa quando il contesto viene eliminato (riga 32) come sarebbe stato nelle versioni precedenti di ENTITY, ma piuttosto quando la connessione stessa viene eliminata (riga 40).

Naturalmente è comunque possibile che DbContext assuma il controllo della connessione (impostare contextOwns Connessione ion su true o usare uno degli altri costruttori) se lo si desidera.

Nota

Quando si usano transazioni con questo nuovo modello, è necessario tenere presenti alcune considerazioni aggiuntive. Per informazioni dettagliate, vedere Utilizzo delle transazioni.

Database. Connessione ion. Open()

Comportamento per EF5 e versioni precedenti

In EF5 e versioni precedenti è presente un bug in modo che ObjectContext.ConnessioneIon. Lo stato non è stato aggiornato per riflettere lo stato effettivo della connessione all'archivio sottostante. Ad esempio, se è stato eseguito il codice seguente, è possibile restituire lo stato Chiuso anche se in realtà la connessione dell'archivio sottostante è Open.

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

Separatamente, se si apre la connessione al database chiamando Database. Connessione ion. Open() verrà aperto fino alla successiva esecuzione di una query o di una chiamata a qualsiasi elemento che richiede una connessione di database(ad esempio, SaveChanges()), ma dopo che la connessione all'archivio sottostante verrà chiusa. Il contesto riaprirà e chiuderà nuovamente la connessione ogni volta che è necessaria un'altra operazione del database:

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 in EF6 e versioni future

Per EF6 e le versioni future è stato adottato l'approccio che se il codice chiamante sceglie di aprire la connessione chiamando il contesto. Database. Connessione ion. Open() ha quindi un buon motivo per farlo e il framework presupporrà che vuole controllare l'apertura e la chiusura della connessione e non chiuderà più automaticamente la connessione.

Nota

Ciò può causare potenzialmente connessioni aperte per molto tempo, quindi usarle con attenzione.

È stato aggiornato anche il codice in modo che ObjectContext. Connessione ion. Lo stato ora tiene traccia dello stato della connessione sottostante correttamente.

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
        }
    }
}