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, сеанс или сеанс закрыты
Что мне здесь не хватает?
Ответы
Ответ 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