2009-12-12 10 views
18

Ho il seguente scenario apparentemente semplice, tuttavia sono ancora abbastanza nuovo in NHibernate.NHibernate - fallito per inizializzare pigramente una collezione di ruoli

Quando si cerca di caricare il seguente modello per un'azione Modifica sul mio controller:

del controller Modifica azione:

public ActionResult Edit(Guid id) 
{ 
    return View(_repository.GetById(id)); 
} 

Repository:

public SomeModel GetById(Guid id) 
{ 
    using (ISession session = NHibernateSessionManager.Instance.GetSession()) 
     return session.Get<SomeModel >(id); 
} 

Modello:

public class SomeModel 
{ 
    public virtual string Content { get; set; } 
    public virtual IList<SomeOtherModel> SomeOtherModel { get; set; } 
} 

ottengo il seguente errore:

-failed per inizializzare pigramente una collezione di ruolo: SomeOtherModel, nessuna sessione o la sessione è stata chiusa

Che cosa mi manca qui?

risposta

19

Il problema è che si crea e si chiude anche la sessione nel metodo GetById del proprio modello. (l'istruzione using chiude la sessione) La sessione deve essere disponibile durante l'intera transazione commerciale.

Ci sono diversi modi per raggiungere questo obiettivo. È possibile configurare NHibernate per utilizzare il metodo GetCurrentSession delle factory di sessione. Vedi this on nhibernate.info o this post on Code Project.

public SomeModel GetById(Guid id) 
{ 
    // no using keyword here, take the session from the manager which 
    // manages it as configured 
    ISession session = NHibernateSessionManager.Instance.GetSession(); 
    return session.Get<SomeModel >(id); 
} 

Non lo uso. Ho scritto il mio servizio di transazione che permette la seguente:

using (TransactionService.CreateTransactionScope()) 
{ 
    // same session is used by any repository 
    var entity = xyRepository.Get(id); 

    // session still there and allows lazy loading 
    entity.Roles.Add(new Role()); 

    // all changes made in memory a flushed to the db 
    TransactionService.Commit(); 
} 

tuttavia di implementare, sessioni e transazioni dovrebbero vivere a lungo come una transazione commerciale (o di una funzione di sistema). A meno che tu non possa fare affidamento sull'isolamento della transazione né sul rollback del tutto.

8

è necessario caricare con entusiasmo la raccolta SomeOtherModel se avete intenzione di usarlo prima di chiudere la sessione:

using (ISession session = NHibernateSessionManager.Instance.GetSession()) 
{ 
    return session 
     .CreateCriteria<SomeModel>() 
     .CreateCriteria("SomeOtherModel", JoinType.LeftOuterJoin) 
     .Add(Restrictions.Eq(Projections.Id(), id)) 
     .UniqueResult<SomeModel>(); 
} 

Per impostazione predefinita FluentNHibernate uses lazy loading per mappature di raccolta. Un'altra opzione è quella di modificare questo comportamento predefinito nella tua mappatura:

HasMany(x => x.SomeOtherModel) 
    .KeyColumns.Add("key_id").AsBag().Not.LazyLoad(); 

Si noti che se si esegue questa operazione SomeOtherModel saranno avidamente caricato (con un outer join) ogni volta che si carica la controllante che potrebbe non essere vuoi vuoi . In generale, preferisco lasciare sempre il carico pigro predefinito al livello di mappatura e regolare le mie query a seconda della situazione.

+2

non vorrei fare questo, dal momento che l'apertura di una transazione per ogni chiamata è cattiva pratica.L'isolamento delle transazioni non è disponibile, la cache di NHibernate non è più utile (ogni chiamata restituisce una nuova istanza), l'ignoranza della persistenza non è possibile, il caricamento lazy non funziona più. In breve: la maggior parte dei vantaggi dell'utilizzo di NHibernate viene distrutta. –

1

"If we want to access the order line items (after the session has been closed) we get an exception. Since the session is closed NHibernate cannot lazily load the order line items for us. We can show this behavior with the following test method"

[Test] 
[ExpectedException(typeof(LazyInitializationException))] 
public void Accessing_customer_of_order_after_session_is_closed_throws() 
{ 
    Order fromDb; 
    using (ISession session = SessionFactory.OpenSession()) 
     fromDb = session.Get<Order>(_order.Id); 

    // trying to access the Customer of the order, will throw exception 
    // Note: at this point the session is already closed 
    string name = fromDb.Customer.CompanyName; 
} 

"Eagerly loading with the NHibernateUtil class If you know you need have access to related objects of the order entity you can use the NHibernateUtil class to initialize the related objects (that is: to fetch them from the database)."

[Test] 
public void Can_initialize_customer_of_order_with_nhibernate_util() 
{ 
    Order fromDb; 

    using (ISession session = SessionFactory.OpenSession()) 
    { 
     fromDb = session.Get<Order>(_order.Id); 

     NHibernateUtil.Initialize(fromDb.Customer); 
    } 

    Assert.IsTrue(NHibernateUtil.IsInitialized(fromDb.Customer)); 
    Assert.IsFalse(NHibernateUtil.IsInitialized(fromDb.OrderLines)); 

} 

Riferimento: http://nhibernate.info/doc/howto/various/lazy-loading-eager-loading.html

Problemi correlati