Risoluzione “L’istanza di ObjectContext è stata eliminata e non può più essere utilizzata per operazioni che richiedono una connessione” InvalidOperationException

Sto cercando di popolare un GridView usando Entity Frameworkm ma ogni volta sto ricevendo il seguente errore:

“L’accesso proprietà” LoanProduct “sull’object” COSIS_DAL.MemberLoan “ha generato la seguente eccezione: L’istanza ObjectContext è stata eliminata e non può più essere utilizzata per operazioni che richiedono una connessione.”

Il mio codice è:

  public List GetAllMembersForLoan(string keyword) { using (CosisEntities db = new CosisEntities()) { IQueryable 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 list = new List(); 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’errore menziona la colonna LoanProductName di LoanProductName . Citato: sto usando C #, ASP.net, SQL-Server 2008 come back-end DB.

Sono abbastanza nuovo per Entity Framework. Non riesco a capire perché sto ottenendo questo errore. Qualcuno mi può aiutare per favore?

Per impostazione predefinita, Entity Framework utilizza il caricamento lazy per le proprietà di navigazione. Ecco perché queste proprietà devono essere contrassegnate come virtuali: EF crea una class proxy per l’ quadro e sostituisce le proprietà di navigazione per consentire il caricamento lento. Ad esempio se hai questa quadro:

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

Entity Framework restituirà il proxy ereditato da questa quadro e fornirà l’istanza di DbContext a questo proxy per consentire il caricamento lazy dell’iscrizione in un secondo momento:

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

Quindi, l’entity framework ha istanza di DbContext che è stata utilizzata per caricare l’entity framework. Questo è il tuo problema. Si sta using blocco attorno all’utilizzo di CosisEntities. Che dispone del contesto prima che le entity framework vengano restituite. Quando in seguito un codice tenta di utilizzare la proprietà di navigazione caricata a livello pigro, fallisce, poiché il contesto è disposto in quel momento.

Per correggere questo comportamento è ansible utilizzare il caricamento ansioso delle proprietà di navigazione di cui avrete bisogno in seguito:

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

Ciò precaricherà tutte le appartenenze e il caricamento lento non verrà utilizzato. Per i dettagli, consultare l’articolo Caricamento di quadro correlate su MSDN.

La class CosisEntities è il tuo DbContext . Quando si crea un contesto in un blocco using , si definiscono i limiti per l’operazione orientata ai dati.

Nel codice, si sta tentando di emettere il risultato di una query da un metodo e quindi terminare il contesto all’interno del metodo. L’operazione si passa il risultato per poi tenta di accedere alle quadro al fine di popolare la visualizzazione della griglia. Da qualche parte nel processo di associazione alla griglia, si accede a una proprietà a carico lento e Entity Framework sta tentando di eseguire una ricerca per ottenere i valori. Non riesce, perché il contesto associato è già terminato.

Hai due problemi:

  1. Sei quadro pigri quando ti leghi alla griglia. Ciò significa che stai eseguendo molte operazioni di query separate su SQL Server, che rallenteranno tutto. È ansible risolvere questo problema rendendo le proprietà correlate caricate di frequente per impostazione predefinita oppure chiedendo ad Entity Framework di includerle nei risultati di questa query utilizzando il metodo di estensione Include .

  2. Stai terminando prematuramente il tuo contesto: un DbContext dovrebbe essere disponibile per tutta l’unità di lavoro che viene eseguita, disponendola solo quando hai finito il lavoro da svolgere. Nel caso di ASP.NET, un’unità di lavoro è in genere la richiesta HTTP gestita.

Linea di fondo

Il codice ha recuperato i dati (entity framework) tramite entity-framework con lazy-loading abilitato e dopo che DbContext è stato eliminato, il codice fa riferimento a proprietà (relative / relazione / quadro di navigazione) che non sono state esplicitamente richieste.

Più specificamente

L’ InvalidOperationException con questo messaggio indica sempre la stessa cosa: si richiedono dati ( quadro) dal framework entity framework dopo che DbContext è stato eliminato.

Un caso semplice:

(queste classi saranno utilizzate per tutti gli esempi in questa risposta e assumeranno che tutte le proprietà di navigazione siano state configurate correttamente e abbiano tabelle associate nel database)

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

L’ultima riga genererà InvalidOperationException perché dbContext non ha disabilitato il caricamento lento e il codice accede alla proprietà di navigazione di Pet dopo che il contesto è stato eliminato dall’istruzione using.

Debug

Come trovi la fonte di questa eccezione? Oltre a guardare l’eccezione stessa, che verrà lanciata esattamente nel punto in cui si verifica, si applicano le regole generali di debugging in Visual Studio: posiziona i breakpoints strategici e controlla le tue variabili , passando il mouse sui loro nomi, aprendo un ( Rapido) Guarda la finestra o utilizza i vari pannelli di debug come Locals e Auto.

Se vuoi scoprire dove il riferimento è o non è impostato, fai clic con il tasto destro sul suo nome e seleziona “Trova tutti i riferimenti”. È quindi ansible inserire un punto di interruzione in ogni posizione che richiede dati ed eseguire il programma con il debugger allegato. Ogni volta che il debugger si interrompe su un punto di interruzione, è necessario determinare se la proprietà di navigazione deve essere stata popolata o se i dati richiesti sono necessari.

Modi da evitare

Disabilita caricamento lento

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

Vantaggi: anziché lanciare InvalidOperationException, la proprietà sarà nullo. L’accesso alle proprietà di null o il tentativo di modificare le proprietà di questa proprietà generano un’eccezione NullReferenceException .

Come richiedere esplicitamente l’object quando necessario:

 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 

Nell’esempio precedente, Entity Framework materializzerà l’animale domestico oltre alla persona. Questo può essere vantaggioso perché è una singola chiamata al database. (Tuttavia, possono esserci anche enormi problemi di prestazioni a seconda del numero di risultati restituiti e del numero di proprietà di navigazione richieste, in questo caso non ci sarebbero penalizzazioni delle prestazioni poiché entrambe le istanze sono solo un singolo record e un singolo join).

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 

Nell’esempio precedente, Entity Framework materializzerà il Pet indipendentemente dalla Persona effettuando una chiamata aggiuntiva al database. Per impostazione predefinita, Entity Framework tiene traccia degli oggetti che ha recuperato dal database e, se trova proprietà di navigazione che lo corrispondono, popolerà automaticamente queste quadro. In questo caso, poiché PetId sull’object Person corrisponde a Pet.Id , Entity Framework assegnerà il Person.Pet al valore Pet recuperato, prima che il valore venga assegnato alla variabile pet.

Raccomando sempre questo approccio in quanto costringe i programmatori a capire quando e come il codice è i dati delle richieste tramite Entity Framework. Quando il codice genera un’eccezione di riferimento null su una proprietà di un’ quadro, puoi quasi sempre essere sicuro di non aver richiesto esplicitamente tali dati.

È una risposta molto tarda, ma ho risolto il problema distriggersndo il caricamento lento:

 db.Configuration.LazyLoadingEnabled = false; 

Nel mio caso, stavo passando per tutti gli “Utenti” dei modelli alla colonna e non era mappato correttamente, quindi ho appena passato “Users.Name” e l’ho risolto.

 var data = db.ApplicationTranceLogs .Include(q=>q.Users) .Include(q => q.LookupItems) .Select(q => new { Id = q.Id, FormatDate = q.Date.ToString("yyyy/MM/dd"), ***Users = q.Users,*** ProcessType = q.ProcessType, CoreProcessId = q.CoreProcessId, Data = q.Data }) .ToList(); var data = db.ApplicationTranceLogs .Include(q=>q.Users).Include(q => q.LookupItems) .Select(q => new { Id = q.Id, FormatDate = q.Date.ToString("yyyy/MM/dd"), ***Users = q.Users.Name***, ProcessType = q.ProcessType, CoreProcessId = q.CoreProcessId, Data = q.Data }) .ToList(); 

La maggior parte delle altre risposte indica un caricamento impaziente, ma ho trovato un’altra soluzione.

Nel mio caso ho avuto un object EF InventoryItem con una collezione di oggetti figlio InvActivity .

 class InventoryItem { ... // EF code first declaration of a cross table relationship public virtual List ItemsActivity { get; set; } public GetLatestActivity() { return ItemActivity?.OrderByDescending(x => x.DateEntered).SingleOrDefault(); } ... } 

E poiché stavo prelevando dalla raccolta oggetti figlio anziché da una query di contesto (con IQueryable ), la funzione Include() non era disponibile per implementare il caricamento ansioso. Quindi la mia soluzione era di creare un contesto da cui utilizza GetLatestActivity() e attach() l’object restituito:

 using (DBContext ctx = new DBContext()) { var latestAct = _item.GetLatestActivity(); // attach the Entity object back to a usable database context ctx.InventoryActivity.Attach(latestAct); // your code that would make use of the latestAct's lazy loading // ie latestAct.lazyLoadedChild.name = "foo"; } 

Quindi non sei bloccato con il caricamento avido.