Teilen über


Verbindungsverwaltung

Diese Seite beschreibt das Verhalten von Entity Framework bei der Übergabe von Verbindungen an den Kontext und die Funktionalität der API für Database.Connection.Open().

Übergeben von Verbindungen an den Kontext

Verhalten für EF 5 und frühere Versionen

Es gibt zwei Konstruktoren, die Verbindungen akzeptieren:

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

Diese können verwendet werden, aber Sie müssen einige Einschränkungen umgehen:

  1. Wenn Sie eine geöffnete Verbindung an einen dieser Konstruktoren übergeben, wird beim ersten Versuch des Frameworks, sie zu verwenden, eine InvalidOperationException ausgelöst, die besagt, dass eine bereits geöffnete Verbindung nicht erneut geöffnet werden kann.
  2. Das Flag contextOwnsConnection wird dahin gehend interpretiert, ob die zugrunde liegende Speicherverbindung verworfen werden soll, wenn der Kontext verworfen wird. Unabhängig von dieser Einstellung wird die Speicherverbindung jedoch immer geschlossen, wenn der Kontext verworfen wird. Wenn mehrere DbContext-Elemente mit derselben Verbindung vorliegen, schließt daher der Kontext, der zuerst gelöscht wird, die Verbindung. (Wenn Sie eine vorhandene ADO.NET-Verbindung mit einem DbContext gemischt haben, schließt DbContext ebenso die Verbindung immer, wenn sie verworfen wird.)

Die erste genannte Einschränkung kann umgangen werden, indem eine geschlossene Verbindung übergeben und Code, der sie öffnen würde, erst ausgeführt wird, wenn alle Kontexte erstellt wurden:

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

Die zweite Einschränkung bedeutet nur, dass Sie DbContext-Objekte nicht löschen dürfen, bis Sie bereit sind, die Verbindung zu schließen.

Verhalten in EF 6 und zukünftigen Versionen

In EF 6 und zukünftigen Versionen weist DbContext die gleichen beiden Konstruktoren auf, erfordert jedoch nicht mehr, dass die an den Konstruktor übergebene Verbindung geschlossen ist, wenn sie empfangen wird. Daher ist jetzt Folgendes möglich:

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

Außerdem steuert das Flag contextOwnsConnection jetzt, ob die Verbindung geschlossen und verworfen wird, wenn der DbContext verworfen wird. Im obigen Beispiel wird die Verbindung daher nicht geschlossen, wenn der Kontext verworfen wird (Zeile 32), wie es in früheren Versionen von EF der Fall wäre, sondern wenn die Verbindung selbst verworfen wird (Zeile 40).

Selbstverständlich ist es für DbContext weiterhin möglich, die Kontrolle über die Verbindung zu übernehmen. Legen Sie dazu einfach contextOwnsConnection auf TRUE fest, oder verwenden Sie einen der anderen Konstruktoren.

Hinweis

Es gibt einige zusätzliche Überlegungen bei der Verwendung von Transaktionen mit diesem neuen Modell. Ausführliche Informationen finden Sie unter Arbeiten mit Transaktionen.

Database.Connection.Open()

Verhalten für EF 5 und frühere Versionen

In EF 5 und früheren Versionen ist ein Fehler vorhanden, durch den ObjectContext.Connection.State nicht entsprechend dem tatsächlichen Zustand der zugrunde liegenden Speicherverbindung aktualisiert wurde. Wenn Sie beispielsweise den folgenden Code ausgeführt haben, kann der Status Geschlossen zurückgegeben werden, obwohl die zugrunde liegende Speicherverbindung eigentlich Geöffnetist.

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

Wenn Sie unabhängig davon die Datenbankverbindung öffnen, indem Sie Database.Connection.Open() aufrufen, bleibt sie geöffnet, bis Sie die nächste Abfrage ausführen oder etwas aufgerufen, das eine Datenbankverbindung erfordert (z. B. SaveChanges()). Danach wird die zugrunde liegende Speicherverbindung jedoch geschlossen. Der Kontext wird dann jedes Mal erneut geöffnet und die Verbindung wieder geschlossen, wenn ein anderer Datenbankvorgang erforderlich ist:

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

Verhalten in EF 6 und zukünftigen Versionen

Für EF 6 und zukünftige Versionen wurde folgender Ansatz verfolgt: Wenn der aufrufende Code die Verbindung durch Aufrufen von context.Database.Connection.Open() öffnet, gibt es dafür einen Grund, und das Framework geht davon aus, dass er die Kontrolle über das Öffnen und Schließen der Verbindung wünscht, und schließt die Verbindung nicht mehr automatisch.

Hinweis

Dies kann potenziell zu Verbindungen führen, die lange Zeit geöffnet sind, daher sollten Sie vorsichtig vorgehen.

Außerdem wurde der Code aktualisiert, sodass ObjectContext.Connection.State jetzt den Status der zugrunde liegenden Verbindung ordnungsgemäß nachverfolgt.

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