Поделиться через


Управление соединениями

На этой странице описывается поведение Entity Framework относительно передачи подключений к контексту и функциональным возможностям Database.ПодключениеИонный. Open() API.

Передача Подключение в контекст

Поведение для EF5 и более ранних версий

Существует два конструктора, которые принимают подключения:

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

Их можно использовать, но вам придется обойти несколько ограничений:

  1. Если вы передаете открытое подключение к одному из них, то при первом попытке платформы использовать его в InvalidOperationException возникает сообщение о том, что он не может повторно открыть уже открытое соединение.
  2. Флаг contextOwns Подключение ion интерпретируется, чтобы означать, следует ли удалять подключение к базовому хранилищу при удалении контекста. Но независимо от этого параметра подключение хранилища всегда закрывается при удалении контекста. Таким образом, если у вас несколько DbContext с тем же подключением, какое бы контекст ни было удалено, сначала закроет соединение (аналогично, если вы смешали существующее ADO.NET соединение с DbContext, DbContext всегда закрывает соединение при удалении).

Можно обойти первое ограничение, приведенное выше, передав закрытое соединение и только выполнив код, который откроет его после создания всех контекстов:

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

Второе ограничение просто означает, что необходимо воздержаться от удаления любого из объектов DbContext, пока вы не будете готовы к закрытию подключения.

Поведение в EF6 и будущих версиях

В EF6 и будущих версиях DbContext имеет одинаковые два конструктора, но больше не требуется, чтобы соединение, переданное конструктору, закрывалось при получении. Таким образом, теперь это возможно:

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

Кроме того, флаг contextOwns Подключение ion теперь определяет, закрывается ли соединение и удаляется при удалении DbContext. Таким образом, в приведенном выше примере подключение не закрывается при удалении контекста (строка 32), так как оно было бы в предыдущих версиях EF, а не при удалении самого подключения (строка 40).

Конечно, dbContext по-прежнему может контролировать соединение (просто задать contextOwns Подключение ion значение true или использовать один из других конструкторов, если это необходимо.

Примечание.

При использовании транзакций с этой новой моделью существуют некоторые дополнительные рекомендации. Дополнительные сведения см. в разделе "Работа с транзакциями".

Базы данных. Подключение. Open()

Поведение для EF5 и более ранних версий

В EF5 и более ранних версиях возникает ошибка, так что объект ObjectContext.ПодключениеИонный. Состояние не было обновлено, чтобы отразить истинное состояние подключения к базовому хранилищу. Например, если вы выполнили следующий код, можно вернуть состояние "Закрыто", хотя на самом деле подключение к базовому хранилищу открыто.

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

Отдельно, если вы открываете подключение к базе данных, вызывая базу данных. Подключение. Open() он будет открыт до следующего момента выполнения запроса или вызова чего-либо, для которого требуется подключение к базе данных (например, SaveChanges()), но после этого базовое подключение хранилища будет закрыто. Затем контекст повторно открывается и повторно закрывает подключение в любой момент, когда требуется другая операция базы данных:

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

Поведение в EF6 и будущих версиях

Для EF6 и будущих версий мы приняли подход, который, если вызывающий код выбирает открытие подключения путем вызова контекста. Базы данных. Подключение. Затем open() он имеет хорошую причину для этого, и платформа предполагает, что он хочет контролировать открытие и закрытие подключения и больше не будет закрывать подключение автоматически.

Примечание.

Это может привести к подключениям, которые открыты в течение длительного времени, поэтому используйте с осторожностью.

Мы также обновили код, чтобы ObjectContext. Подключение. Теперь состояние отслеживает состояние базового подключения правильно.

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