Gestion des connexions

Cette page décrit le comportement de Entity Framework en ce qui concerne la transmission de connexions au contexte et les fonctionnalités de l’API Database.Connection.Open().

Passage de connexions au contexte

Comportement pour EF5 et versions antérieures

Il existe deux constructeurs qui acceptent les connexions :

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

Il est possible d’utiliser ces éléments, mais vous devez contourner quelques limitations :

  1. Si vous passez une connexion ouverte à l’une de ces connexions, la première fois que l’infrastructure tentera de l’utiliser, une exception InvalidOperationException sera levée en disant qu’elle ne peut pas rouvrir une connexion déjà ouverte.
  2. L’indicateur contextOwnsConnection est interprété pour indiquer si la connexion de magasin sous-jacente doit être supprimée lorsque le contexte est supprimé. Toutefois, indépendamment de ce paramètre, la connexion de magasin est toujours fermée lorsque le contexte est supprimé. Par conséquent, si vous avez plusieurs DbContext avec la même connexion, le contexte qui est supprimé en premier ferme la connexion (même chose si vous avez mélangé une connexion ADO.NET existante avec un DbContext, DbContext ferme toujours la connexion lorsqu’elle est supprimée).

Il est possible de contourner la première limitation ci-dessus en transmettant une connexion fermée et en exécutant uniquement le code qui l’ouvrirait une fois que tous les contextes ont été créés :

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

La deuxième limitation signifie simplement que vous devez vous abstenir de supprimer l’un de vos objets DbContext jusqu’à ce que vous soyez prêt à fermer la connexion.

Comportement dans EF6 et les versions futures

Dans EF6 et les versions ultérieures, dbContext a les deux mêmes constructeurs, mais ne nécessite plus que la connexion transmise au constructeur soit fermée lorsqu’elle est reçue. Ceci est donc possible :

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

En outre, l’indicateur contextOwnsConnection contrôle maintenant si la connexion est fermée et supprimée lorsque DbContext est supprimé. Par conséquent, dans l’exemple ci-dessus, la connexion n’est pas fermée lorsque le contexte est supprimé (ligne 32), car il aurait été dans les versions précédentes d’EF, mais plutôt lorsque la connexion elle-même est supprimée (ligne 40).

Bien sûr, il est toujours possible que dbContext prenne le contrôle de la connexion (définissez simplement contextOwnsConnection sur true ou utilisez l’un des autres constructeurs) si vous le souhaitez.

Remarque

Il existe des considérations supplémentaires lors de l’utilisation de transactions avec ce nouveau modèle. Pour plus d’informations, consultez Utilisation des transactions.

Database.Connection.Open()

Comportement pour EF5 et versions antérieures

Dans EF5 et les versions antérieures, il existe un bogue de sorte que ObjectContext.Connection.State n’a pas été mis à jour pour refléter l’état réel de la connexion de magasin sous-jacente. Par exemple, si vous avez exécuté le code suivant, il est possible que vous soit retourné l’état Fermé , même si en fait la connexion de magasin sous-jacente est Ouverte.

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

Séparément, si vous ouvrez la connexion de base de données en appelant Database.Connection.Open() elle sera ouverte jusqu’à la prochaine exécution d’une requête ou jusqu’à ce que vous appeliez tout ce qui nécessite une connexion de base de données (par exemple, SaveChanges()), mais après cela, la connexion de magasin sous-jacente sera fermée. Le contexte rouvrira et fermera la connexion à tout moment où une autre opération de base de données est requise :

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

Comportement dans EF6 et les versions futures

Pour EF6 et les versions futures, nous avons fait en sorte que si le code appelant choisit d’ouvrir la connexion en appelant context.Database.Connection.Open() , il a alors une bonne raison de le faire et l’infrastructure supposera que le contrôle est privilégié plutôt que l’ouverture et la fermeture de la connexion, et ne fermera plus automatiquement la connexion.

Remarque

Cela peut entraîner des connexions ouvertes pendant une longue période, donc à utiliser avec précaution.

Nous avons également mis à jour le code afin que ObjectContext.Connection.State effectue désormais le suivi de l’état de la connexion sous-jacente correctement.

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