Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Questa pagina descrive il comportamento di Entity Framework per quanto riguarda il passaggio di connessioni al contesto e la funzionalità dell'API Database.Connection.Open().
Passaggio di connessioni 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:
- 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.
- Il flag contextOwnsConnection 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ù DbContext con la stessa connessione, il primo DbContext che viene rilasciato chiuderà la connessione (analogamente, se si dispone di una connessione ADO.NET esistente abbinata a un DbContext, il DbContext chiuderà sempre la connessione quando viene rilasciato).
È 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 il DbContext ha gli stessi due costruttori, ma non richiede più che la connessione passata al costruttore debba essere 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 contextOwnsConnection 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 contextOwnsConnection su true o usare uno degli altri costruttori) se lo si desidera.
Annotazioni
Quando si usano transazioni con questo nuovo modello, è necessario tenere presenti alcune considerazioni aggiuntive. Per informazioni dettagliate, vedere Utilizzo delle transazioni.
Database.Connection.Open()
Comportamento per EF5 e versioni precedenti
In EF5 e versioni precedenti è presente un bug in modo che ObjectContext.Connection.State non sia stato aggiornato in modo da riflettere il vero stato 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.Connection.Open() verrà aperta 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 la chiusura della connessione all'archivio sottostante. 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.Connection.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.
Annotazioni
Questo può potenzialmente portare a connessioni che rimangono aperte per molto tempo, quindi si consiglia di usarle con cautela.
È stato aggiornato anche il codice in modo che ObjectContext.Connection.State tenga ora 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
}
}
}