2012-11-29 13 views
28

Sono nuovo di EF e sto cercando di utilizzare un metodo di estensione che converte dal mio tipo di database User alla mia classe di informazioni UserInfo.
Sto usando il database per primo se questo fa la differenza?L'operazione non può essere completata perché DbContext è stato eliminato errore

mio codice qui sotto dà l'errore

L'operazione non può essere completata perché il DbContext è stato eliminato.

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

vedo il motivo per cui lo farebbe, ma anche io non capisco perché il risultato della cui dichiarazione è non essere salvato nell'oggetto users?

Quindi penso che la mia domanda principale è perché non funziona e in secondo luogo qual è il modo giusto di utilizzare i metodi di estensione e EF?

risposta

29

Questo question & answer mi porta a credere che IQueryable richieda un contesto attivo per il suo funzionamento. Ciò significa che si dovrebbe provare questo invece:

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) 
{ 
    ... 
} 
+1

Grazie. Ora posso vedere che IQueryable è dove mi trovo nei guai. Se metto gli utenti.Seleziona() ... riga nell'istruzione using mi dà un altro errore (domanda diversa credo). Così ho cambiato il tipo di ritorno in IEnumerable e questo ha risolto il mio problema. – Colin

+0

Grazie per aver lavorato per me e ora sono venuto a sapere che IQueryable richiesto DBContext attivo –

2

Il motivo per cui sta gettando l'errore è l'oggetto è disposto e dopo che stiamo cercando di accedere ai valori della tabella attraverso l'oggetto, ma è oggetto di disposed.Better convertirlo in ToList() in modo che possiamo avere valori

Forse in realtà non sta ricevendo i dati finché non li usi (è un caricamento lento), quindi dataContext non esiste quando stai provando a fare il lavoro . Scommetto che se hai fatto il ToList() in ambito sarebbe 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) 
{ 
    ... 
} 
22

oggetti esposti come IQueryable<T> e IEnumerable<T> realtà non "eseguono" finché non vengono iterate su o altrimenti accessibili, come essere composto in un List<T>. Quando EF restituisce un valore IQueryable<T>, si tratta essenzialmente di comporre qualcosa in grado di recuperare i dati, in realtà non sta eseguendo il recupero finché non lo si consuma.

È possibile ottenere una sensazione per questo mettendo un punto di interruzione in cui è definito il IQueryable, rispetto a quando viene chiamato il .ToList(). (Dall'interno del contesto dei dati come Jofry ha correttamente sottolineato.) Il lavoro per estrarre i dati viene eseguito durante la chiamata ToList().

Per questo motivo, è necessario mantenere IQueryable<T> nell'ambito del contesto dati.

12

È necessario ricordare che le query IQueryable non vengono effettivamente eseguite sull'archivio dati finché non vengono enumerate.

using (var dataContext = new dataContext()) 
{ 

Questa riga di codice in realtà non fa altro che creare l'istruzione SQL

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

.Any() è un'operazione che enumera l'IQueryable, in modo che lo SQL viene inviato ai dati fonte (attraverso dataContext), e poi il.Tutte le operazioni() viene eseguito contro di essa

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

La linea "problema" è riutilizzare la sql costruita sopra, e poi facendo un'operazione aggiuntiva (.Select()), che aggiunge solo alla query. Se hai lasciato qui, non fa eccezione, tranne la vostra linea problema

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

chiama ToList(), che elenca l'IQueryable, che fa sì che il codice SQL da inviare all'origine dati attraverso il DataContext che è stato utilizzato in originale Query LINQ. Poiché questo dataContext è stato eliminato, non è più valido e .ToList() genera un'eccezione.

Questo è il "motivo per cui non funziona". La soluzione è spostare questa linea di codice all'interno dell'ambito del tuo dataContext.

Come usarlo correttamente è un'altra domanda con alcune risposte probabilmente corrette che dipendono dall'applicazione (Forms vs ASP.net vs MVC, ecc.). Lo schema che implementa è il modello dell'unità di lavoro. Non c'è quasi alcun costo per creare un nuovo oggetto di contesto, quindi la regola generale è crearne uno, fare il tuo lavoro e poi eliminarlo. Nelle app Web, alcune persone creeranno un contesto per richiesta.

+0

SingleOrDefault() causa anche problemi simili – code4j

0

Questo può essere semplice come aggiungere ToList() nel repository. Per esempio:

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

produrrà contesto Db errore smaltiti nella classe chiamata, ma questo può essere risolto con l'esercizio in modo esplicito l'enumerazione aggiungendo ToList() sul funzionamento LINQ:

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

Modificare questa :

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

} 

a questo:

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

L'essenza è che si desidera forzare l'enumerazione del set di dati del contesto solo una volta. Lascia che il chiamante si occupi dello scenario set vuoto, come dovrebbero.

1

Qui si sta tentando di eseguire l'oggetto IQueryable su DBContext inattivo. il tuo DBcontext è già eliminato. è possibile eseguire solo oggetti IQueryable prima che DBContext venga eliminato. Significa che è necessario scrivere l'istruzione users.Select(x => x.ToInfo()).ToList() all'interno dell'ambito

Problemi correlati