由于加载后关闭会话导致的多对一关系异常

时间:2011-01-13 21:48:40

标签: asp.net nhibernate session

我第一次使用NHibernate(版本1.2.1),因此我编写了一个使用它的简单测试应用程序(一个ASP.NET项目)。在我的数据库中,我有两个表:人员和类别。每个人都有一个类别,看起来很容易。


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

Id,CreatedTime,UpdatedTime和Deleted属性是我在所有表中使用的约定,所以我试图将这个事实带入另一个抽象层。我有一个项目DatabaseFramework,它有三个重要的类:

  • 实体:定义这四个属性的抽象类。所有'实体对象'(在本例中为Person和Category)必须继承实体。
  • IEntityManager:一个通用接口(类型参数为Entity),用于定义Load,Insert,Update等方法。
  • NHibernateEntityManager:这个接口的实现使用NHibernate来进行加载,保存等。

现在,Person和Category类很简单,它们只是定义了表的属性(请记住,其中有四个属于基类Entity类)。
由于Persons表通过CategoryId属性与Categories表相关,因此Person类具有包含相关类别的Category属性。但是,在我的网页中,我还需要此类别的名称(CategoryName),例如用于数据绑定目的。所以我创建了一个额外的属性CategoryName,它返回当前Category属性的CategoryName属性,如果Category为null,则返回一个空字符串:


Namespace Database
    Public Class Person
        Inherits DatabaseFramework.Entity

    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

End Namespace

我使用此映射文件映射Person类。 Yads在另一个帖子中提出了多对一的关系:
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

<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的标签...)

最后一个重要细节是NHibernateEntityManager实现的Load方法。 (这是在C#,因为它在一个不同的项目,抱歉) 我只是在GetSession方法中打开一个新的ISession(ISessionFactory.OpenSession),然后用它来填充一个EntityCollection(Of TEntity),它只是一个继承System.Collections.ObjectModel.Collection(Of T)的集合。

<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方法的想法是我获得了一个功能完整的Persons集合,它们的所有属性都设置为正确的值(包括Category属性,因此,CategoryName属性应该返回正确的名称)。登记/> 但是,似乎并非如此。当我尝试将此Load方法的结果数据绑定到ASP.NET中的GridView时,它告诉我:

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

此处的DataBind方法调用发生异常:



嗯,当然会话已关闭,我通过using块关闭它。这不是正确的方法,我应该保持会议开放吗?而且持续多久?我可以在运行DataBind方法后关闭它吗?

在每种情况下,我都非常喜欢我的Load方法只返回一个功能的项目集合。在我看来,它现在只在需要时才获得类别(例如,当GridView想要读取想要读取Category属性的CategoryName时),但此时会话被关闭。这个推理是否正确?

如何阻止此行为?或者我不应该?那我该怎么办?

谢谢!

2 个答案:

答案 0 :(得分:0)

问题是当你的实体延迟加载时。查询仅获取当前所需的项目。所以在你的情况下,它获取Person对象。然后,当您访问任何链接的实体时,它会触发另一个查询。它使用代理对象来了解所使用的Session。您只是为负载保持会话打开,然后您关闭它。

这在NHibernate世界中实际上是不好的做法。您希望将会话保持活动一段时间,称为unit of work。这为您提供了许多好处,例如缓存和延迟加载。因此,您可以禁用它应该工作的延迟加载。虽然我建议在任何可能的情况下修改你的装载程序类

答案 1 :(得分:0)

在映射中设置lazy loading = false将解决错误。尽管在你的加载查询中告诉NHibernate你想要急切地获取子类集合,这是一个更好的做法。

对于criteia查询,类似.SetFetchMode(“Categories”,FetchMode.Eager)应该有效。

这是一个link,可以深入了解所谓的“n + 1”问题,延迟加载与它的关系,以及如何使用NHibernate。

HTH,
Berryl

类似的东西:

           var entities = session
                            .CreateCriteria<TEntity>()
                            .SetFetchMode("Categories", FetchMode.Eager)
                            .Add(Expression.Eq("Deleted", false))
                            .List< TEntity >();