Perché è possibile enumerare una query DbLinq dopo aver chiamato Dispose () sul DataContext?
Domanda
Aggiorna - La risposta è apparentemente che DbLinq non implementa Dispose()
correttamente. D'oh!
Il sotto è tutto sorta di fuorviante - Bottom line: DbLinq non è (ancora) equivalente a LinqToSql, come ho assunto quando ho inizialmente fatto questa domanda. Usare con cautela!
Sto utilizzando il Pattern Repository con DbLinq. I miei oggetti di repository implementare IDisposable
, e il metodo Dispose()
fa unica cosa - le chiamate Dispose()
sul DataContext
. Ogni volta che uso un repository, mi avvolgo in un blocco using
, in questo modo:
public IEnumerable<Person> SelectPersons()
{
using (var repository = _repositorySource.GetPersonRepository())
{
return repository.GetAll(); // returns DataContext.Person as an IQueryable<Person>
}
}
Questo metodo restituisce un IEnumerable<Person>
, quindi se la mia comprensione è corretta, non l'interrogazione del database avviene effettivamente fino a quando è attraversato Enumerable<Person>
(ad esempio, convertendolo in un elenco o una matrice o utilizzando in un ciclo foreach
), come in questo esempio:
var persons = gateway.SelectPersons();
// Dispose() is fired here
var personViewModels = (
from b in persons
select new PersonViewModel
{
Id = b.Id,
Name = b.Name,
Age = b.Age,
OrdersCount = b.Order.Count()
}).ToList(); // executes queries
In questo esempio, Dispose()
viene chiamato immediatamente dopo l'impostazione persons
, che è un IEnumerable<Person>
, e questa è l'unica volta che viene chiamato.
Quindi, tre domande:
- Come funziona? Come può un
DataContext
disposto ancora interrogare il database per i risultati dopo laDataContext
è stato eliminato? - Cosa fa
Dispose()
effettivamente fare? - Ho sentito che non è necessario (ad esempio, vedere questa domanda ) di disporre di un
DataContext
, ma la mia impressione è che non è una cattiva idea. C'è qualche ragione non di disporre di unDataContext
DbLinq?
Soluzione
1 Come funziona? Come può un DataContext disposto ancora interrogare il database per i risultati dopo il DataContext è stato eliminato?
non il lavoro. C'è qualcosa che non sta mostrando noi. Sto indovinando che sia la classe repository non smaltire il DataContext
correttamente / al momento giusto, o che si sta scrivendo sommariamente ToList()
alla fine di ogni query, che nega completamente la trasformazione query e l'esecuzione differita si ottiene generalmente.
Prova il seguente codice in un'applicazione di test, I garanzia che si genera un ObjectDisposedException
:
// Bad code; do not use, will throw exception.
IEnumerable<Person> people;
using (var context = new TestDataContext())
{
people = context.Person;
}
foreach (Person p in people)
{
Console.WriteLine(p.ID);
}
Questo è il più semplice caso riproducibili possibile, e sarà sempre buttare. D'altra parte, se si scrive people = context.Person.ToList()
invece, allora i risultati della query sono già state enumerate all'interno il blocco using
, che scommetto è quello che sta succedendo nel vostro caso.
2 Cosa significa Dispose () in realtà fare?
Tra le altre cose, si imposta un flag che indica che il DataContext
è disposto, che viene controllata in ogni query successiva e fa sì che il DataContext
di gettare un ObjectDisposedException
con il messaggio Object name: 'DataContext accessed after Dispose.'.
Si chiude anche il collegamento, se il DataContext
apri e lasciata aperta.
3 Ho sentito dire che non è necessario (ad esempio, vedere questa domanda) di disporre di un DataContext, ma la mia impressione è che non è una cattiva idea. C'è qualche motivo per non disporre di un LinqToSql DataContext?
è necessario Dispose
la DataContext
, in quanto è necessario per Dispose
ogni altro IDisposable
. Si potrebbe potenzialmente perdere connessioni se non si riesce a smaltire il DataContext
. Si potrebbe anche perdere la memoria se una qualsiasi delle entità recuperati dal DataContext
sono tenuti in vita, poiché il contesto mantiene una cache identità interna per il modello di unità di lavoro da essa svolte. Ma anche se tutto questo non fosse il caso, non è la vostra preoccupazione ciò che il metodo Dispose
fa internamente. Si supponga che si fa qualcosa di importante.
IDisposable
è un contratto che dice: "la pulizia non può essere automatico, è necessario disporre di me quando hai finito." Non ci sono garanzie di se l'oggetto ha un proprio finalizzatore che pulisce dopo di voi, se si dimentica di Dispose
. Implementazioni sono soggette a cambiamenti, ed è per questo che non è una buona idea fare affidamento su comportamento osservato in contrasto con le specifiche esplicite.
La cosa peggiore che può accadere se si smaltisce un IDisposable
con un metodo Dispose
vuoto è che si rifiuti di un paio di cicli di CPU. La cosa peggiore che può accadere se si fallire per smaltire un IDisposable
con un implementazione non banali è che si perdita di risorse. La scelta qui è evidente; se vedete un IDisposable
, non dimenticate di smaltire.
Altri suggerimenti
"persone" è una raccolta IEnumerable, il DataContext (repository) è necessaria solo per effettuare la chiamata .GetNew.
lo zucchero da / select / etc parole chiave sono sintattica per i metodi di estensione aggiunti nello spazio dei nomi System.Linq. Questi metodi di estensione aggiungere la funzionalità IEnumerable si utilizza nella query, non il DataContext. In realtà, si può fare tutto questo senza usare LINQ2SQL a tutti, per la creazione di un programatically IEnumerable per dimostrare.
Se si tenta di fare ulteriori repository (DataContext) chiamate utilizzando questi oggetti, che è quando si riceve un errore.
La raccolta IEnumerable conterrà tutti i record dalla repository, questo è il motivo per cui non si richiede il DataContext per rendere la query.
metodi di estensione: http://msdn.microsoft.com/en- us / library / bb383977.aspx
metodi di estensione LINQ: http://msdn.microsoft.com/en-us/ biblioteca / system.linq.enumerable_members.aspx
Nel profondo l'API, probabilmente vedrete un metodo che utilizza un API come questo:
http://msdn.microsoft. com / it-it / library / y6wy5a0f (v = VS.100) aspx
Quando viene eseguito il comando, l'oggetto di connessione viene chiuso quando l'oggetto DataReader associato chiuso.