139 votes

Résoudre l'exception InvalidOperationException "L'instance ObjectContext a été éliminée et ne peut plus être utilisée pour des opérations nécessitant une connexion".

J'essaie d'alimenter un GridView en utilisant Entity Frameworkm mais à chaque fois j'obtiens l'erreur suivante :

Property accessor 'LoanProduct' on object 'COSIS_DAL.MemberLoan'". a généré l'exception suivante : L'instance ObjectContext a été été éliminée et ne peut plus être utilisée pour des opérations nécessitant une connexion".

Mon code est :

public List<MemberLoan> GetAllMembersForLoan(string keyword)
{
    using (CosisEntities db = new CosisEntities())
    {
        IQueryable<MemberLoan> query = db.MemberLoans.OrderByDescending(m => m.LoanDate);
        if (!string.IsNullOrEmpty(keyword))
        {
            keyword = keyword.ToLower();
            query = query.Where(m =>
                  m.LoanProviderCode.Contains(keyword)
                  || m.MemNo.Contains(keyword)
                  || (!string.IsNullOrEmpty(m.LoanProduct.LoanProductName) && m.LoanProduct.LoanProductName.ToLower().Contains(keyword))
                  || m.Membership.MemName.Contains(keyword)
                  || m.GeneralMasterInformation.Description.Contains(keyword)

                  );
        }
        return query.ToList();
    }
}

protected void btnSearch_Click(object sender, ImageClickEventArgs e)
{
    string keyword = txtKeyword.Text.ToLower();
    LoanController c = new LoanController();
    List<COSIS_DAL.MemberLoan> list = new List<COSIS_DAL.MemberLoan>();
    list = c.GetAllMembersForLoan(keyword);

    if (list.Count <= 0)
    {
        lblMsg.Text = "No Records Found";
        GridView1.DataSourceID = null;
        GridView1.DataSource = null;
        GridView1.DataBind();
    }
    else
    {
        lblMsg.Text = "";
        GridView1.DataSourceID = null;   
        GridView1.DataSource = list;
        GridView1.DataBind();
    }
}

L'erreur mentionne le LoanProductName de la colonne Gridview . Mentionné : J'utilise C#, ASP.net, SQL-Server 2008 comme base de données dorsale.

Je suis assez novice en matière d'Entity Framework. Je n'arrive pas à comprendre pourquoi j'obtiens cette erreur. Quelqu'un peut-il m'aider ?

195voto

Sergey Berezovskiy Points 102044

Par défaut, Entity Framework utilise le chargement paresseux pour les propriétés de navigation. C'est pourquoi ces propriétés doivent être marquées comme virtuelles - EF crée une classe proxy pour votre entité et surcharge les propriétés de navigation pour permettre le lazy-loading. Par exemple, si vous avez cette entité :

public class MemberLoan
{
   public string LoandProviderCode { get; set; }
   public virtual Membership Membership { get; set; }
}

Entity Framework retournera le proxy hérité de cette entité et fournira l'instance de DbContext à ce proxy afin de permettre le chargement paresseux de l'adhésion plus tard :

public class MemberLoanProxy : MemberLoan
{
    private CosisEntities db;
    private int membershipId;
    private Membership membership;

    public override Membership Membership 
    { 
       get 
       {
          if (membership == null)
              membership = db.Memberships.Find(membershipId);
          return membership;
       }
       set { membership = value; }
    }
}

Ainsi, l'entité possède une instance de DbContext qui a été utilisée pour charger l'entité. C'est votre problème. Vous avez using bloc autour de l'utilisation de CosisEntities. Ce qui élimine le contexte avant que les entités ne soient retournées. Lorsque, plus tard, un code tente d'utiliser la propriété de navigation chargée paresseusement, il échoue, car le contexte est éliminé à ce moment-là.

Pour corriger ce comportement, vous pouvez utiliser le chargement rapide des propriétés de navigation dont vous aurez besoin plus tard :

IQueryable<MemberLoan> query = db.MemberLoans.Include(m => m.Membership);

Cela permettra de précharger toutes les adhésions et le chargement paresseux ne sera pas utilisé. Pour plus de détails, voir Chargement des entités connexes article sur MSDN.

32voto

Tragedian Points 12308

El CosisEntities est votre DbContext . Lorsque vous créez un contexte dans un using vous définissez les limites de votre opération orientée données.

Dans votre code, vous essayez d'émettre le résultat d'une requête à partir d'une méthode, puis de terminer le contexte dans la méthode. L'opération à laquelle vous passez le résultat tente ensuite d'accéder aux entités afin de remplir la vue de la grille. Quelque part dans le processus de liaison à la grille, on accède à une propriété chargée paresseusement et Entity Framework essaie d'effectuer une recherche pour obtenir les valeurs. Il échoue, car le contexte associé est déjà terminé.

Vous avez deux problèmes :

  1. Vous chargez les entités paresseusement lorsque vous vous liez à la grille. Cela signifie que vous effectuez un grand nombre d'opérations de requête distinctes sur le serveur SQL, ce qui va tout ralentir. Vous pouvez résoudre ce problème en faisant en sorte que les propriétés liées soient eager-loaded par défaut, ou en demandant à Entity Framework de les inclure dans les résultats de cette requête en utilisant l'attribut Include méthode d'extension.

  2. Vous mettez fin prématurément à votre contexte : un DbContext doit être disponible tout au long de l'unité de travail en cours d'exécution, et ne doit être éliminé que lorsque vous avez terminé le travail en cours. Dans le cas d'ASP.NET, une unité de travail est généralement la requête HTTP traitée.

29voto

Erik Philips Points 18156

Ligne de fond

Votre code a récupéré des données (entités) via entity-framework avec lazy-loading activé et après que le DbContext ait été disposé, votre code fait référence à des propriétés (entités liées/relations/navigation) qui n'ont pas été explicitement demandées.

Plus précisément

El InvalidOperationException avec ce message signifie toujours la même chose : vous demandez des données (entités) à entity-framework après que le DbContext ait été éliminé.

Un cas simple :

(ces classes seront utilisées pour tous les exemples de cette réponse, et supposent que toutes les propriétés de navigation ont été configurées correctement et ont des tables associées dans la base de données)

public class Person
{
  public int Id { get; set; }
  public string name { get; set; }
  public int? PetId { get; set; }
  public Pet Pet { get; set; }
}

public class Pet 
{
  public string name { get; set; }
}

using (var db = new dbContext())
{
  var person = db.Persons.FirstOrDefaultAsync(p => p.id == 1);
}

Console.WriteLine(person.Pet.Name);

La dernière ligne lancera le InvalidOperationException parce que le dbContext n'a pas désactivé le chargement paresseux et que le code accède à la propriété de navigation Pet après que le Context a été éliminé par l'instruction using.

Débogage

Comment trouver la source de cette exception ? Outre l'examen de l'exception elle-même, qui sera levée exactement à l'endroit où elle se produit, les règles générales de débogage dans Visual Studio s'appliquent : placez des points d'arrêt stratégiques et des points de contrôle. contrôlez vos variables soit en passant la souris sur leur nom, en ouvrant une fenêtre (Quick)Watch ou en utilisant les différents panneaux de débogage comme Locals et Autos.

Si vous voulez savoir où se trouve ou ne se trouve pas la référence, faites un clic droit sur son nom et sélectionnez "Rechercher toutes les références". Vous pouvez alors placer un point d'arrêt à chaque endroit qui demande des données, et exécuter votre programme avec le débogueur attaché. Chaque fois que le débogueur s'arrête sur un tel point d'arrêt, vous devez déterminer si votre propriété de navigation aurait dû être remplie ou si les données demandées sont nécessaires.

Les moyens d'éviter

Désactiver le Lazy-Loading

public class MyDbContext : DbContext
{
  public MyDbContext()
  {
    this.Configuration.LazyLoadingEnabled = false;
  }
}

Pour : Au lieu de lancer l'exception InvalidOperationException, la propriété sera nulle. Accéder aux propriétés de null ou essayer de changer les propriétés de cette propriété lèvera une NullReferenceException .

Comment demander explicitement l'objet en cas de besoin :

using (var db = new dbContext())
{
  var person = db.Persons
    .Include(p => p.Pet)
    .FirstOrDefaultAsync(p => p.id == 1);
}
Console.WriteLine(person.Pet.Name);  // No Exception Thrown

Dans l'exemple précédent, Entity Framework matérialisera l'animal de compagnie en plus de la personne. Cela peut être avantageux car il s'agit d'un appel unique à la base de données. (Cependant, il peut aussi y avoir d'énormes problèmes de performance en fonction du nombre de résultats retournés et du nombre de propriétés de navigation demandées, dans ce cas, il n'y aurait pas de pénalité de performance car les deux instances ne sont qu'un seul enregistrement et une seule jointure).

o

using (var db = new dbContext())
{
  var person = db.Persons.FirstOrDefaultAsync(p => p.id == 1);

  var pet = db.Pets.FirstOrDefaultAsync(p => p.id == person.PetId);
}
Console.WriteLine(person.Pet.Name);  // No Exception Thrown

Dans l'exemple précédent, Entity Framework matérialisera l'animal de compagnie indépendamment de la personne en effectuant un appel supplémentaire à la base de données. Par défaut, Entity Framework suit les objets qu'il a récupérés dans la base de données et s'il trouve des propriétés de navigation qui correspondent, il va auto-magique de peupler ces entités. Dans ce cas, parce que le PetId sur le Person correspond à l'objet Pet.Id Entity Framework attribuera l'adresse Person.Pet a la Pet récupéré, avant que la valeur ne soit affectée à la variable pet.

Je recommande toujours cette approche car elle oblige les programmeurs à comprendre quand et comment le code demande des données via Entity Framework. Lorsque le code lève une exception de référence nulle sur une propriété d'une entité, vous pouvez presque toujours être sûr que vous n'avez pas explicitement demandé ces données.

17voto

Ricardo Pontual Points 3145

C'est une réponse très tardive mais j'ai résolu le problème en désactivant le chargement paresseux :

db.Configuration.LazyLoadingEnabled = false;

2voto

John Points 1079

Si vous utilisez ASP.NET Core et que vous vous demandez pourquoi vous obtenez ce message dans l'une de vos méthodes de contrôleur asynchrone, assurez-vous de renvoyer une valeur Task plutôt que void - ASP.NET Core dispose des contextes injectés.

(Je publie cette réponse car cette question figure en bonne place dans les résultats de recherche de ce message d'exception et il s'agit d'un problème subtil - peut-être sera-t-il utile aux personnes qui le rechercheront sur Google).

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