65 votes

Le thread DbContext est-il sûr?

Je me demandais si la classe DbContext est thread-safe, je suppose que ce n'est pas le cas, car j'exécute actuellement des threads parallèles qui accèdent au DbContext dans mon application et j'obtiens une multitude de les exceptions de verrouillage et d'autres choses qui semblent être liées au thread.

Jusqu'à récemment, je n'obtenais aucune erreur ... mais jusqu'à récemment, je n'accédais pas au DbContext dans les threads.

Si j'ai raison, que proposeraient les gens comme solution?

71voto

DanielB Points 12646

Ce n'est pas sûr pour les threads. Créez simplement une nouvelle instance de DbContext dans votre thread.

27voto

Ladislav Mrnka Points 218632

Non, il n'est pas sûr pour les threads - EF complet n'est pas sûr pour les threads car le contexte EF ne doit jamais être partagé.

16voto

Chris Moschini Points 7278

Édité vieux de réponse ci-dessous.

J'ai maintenant toujours utiliser ce modèle avec DbContext:

using(var db = new LogDbContext())
{
    // Perform work then get rid of the thing
}

Mon approche de l'un par Thread de Demande signifiait objets mis en cache dans le DbContext serait de rester et de devenir obsolètes, même si d'autres DbContext cas de l'écriture de nouvelles valeurs à la base de données derrière. Cela permettrait de créer de drôles de questions, par exemple, d'une demande d'exécution d'une insertion et de la demande suivante de la liste à venir sur un autre thread qui avait un mis en cache, rassis liste des données pour cette requête.

Il existe des approches qui font de la ci-dessous le travail et même d'améliorer les performances de nombreuses lectures/quelques-écrit style apps, mais ils prennent plus de la conception et de la stratégie que de la beaucoup plus simple schéma ci-dessus.

Mise à jour

J'utilise aussi de l'utilité de la méthode d'assistance pour la bibliothèque de méthodes, comme la journalisation des appels. Voici la méthode d'assistance:

    public static void Using(Db db, Action<Db> action)
    {
        if (db == null)
        {
            using (db = new Db())
            {
                action(db);
            }
        }
        else
        {
            action(db);
        }
    }

Avec cela, je peux facilement écrire du code qui prend une option existante DbContext, ou instancie un à l'intérieur d'une utilisation d'un contexte, en fonction de la façon dont il arrive à être appelé.

Par exemple, tout en travaillant avec un DbContext je pourrais charger des données, le journal des infos, et ensuite enregistrer ces données, il est préférable de faire tout cela avec le même DbContext à partir d'un point de vue performances. Sur l'autre main je pourrais aussi voulez enregistrer quelque chose en réponse à une action simple, et de ni la charge ni écrire toutes les autres données. En tirant parti de la méthode ci-dessus, je peux avoir juste une méthode de journalisation qui fonctionne si vous souhaitez travailler à l'intérieur d'un DbContext ou pas:

public void WriteLine(string line, Db _db = null)
{
    Db.Using(_db, db => {
        db.LogLines.Add(new LogLine(line));
        db.SaveChanges();
    });
}

Maintenant l'appel de cette méthode peut être appelée à l'intérieur ou à l'extérieur d'un DbContext et de toujours se comporter de la bonne façon, au lieu d'avoir 2 avoir 2 versions de ce et tout autre commodité méthode de journalisation ou autre utilitaire méthode que j'ai, et au lieu d'avoir à connaître et à planifier le cadre de chaque appel qui ne sera jamais fait pour eux ou leurs appelants. En fait cela me renvoie l'un des avantages de la ci-dessous threadstatic stratégie où je n'ai pas à vous soucier de quand exactement la db a ouvert l'utilitaire d'appels qui devraient être inquiet à ce sujet.

Vieille réponse

J'ai l'habitude de gérer des threads de sécurité avec EF DbContext comme suit:

public class LogDbContext : DbContext
{
    . . .

    [ThreadStatic]
    protected static LogDbContext current;

    public static LogDbContext Current()
    {
        if (current == null)
            current = new LogDbContext();

        return current;
    }

    . . .
}

Avec cela en place, je peux obtenir un DbContext pour ce fil comme suit:

var db = LogDbContext.Current();

Il est important de remarquer que, puisque chaque DbContext conserve sa propre cache local, chaque thread va maintenant avoir son propre cache d'objets de l'entité, qui peut introduire quelques fous comportement si vous n'êtes pas prêt pour cela. Cependant, la création de nouveaux DbContext les objets peuvent être coûteux, et cette approche minimise ce coût.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X