연결 관리

이 페이지에서는 컨텍스트에 연결을 전달하는 것과 관련된 Entity Framework의 동작과 Database.Connection.Open() API의 기능을 설명합니다.

컨텍스트에 연결 전달

EF5 및 이전 버전에 대한 동작

연결을 허용하는 두 가지 생성자가 있습니다.

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

이러한 항목을 사용할 수 있지만 몇 가지 제한 사항을 해결해야 합니다.

  1. 두 연결 중 하나에 열린 연결을 전달하면 프레임워크가 처음 사용하려고 할 때 이미 열려 있는 연결을 다시 열 수 없다는 InvalidOperationException이 throw됩니다.
  2. 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가 삭제될 때 연결이 닫히고 삭제되는지 여부를 제어합니다. 따라서 위의 예제에서는 컨텍스트가 이전 버전의 EF에서와 같이 삭제될 때(줄 32) 연결이 닫히는 것이 아니라 연결 자체가 삭제될 때(줄 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
        }
    }
}