Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
На этой странице описывается поведение Entity Framework в отношении передачи подключений к контексту и функциональных возможностей API Database.Connection.Open().
Передача соединений в контекст
Поведение в EF5 и более ранних версиях
Существует два конструктора, которые принимают подключения:
public DbContext(DbConnection existingConnection, bool contextOwnsConnection)
public DbContext(DbConnection existingConnection, DbCompiledModel model, bool contextOwnsConnection)
Их можно использовать, но вам придется обойти несколько ограничений:
- Если вы передаете открытое подключение к одному из них, то при первой попытке платформы его использовать, будет вызвано исключение InvalidOperationException с сообщением о том, что невозможно повторно открыть уже открытое соединение.
- Флаг contextOwnsConnection интерпретируется как указывающий, следует ли закрывать базовое соединение хранилища при удалении контекста. Но независимо от этого параметра соединение с хранилищем всегда закрывается при освобождении контекста. Таким образом, если у вас есть несколько 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();
}
}
}
}
Кроме того, флаг contextOwnsConnection теперь определяет, закрывается ли соединение и удаляется при удалении DbContext. Таким образом, в приведенном выше примере подключение не закрывается при удалении контекста (строка 32), как это было бы в предыдущих версиях EF, а закрывается при удалении самого подключения (строка 40).
Конечно, dbContext по-прежнему может контролировать соединение (просто задать contextOwnsConnection значение true или использовать один из других конструкторов), если вы хотите.
Замечание
При использовании транзакций с этой новой моделью существуют некоторые дополнительные рекомендации. Дополнительные сведения см. в разделе "Работа с транзакциями".
Database.Connection.Open()
Поведение для EF5 и более ранних версий
В EF5 и более ранних версиях возникает ошибка, в которой объект ObjectContext.Connection.State не был обновлен, чтобы отразить истинное состояние подключения к базовому хранилищу. Например, если вы выполнили следующий код, можно вернуть состояние "Закрыто", хотя на самом деле подключение к базовому хранилищу открыто.
((IObjectContextAdapter)context).ObjectContext.Connection.State
Отдельно при открытии подключения к базе данных путем вызова Database.Connection.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 и будущих версий мы приняли следующий подход: если вызывающий код выбирает открыть подключение, вызвав context.Database.Connection.Open(), то у него есть на это веская причина, и платформа будет считать, что он хочет контролировать открытие и закрытие подключения, и больше автоматически не будет закрывать подключение.
Замечание
При неосторожном использовании это может привести к подключениям, которые остаются открытыми в течение длительного времени.
Мы также обновили код, чтобы ObjectContext.Connection.State теперь отслеживает состояние базового подключения.
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
}
}
}