L'opération ne peut pas être terminée car L'erreur DbContext a été éliminée

Je suis nouveau sur EF et j'essaie d'utiliser une méthode d'extension qui convertit de mon type de base de données User à ma classe info UserInfo.
j'utilise d'abord la base de données si cela fait une différence?

Mon code ci-dessous donne l'erreur

Impossible de terminer l'opération car DbContext a été supprimé.

try
{
    IQueryable<User> users;
    using (var dataContext = new dataContext())
    {
        users = dataContext.Users
                  .Where(x => x.AccountID == accountId && x.IsAdmin == false);
        if(users.Any() == false)
        {
            return null;
        }
    }
    return users.Select(x => x.ToInfo()).ToList(); // this line is the problem
}
catch (Exception ex)
{
    //...
}

, je peux voir pourquoi il le ferait, mais je ne comprends pas pourquoi le résultat de l'instruction where n'est pas enregistré dans l' users l'objet?

Donc, je suppose que ma question principale est pourquoi cela ne fonctionne pas et deuxièmement Quelle est la bonne façon d'utiliser les méthodes d'extension et EF?

30
demandé sur Hakam Fostok 2012-11-29 06:03:43

7 réponses

Cette question & Réponse me conduit à croire que IQueryable nécessite un contexte actif pour son fonctionnement. Cela signifie que vous devriez essayer ceci à la place:

try
{
    IQueryable<User> users;

    using (var dataContext = new dataContext())
    {
        users = dataContext.Users.Where(x => x.AccountID == accountId && x.IsAdmin == false);

        if(users.Any() == false)
        {
            return null;
        }
        else
        {
            return users.Select(x => x.ToInfo()).ToList(); // this line is the problem
        }
    }


}
catch (Exception ex)
{
    ...
}
30
répondu JofryHS 2017-05-23 12:10:18

Les objets exposés en tant que IQueryable<T> et IEnumerable<T> ne "s'exécutent" pas tant qu'ils ne sont pas itérés ou autrement accessibles, comme être composés en List<T>. Lorsque EF renvoie un IQueryable<T>, Il ne fait que composer quelque chose capable de récupérer des données, il n'effectue pas réellement la récupération jusqu'à ce que vous la consommiez.

Vous pouvez obtenir une idée de cela en mettant un point d'arrêt où le IQueryable est défini, par rapport à quand le .ToList() est appelé. (De L'intérieur de la portée du contexte de données comme Jofry a correctement souligné.) Le travail d'extraction des données est effectué pendant l'appel ToList().

Pour cette raison, vous devez garder le IQueryable<T> dans la portée du contexte de données.

24
répondu Steve Py 2012-11-29 02:26:04

Vous devez vous rappeler que les requêtes IQueryable ne sont pas réellement exécutées sur le magasin de données jusqu'à ce que vous les énumériez.

using (var dataContext = new dataContext())
{

Cette ligne de code ne fait rien d'autre que de construire L'instruction SQL

    users = dataContext.Users.Where(x => x.AccountID == accountId && x.IsAdmin == false);

.Any () est une opération qui énumère le IQueryable, de sorte que le SQL est envoyé à la source de données (via dataContext), puis le .Toutes les opérations () sont exécutées contre elle

    if(users.Any() == false)
    {
        return null;
    }
}

Votre ligne "problème" réutilise le sql construit ci-dessus, puis faire une opération supplémentaire (.Select ()), qui ajoute simplement à la requête. Si vous l'avez laissé ici, aucune exception, sauf votre ligne de problème

return users.Select(x => x.ToInfo()).ToList(); // this line is the problem

Appels .ToList (), qui énumère IQueryable, ce qui provoque L'envoi du SQL à la source de données via le dataContext utilisé dans la requête LINQ d'origine. Depuis ce dataContext a été éliminé, il n'est plus valide, et .ToList() lève une exception.

C'est le "pourquoi ça ne marche pas". La solution est de déplacer cette ligne de code dans la portée de votre dataContext.

Comment l'utiliser correctement est une autre question avec quelques réponses sans doute correctes qui dépendent de votre application (formulaires vs. ASP.net vs. MVC, etc.). Le modèle que cela implémente est l'Unité de modèle de travail. Il n'y a presque aucun coût à créer un nouvel objet context, donc la règle générale est d'en créer un, de faire votre travail, puis d'en disposer. Dans les applications web, certaines personnes créeront un contexte par requête.

13
répondu Joe Enzminger 2012-11-29 02:40:26

La raison pour laquelle il lance l'erreur est que l'objet est éliminé et après cela, nous essayons d'accéder aux valeurs de la table à travers l'objet, mais l'objet est éliminé.Mieux vaut convertir cela en ToList() afin que nous puissions avoir des valeurs

Peut-être qu'il n'obtient pas réellement les données avant de les utiliser (c'est un chargement paresseux), donc dataContext n'existe pas lorsque vous essayez de faire le travail. Je parie que si vous avez fait le ToList() dans la portée, ce serait ok.

try
{
    IQueryable<User> users;
    var ret = null;

    using (var dataContext = new dataContext())
    {
        users = dataContext.Users.Where(x => x.AccountID == accountId && x.IsAdmin == false);

        if(users.Any())
        {
            ret = users.Select(x => x.ToInfo()).ToList(); 
        }

     }

   Return ret;
}
catch (Exception ex)
{
    ...
}
2
répondu JBrooks 2015-05-07 13:28:16

Cela peut être aussi simple que D'ajouter ToList() dans votre référentiel. Par exemple:

public IEnumerable<MyObject> GetMyObjectsForId(string id)
{
    using (var ctxt = new RcContext())
    {
        // causes an error
        return ctxt.MyObjects.Where(x => x.MyObjects.Id == id);
    }
}

Donnera l'erreur de contexte de base de données dans la classe appelante, mais cela peut être résolu en exerçant explicitement l'énumération en ajoutant ToList () sur L'opération LINQ:

public IEnumerable<MyObject> GetMyObjectsForId(string id)
{
    using (var ctxt = new RcContext())
    {
        return ctxt.MyObjects.Where(x => x.MyObjects.Id == id).ToList();
    }
}
1
répondu Grant Cermak 2014-11-18 16:07:28

Changez ceci:

using (var dataContext = new dataContext())
{
    users = dataContext.Users.Where(x => x.AccountID == accountId && x.IsAdmin == false);

    if(users.Any())
    {
        ret = users.Select(x => x.ToInfo()).ToList(); 
    }

 }

À ceci:

using (var dataContext = new dataContext())
{
    return = dataContext.Users.Where(x => x.AccountID == accountId && x.IsAdmin == false).Select(x => x.ToInfo()).ToList();
} 

L'essentiel est que vous ne voulez forcer l'énumération du contexte dataset une fois. Laissez l'appelant traiter avec un scénario de jeu vide, comme il se doit.

1
répondu ComeIn 2016-04-25 10:23:11

Ici, vous essayez D'exécuter l'objet IQueryable sur DBContext inactif. votre DBcontext est déjà éliminé. vous pouvez uniquement exécuter l'objet IQueryable avant que DBContext ne soit éliminé. Signifie que vous devez écrire users.Select(x => x.ToInfo()).ToList() instruction à l'intérieur en utilisant scope

1
répondu Niraj Trivedi 2017-08-09 00:00:14