Подтвердить что ты не робот

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

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

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

Что мне здесь не хватает?

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

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

По умолчанию FluentNHibernate использует ленивую загрузку для сопоставлений коллекции. Другой вариант - изменить это поведение по умолчанию в вашем сопоставлении:

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

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

Ответ 3

"Если мы хотим получить доступ к позициям заказа (после закрытия сеанса), мы получим исключение. Поскольку сеанс закрыт, NHibernate не может лениво загружать позиции заказа для нас. Мы можем показать это поведение следующим образом: метод тестирования"

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

"С нетерпением загружаем класс NHibernateUtil. Если вы знаете, что вам нужен доступ к связанным объектам сущности заказа, вы можете использовать класс NHibernateUtil для инициализации связанных объектов (то есть: для их извлечения из базы данных)".

[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