Как исправить ошибку отложенной загрузки NHibernate «ни одна сессия не была закрыта»? - PullRequest
4 голосов
/ 26 марта 2010

Я разрабатываю веб-сайт с ASP.NET MVC, NHibernate и Fluent Hibernate и получаю сообщение об ошибке " Сессия не была закрыта * " при попытке доступа к дочернему объекту.

Это мои доменные классы:

public class ImageGallery {
    public virtual int Id { get; set; }
    public virtual string Title { get; set; }
    public virtual IList<Image> Images { get; set; }
}

public class Image {
    public virtual int Id { get; set; }
    public virtual ImageGallery ImageGallery { get; set; }
    public virtual string File { get; set; }
}

Это мои карты:

public class ImageGalleryMap:ClassMap<ImageGallery> {
    public ImageGalleryMap() {
        Id(x => x.Id);
        Map(x => x.Title);
        HasMany(x => x.Images);
    }
}

public class ImageMap:ClassMap<Image> {
    public ImageMap() {
        Id(x => x.Id);
        References(x => x.ImageGallery);
        Map(x => x.File);
    }
}

А это мой вспомогательный класс Session Factory:

public class NHibernateSessionFactory {
    private static ISessionFactory _sessionFactory;
    private static ISessionFactory SessionFactory {
        get {
            if(_sessionFactory == null) {
                _sessionFactory = Fluently.Configure()
                    .Database(MySQLConfiguration.Standard.ConnectionString(MyConnString))
                    .Mappings(m => m.FluentMappings.AddFromAssemblyOf<ImageGalleryMap>())
                    .ExposeConfiguration(c => c.Properties.Add("hbm2ddl.keywords", "none"))
                    .BuildSessionFactory();
            }
            return _sessionFactory;
        }
    }
    public static ISession OpenSession() {
        return SessionFactory.OpenSession();
    }
}

Все работает нормально, когда я получаю ImageGallery из базы данных, используя этот код:

IImageGalleryRepository igr = new ImageGalleryRepository();
ImageGallery ig = igr.GetById(1);

Но когда я пытаюсь получить доступ к дочернему объекту Image с помощью этого кода

string imageFile = ig.Images[1].File;

Я получаю эту ошибку:

Инициализация [Entities.ImageGallery # 1] - не удалось лениво инициализировать набор ролей: Entities.ImageGallery.Images, ни один сеанс или сеанс не был закрыт

Кто-то знает, как я могу это исправить?

Большое спасибо!

Редактировать

Мой метод GetById:

    public ImageGallery GetById(int id) {
        using(ISession session = NHibernateSessionFactory.OpenSession()) {
            return session.Get<ImageGallery>(id);
        }
    }

Ответы [ 3 ]

8 голосов
/ 26 марта 2010

Предположительно, ваш GetById закрывает сессию. Это может быть явным или с помощью оператора using.

Лучшим подходом к управлению сеансом является шаблон Open Session in View . Сессии NHibernate дешевы в создании, поэтому создавайте их в начале каждого запроса и закрывайте их в конце.

// in global.asax.cs

public void Application_Start()
{
    BeginRequest += delegate {
        CurrentSessionContext.Bind( sessionFactory.OpenSession());
    };

    EndRequest += delegate {
        var session = sessionFactory.GetCurrentSession();
        if (null != session) {
            session.Dispose();
        }
        CurrentSessionContext.Unbind(sessionFactory);
    };
}

// in your NHibernateSessionFactory class
public static ISession OpenSession() {
    return SessionFactory.GetCurrentSession();
}

Используя контейнер DI, мы можем внедрить сеанс с экземплярами, определенными для каждого запроса.

// ninject example
Bind<ISession>()
    .ToMethod( ctx => sessionFactory.GetCurrentSession() )
    .InRequestScope();
3 голосов
/ 26 марта 2010

Сессия, используемая для получения ig, должна быть активной при доступе к ig.Images[1]

Обычно я делаю это путем создания экземпляра сеанса перед всеми вызовами хранилища, передачи ссылки на сеанс в конструктор хранилища и использования этой ссылки внутри класса хранилища

2 голосов
/ 26 марта 2010

Я не уверен, применимо ли это здесь, но это (или, по крайней мере, раньше) распространенная проблема в средах Java MVC. Обычно это происходит при создании сеанса внутри вашего действия. Когда ваше действие завершается, объект сеанса выходит из области видимости, а сеанс закрывается / сбрасывается. Затем, когда представление пытается отобразить коллекцию объектов, которые вы извлекли, оно пытается загрузить коллекцию с отложенной загрузкой, используя закрытый сеанс.

Если вы используете внедрение зависимостей в своем проекте, вы можете сделать так, чтобы ваша DI-инфраструктура создавала для вас сеанс и передавала его как аргумент конструктора в ваш контроллер. Вы также можете указать, что контейнер DI должен охватывать сеанс NHibernate так же, как ваш HTTP-запрос. Это будет поддерживать сеанс до тех пор, пока запрос не закончится (представление завершило рендеринг).

Я использовал Autofac в качестве нашего DI-контейнера для проекта и работаю и был очень доволен им.

...