2009-12-12 2 views
18

У меня есть следующий, казалось бы, простой сценарий, однако я все еще довольно новичок в NHibernate.NHibernate-неудачно лениво инициализировать коллекцию роли

При попытке загрузить следующую модель для редактирования действий на мой контроллер:

Редактировать действие контроллера:

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

Модель:

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

Я получаю следующее сообщение об ошибке:

-failed лениво инициализировать коллекцию роли: SomeOtherModel, не сеанс или сеанс не был закрыт

Что я здесь отсутствует?

ответ

19

Проблема в том, что вы создаете и также закрываете сеанс в моделях GetById. (оператор using закрывает сеанс). Сеанс должен быть доступен во время всей бизнес-транзакции.

Существует несколько способов достижения этого. Вы можете настроить NHibernate для использования метода сессионных фабрик GetCurrentSession. См. this on nhibernate.info или 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); 
} 

Я не использую это. Я написал свою собственную службу транзакций, которая позволяет следующее:

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

Однако вы реализуете его, сеансы и транзакции должны жить до тех пор, как бизнес-операции (или функция системы). Если вы не можете полагаться на изоляцию транзакции или откатить все это.

8

Вы должны с нетерпением загрузить SomeOtherModel коллекцию, если вы собираетесь использовать его перед закрытием сессии:

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

По умолчанию FluentNHibernate uses lazy loading для отображения коллекции. Другой вариант заключается в изменении поведения по умолчанию в вашем отображении:

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

Обратите внимание, что если вы сделаете это SomeOtherModel будет охотно загружен (с помощью внешнего соединения) каждый раз, когда вы загружаете родительский объект, который может быть не хочу, чтобы вы хотите , В общем, я предпочитаю всегда оставлять по умолчанию ленивую загрузку на уровне сопоставления и настраивать свои запросы в зависимости от ситуации.

+2

Я бы не сделать это, так как открытие сделки для каждого вызова плохая практика.Изоляция транзакций и недоступна, кеш-память NHibernate больше не полезна (каждый вызов возвращает новый экземпляр), сохранение незнания невозможно, ленивая загрузка больше не работает. Короче: большинство преимуществ использования NHibernate уничтожается. –

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

} 

Ссылка: http://nhibernate.info/doc/howto/various/lazy-loading-eager-loading.html

Смежные вопросы