Исключение отношения многие-к-одному из-за закрытого сеанса после загрузки - PullRequest
0 голосов
/ 14 января 2011

Я впервые использую NHibernate (версия 1.2.1), поэтому я написал простое тестовое приложение (проект ASP.NET), которое его использует.В моей базе данных есть две таблицы: Персоны и Категории.Каждый человек получает одну категорию, кажется достаточно легкой.

<code>
| Persons      |      | Categories   |
|--------------|      |--------------|
| Id (PK)      |      | Id (PK)      |
| Firstname    |      | CategoryName |
| Lastname     |      | CreatedTime  |
| CategoryId   |      | UpdatedTime  |
| CreatedTime  |      | Deleted      |
| UpdatedTime  |
| Deleted      |

Атрибуты Id, CreatedTime, updatedTime и Deleted - это соглашение, которое я использую во всех своих таблицах, поэтому я попытался перенести этот факт на дополнительный уровень абстракции.У меня есть проект DatabaseFramework, который имеет три важных класса:

  • Entity: абстрактный класс, который определяет эти четыре свойства.Все «объекты сущностей» (в данном случае Person и Category) должны наследовать Entity.
  • IEntityManager: универсальный интерфейс (параметр типа как Entity), который определяет такие методы, как Load, Insert, Update и т. Д.
  • NHibernateEntityManager: реализация этого интерфейса, использующая NHibernate для загрузки, сохранения и т. Д.

Теперь классы Person и Category просты, они, конечно, просто определяют атрибуты таблиц (с учетомчто четыре из них находятся в базовом классе Entity).
Поскольку таблица Persons связана с таблицей Categories через атрибут CategoryId, класс Person имеет свойство Category, которое содержит связанную категорию.Однако на моей веб-странице мне также понадобится название этой категории (CategoryName), например, для привязки данных.Поэтому я создал дополнительное свойство CategoryName, которое возвращает свойство CategoryName текущего свойства Category, или пустую строку, если Category имеет значение null:


Namespace Database
    Public Class Person
        Inherits DatabaseFramework.Entity</p>

<pre><code>    Public Overridable Property Firstname As String
    Public Overridable Property Lastname As String
    Public Overridable Property Category As Category

    Public Overridable ReadOnly Property CategoryName As String
        Get
            Return If(Me.Category Is Nothing, _
                      String.Empty, _
                      Me.Category.CategoryName)
        End Get
    End Property

End Class

Конечное пространство имен

Я сопоставляю класс Person с помощью этого файла сопоставления.Отношение «многие к одному» было предложено Ядсом в другой ветке:
</p> <p></p> <pre><code><id name="Id" column="Id" type="int" unsaved-value="0"> <generator class="identity" /> </id> <property name="CreatedTime" type="DateTime" not-null="true" /> <property name="UpdatedTime" type="DateTime" not-null="true" /> <property name="Deleted" type="Boolean" not-null="true" /> <property name="Firstname" type="String" /> <property name="Lastname" type="String" /> <many-to-one name="Category" column="CategoryId" class="NHibernateWebTest.Database.Category, NHibernateWebTest" />

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

Последней важной деталью является метод Load реализации NHibernateEntityManager.(Это в C #, как в другом проекте, извините за это).
Я просто открываю новую ISession (ISessionFactory.OpenSession) в методе GetSession, а затем использую это для заполнения EntityCollection (Of TEntity), который простоколлекция, унаследованная System.Collections.ObjectModel.Collection (Of T).

<code>
        public virtual EntityCollection< TEntity > Load()
        {
            using (ISession session = this.GetSession())
            {
                var entities = session
                                .CreateCriteria(typeof (TEntity))
                                .Add(Expression.Eq("Deleted", false))
                                .List< TEntity >();
                return new EntityCollection< TEntity >(entities);
            }
        }

(Опять же, я не могу заставить ее правильно отформатировать код, она скрывает параметры универсального типа, вероятно,потому что он читает угловые символы как тег HTML ...? Если вы знаете, как мне это сделать, дайте мне знать!)

Теперь идея этого метода Load состоит в том, что я получаю полностью функциональную коллекциюof Persons, все их свойства установлены в правильные значения (включая свойство Category, и, следовательно, свойство CategoryName должно возвращать правильное имя).
Однако, похоже, что это не так.Когда я пытаюсь связать данные результата этого метода Load с GridView в ASP.NET, он говорит мне следующее:

Property accessor 'CategoryName' on object 'NHibernateWebTest.Database.Person' threw the following exception:'Could not initialize proxy - the owning Session was closed.'

Исключение происходит при вызове метода DataBind здесь:

        public virtual void LoadGrid()
        {
            if (this.Grid == null) return;</p>

<pre><code>        this.Grid.DataSource = this.Manager.Load();
        this.Grid.DataBind();
    }

Ну, конечно, сессия закрыта, я закрыл ее с помощью блока using.Разве это не правильный подход, я должен держать сессию открытой?И как долго?Могу ли я закрыть его после запуска метода DataBind?

В каждом случае мне бы очень хотелось, чтобы мой метод Load просто возвращал функциональную коллекцию элементов.Мне кажется, что теперь он получает категорию только тогда, когда это требуется (например, когда GridView хочет прочитать CategoryName, который хочет прочитать свойство Category), но в это время сеанс закрыт.Это рассуждение правильно?

Как мне остановить это поведение?Или я не должен?А что мне делать иначе?

Спасибо!

Ответы [ 2 ]

0 голосов
/ 14 января 2011

Установка отложенной загрузки = false в вашем отображении устранит ошибку. Тем не менее, было бы лучше сообщить NHibernate в своем запросе на загрузку, что вы хотите охотно получать дочернюю коллекцию категорий.

Для запроса criteia должно работать что-то вроде .SetFetchMode ("Categories", FetchMode.Eager).

Вот ссылка , которая дает некоторое представление о так называемой проблеме «n + 1», о том, как ленивая загрузка связана с ней, и как NHibernate предназначен для использования.

НТН,
Berryl

как то так:

           var entities = session
                            .CreateCriteria<TEntity>()
                            .SetFetchMode("Categories", FetchMode.Eager)
                            .Add(Expression.Eq("Deleted", false))
                            .List< TEntity >();
0 голосов
/ 14 января 2011

Проблема в том, что когда ваша сущность загружается лениво.Запрос получает только те элементы, которые ему нужны в данный момент.Так что в вашем случае он получает объект Person.Затем, когда вы получаете доступ к любым связанным объектам, он запускает другой запрос.Для этого он использует прокси-объекты, которые знают о сеансе, который использовался.Вы только оставляете сеанс открытым для загрузки, а затем закрываете его.

Это на самом деле плохая практика в мире NHibernate.Вы хотите сохранить сеанс в течение периода, известного как единица работы .Это дает вам много преимуществ, таких как кэширование и отложенная загрузка.Следовательно, вы можете отключить отложенную загрузку, это должно работать.Хотя я бы порекомендовал изменить класс загрузчика, если это вообще возможно

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...