web-dev-qa-db-fra.com

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

EF est nouveau pour moi et j'essaie d'utiliser une méthode d'extension qui convertit mon type de base de données User en 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 

L'opération ne peut pas être terminée car le 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 non plus pourquoi le résultat de l'instruction where n'est pas enregistré dans l'objet users?

Je suppose donc que ma principale question est de savoir pourquoi cela ne fonctionne pas et, en second lieu, quelle est la bonne façon d'utiliser les méthodes d'extension et EF?

32
Colin

Ceci question/réponse m'amène à croire que IQueryable nécessite un contexte actif pour son fonctionnement. Cela signifie que vous devriez plutôt essayer ceci:

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)
{
    ...
}
31
JofryHS

Les objets exposés en tant que IQueryable<T> et IEnumerable<T> ne s'exécutent pas réellement tant qu'ils ne sont pas itérés ou consultés, par exemple s'ils sont composés dans un List<T>. Lorsque EF renvoie un IQueryable<T>, il ne s’agit en réalité que de composer quelque chose capable de récupérer des données. Il n’effectue en réalité la récupération que lorsque vous la consommez.

Vous pouvez avoir une idée de cela en mettant un point d'arrêt où la IQueryable est définie, par opposition au moment où la .ToList() est appelée. (Depuis l'intérieur de l'étendue du contexte de données, comme Jofry l'a correctement souligné.) Le travail d'extraction des données est effectué pendant l'appel ToList().

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

24
Steve Py

N'oubliez pas que les requêtes IQueryable ne sont pas exécutées sur le magasin de données tant que vous ne les avez pas énumérées. 

using (var dataContext = new dataContext())
{

Cette ligne de code ne fait réellement 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 IQueryable, ainsi le code SQL est envoyé à la source de données (via dataContext), puis les opérations .Any () sont exécutées sur celle-ci.

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

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

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

appelle .ToList (), qui énumère IQueryable, ce qui entraîne l'envoi du code SQL à la source de données via le dataContext utilisé dans la requête LINQ d'origine. Puisque ce dataContext a été supprimé, il n'est plus valide et .ToList () lève une exception.

C'est la "pourquoi ça ne marche pas". Le correctif consiste à 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 correctes qui dépendent probablement de votre application (Forms vs. ASP.net vs. MVC, etc.). Le modèle que cela implémente est le modèle d'unité de travail. La création d'un nouvel objet de contexte n'entraîne pratiquement aucun coût. La règle générale consiste donc à en créer un, à effectuer votre travail, puis à le supprimer. Dans les applications Web, certaines personnes créeront un contexte par demande. 

13
Joe Enzminger

La raison pour laquelle l'erreur est générée est que l'objet est supprimé et que, après cela, nous essayons d'accéder aux valeurs de la table par l'intermédiaire de l'objet, mais l'objet est disposé.

Peut-être que les données ne sont pas récupérées tant que vous ne les utilisez pas (le chargement est paresseux), de sorte que dataContext n'existe pas lorsque vous essayez d'effectuer le travail. Je parie que si vous utilisiez la liste ToList (), ce serait correct.

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
JBrooks

Ici, vous essayez d'exécuter un objet IQueryable sur un DBContext inactif. votre DBcontext est déjà éliminé. vous pouvez uniquement exécuter un objet IQueryable avant la suppression de DBContext. Cela signifie que vous devez écrire l'instruction users.Select(x => x.ToInfo()).ToList() à l'intérieur de scope

1
Niraj Trivedi

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 suppression du 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 () à 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
Grant Cermak

Change ça:

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

 }

pour ça:

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

En résumé, vous voulez forcer l'énumération du jeu de données contextuelles une seule fois. Laissez l'appelant gérer le scénario d'ensemble vide, comme il se doit.

1
ComeIn
using(var database=new DatabaseEntities()){}

N'utilisez pas d'affirmation. Il suffit d'écrire comme ça

DatabaseEntities database=new DatabaseEntities ();{}

Ça va marcher. 

Pour la documentation sur la déclaration using, voir ici .

0
Rajesh Kamble