Udostępnij za pośrednictwem


Zarządzanie połączeniami

Na tej stronie opisano zachowanie platformy Entity Framework w odniesieniu do przekazywania połączeń do kontekstu i funkcjonalności elementu Database.PołączenieJonów. Interfejs API Open().

Przekazywanie Połączenie ions do kontekstu

Zachowanie w przypadku programu EF5 i starszych wersji

Istnieją dwa konstruktory, które akceptują połączenia:

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

Można ich użyć, ale trzeba obejść kilka ograniczeń:

  1. Jeśli przekażesz otwarte połączenie do jednego z tych elementów, po raz pierwszy platforma spróbuje go użyć, zostanie zgłoszony wyjątek InvalidOperationException z informacją, że nie może ponownie otworzyć już otwartego połączenia.
  2. Flaga contextOwns Połączenie ion jest interpretowana, aby oznaczać, czy bazowe połączenie magazynu powinno zostać usunięte po usunięciu kontekstu. Jednak niezależnie od tego ustawienia połączenie magazynu jest zawsze zamykane po usunięciu kontekstu. Jeśli więc masz więcej niż jeden obiekt DbContext z tym samym połączeniem, w zależności od kontekstu, który jest usuwany, najpierw zamknie połączenie (podobnie jeśli masz mieszane istniejące połączenie ADO.NET z bazą danych DbContext, dbContext zawsze zamknie połączenie, gdy zostanie usunięte).

Istnieje możliwość obejścia pierwszego ograniczenia powyżej przez przekazanie zamkniętego połączenia i wykonanie tylko kodu, który otworzy go po utworzeniu wszystkich kontekstów:

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

Drugie ograniczenie oznacza jedynie, że musisz powstrzymać się od usunięcia dowolnego obiektu DbContext, dopóki nie będzie gotowe do zamknięcia połączenia.

Zachowanie w programie EF6 i przyszłych wersjach

W wersjach EF6 i przyszłych dbContext ma te same dwa konstruktory, ale nie wymaga już, aby połączenie przekazane do konstruktora zostało zamknięte po odebraniu. Teraz jest to możliwe:

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

Ponadto flaga contextOwns Połączenie ion określa teraz, czy połączenie jest zarówno zamknięte, jak i usunięte po usunięciu obiektu DbContext. W powyższym przykładzie połączenie nie jest zamykane, gdy kontekst jest usuwany (wiersz 32), tak jak w poprzednich wersjach programu EF, ale raczej wtedy, gdy samo połączenie zostanie usunięte (wiersz 40).

Oczywiście nadal istnieje możliwość przejęcia kontroli nad połączeniem (po prostu ustaw kontekstOwns Połączenie ion na wartość true lub użyj jednego z innych konstruktorów), jeśli chcesz.

Uwaga

Podczas korzystania z transakcji z tym nowym modelem należy wziąć pod uwagę pewne dodatkowe kwestie. Aby uzyskać szczegółowe informacje, zobacz Praca z transakcjami.

Bazy danych. Połączenie ion. Open()

Zachowanie w przypadku programu EF5 i starszych wersji

W programie EF5 i starszych wersjach występuje usterka, która polega na tym, że obiekt ObjectContext.PołączenieJonów. Stan nie został zaktualizowany w celu odzwierciedlenia rzeczywistego stanu bazowego połączenia magazynu. Jeśli na przykład wykonano następujący kod, można zwrócić stan Zamknięty, mimo że w rzeczywistości bazowe połączenie magazynu ma wartość Otwórz.

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

Oddzielnie, jeśli otworzysz połączenie z bazą danych, wywołując metodę Database. Połączenie ion. Program Open() zostanie otwarty do następnego wykonania zapytania lub wywołania niczego, co wymaga połączenia z bazą danych (na przykład SaveChanges()), ale po zakończeniu połączenia magazynu bazowego zostanie zamknięte. Następnie kontekst zostanie ponownie otwarty i ponownie zamknie połączenie za każdym razem, gdy jest wymagana inna operacja bazy danych:

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

Zachowanie w programie EF6 i przyszłych wersjach

W przypadku programu EF6 i przyszłych wersji podjęliśmy podejście, które jeśli kod wywołujący zdecyduje się otworzyć połączenie przez wywołanie kontekstu. Bazy danych. Połączenie ion. Funkcja Open() ma dobry powód, aby to zrobić, a struktura zakłada, że chce mieć kontrolę nad otwieraniem i zamykaniem połączenia i nie zamknie już połączenia automatycznie.

Uwaga

Może to potencjalnie prowadzić do połączeń, które są otwarte przez długi czas, więc należy używać z ostrożnością.

Zaktualizowaliśmy również kod tak, aby obiekt ObjectContext. Połączenie ion. Stan teraz śledzi stan połączenia bazowego poprawnie.

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