2013-01-13 10 views
6

Ho NHibernate (con NHibernate.Linq e Fluent NHibernate) impostato con la cache di query. Tutto funziona correttamente finché non eseguo lo session.Save(new Widget()) (ad esempio, SQL INSERT). Dopo questo punto, tutte le query su quel tipo di Widget mancano la cache della query. Le query su altri tipi di entità sono memorizzate nella cache correttamente.NHibernate - Query manca la cache della query dopo aver salvato una nuova entità

using (ISession session = MySessionFactory.OpenSession()) 
{ 
    using (var transaction = session.BeginTransaction()) 
    { 
     // this INSERT screws things up 
     var widget = new Widget {Name = "Foo"}; 
     session.Save(widget); 

     var query = (from w in session.Query<Widget>().Cacheable() 
        where w.Name == "Bar" 
        select w); 

     var fetched1 = query.FirstOrDefault(); 
     var fetched2 = query.FirstOrDefault(); // miss?! 

     transaction.Commit(); 
    } 
} 

Se inizio un nuovo Transaction, il problema persiste. Se avvio un nuovo Session, il problema scompare. Questo mi sembra strano, dal momento che la mia comprensione era che il cache di secondo livello veniva ripristinato per SessionFactory (non Session).

Non penso che questo sia importante, ma sto usando lo HashtableCacheProvider, dato che sto testando proprio adesso.

+0

quale errore si è verificato, signore? – spajce

+0

Non è un errore nel senso che è stata lanciata un'eccezione. Piuttosto, la seconda query ('var fetched2 = query.FirstOrDefault();') salta la cache della query e colpisce direttamente il database (che non è il comportamento previsto). –

+0

Hai abilitato la cache della query? Penso che devi farlo separatamente dalla cache delle entità. –

risposta

6

Il comportamento che hai descritto è corretto (more here).

La cache di timestamp di aggiornamento non viene aggiornata finché non si esegue il commit della transazione ! Questo per garantire che non vengano letti "valori non confermati " dalla cache.

Ogni volta che c'è un cambiamento su un type che abbiamo in Cache - dati memorizzati nella cache sono stantio ... fino a quando la transazione completa si impegna.

Immaginate che avete memorizzato nella cache i risultati di questo filtro:

var query = (from w in session.Query<Widget>().Cacheable() 
    where w.Name == "B*" // all names starting with B 
    select w); 

E più tardi aggiungerà nuovo widget: Se la query sarebbe ancora nella cache, non apparirà mai

var widget = new Widget {Name = "Brigitte"}; 
session.Save(widget); 
// explicit Flush is not needed, 
// but then, until Commit(), query will never return Brigitte 
session.Flush(); // to immediately execute INSERT 

Brigitte ..

E mentre in Transaction, le query con FirstOrDefault() vengono eseguite immediatamente - le operazioni di scrittura possono attendere un Flush on Commit.

A causa della transazione, tutte le operazioni contenute (insert, udpate, select) non possono trarre profitto dalla memorizzazione nella cache, poiché solo la transazione come batch ha senso. Quindi, finché non viene chiamato commit, non è possibile utilizzare la cache.

Molte informazioni dettagliate e molto utile potrebbe essere trovato qui: First and Second Level caching in NHibernate

La cache timestamp viene aggiornato ogni volta che una tabella viene scritto, ma in una specie delicata di passaggio:

  • Quando abbiamo esegui la scrittura vera e propria, scriviamo un valore che è da qualche parte in futuro nella cache. Quindi tutte le query che hanno colpito la cache ora non la troveranno, quindi colpiranno il DB per ottenere i nuovi dati. Dato che siamo nel bel mezzo della transazione, avrebbero aspettato fino al terminare la transazione. Se utilizziamo un livello di isolamento basso e un altro thread/macchina tenta di reinserire i vecchi risultati nella cache , poiché la data/ora dell'aggiornamento si trova nel futuro .
  • Quando eseguiamo il commit sulla transazione, aggiorniamo la cache del timestamp con il valore corrente.
Problemi correlati