我可以在NHibernate中重新附加一个集合吗?

时间:2012-08-28 09:05:31

标签: c# nhibernate lazy-initialization

我有一些对象,其中一些有文档列表,其他对象,......等等。我不想在关系上使用lazy=false,因为它会使应用程序变得非常慢。我可以使用Lock重新附加对象,因此其他属性或一对多关系会在应用时加载。但对于集合,如果我尝试访问它们,我总是会“无法初始化集合”。如果我在连接到该位置的对象上调用Lock(obj),则无效。

我希望我的映射看起来像这样:

<set name="DocumentList" table="material_document" cascade="all" lazy="true">
  <key column="material_id"/>
  <many-to-many class="Document">
    <column name="document_id"/>
  </many-to-many>
</set>

有没有重新附加的方法?或者是否有映射设置?

UPDATE1:

这是材料方面的映射:

<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
<class name="Material" table="material">
<!-- Defining PK -->
<id name="Id" type="integer" column="id">
    <generator class="sequence">
      <param name="sequence">material_id_seq</param>
    </generator>
</id>
<!-- Defining Properties -->
<!-- Defining FK-Relations -->
<set name="DocumentList" table="material_document" cascade="all" lazy="false">
  <key column="material_id"/>
  <many-to-many class="Document">
    <column name="document_id"/>
  </many-to-many>
</set>

文档映射文件没有关于关系的任何信息。数据库名称没问题,其他一切都有效。这些表称为materialmaterial_documentdocument。我的课程为public,属性为public virtual。我的实体有什么问题?你的意思是数据是错误的,或者程序中的对象可能发生了什么......?

List material = LoadAllMaterials();
//some code, that causes the session to be closed, a new session will be opened
foreach (Document d in material.DocumentList) { //causes the exception
    //do something
}

在此之前,我想要获取文档,而不事先加载它们。

更新2: 我目前如何重新附加对象: 当我知道有一个代理并且它不能以另一种方式工作时我设置了强制...我必须检查它是否在会话中因为如果我在不在会话中的对象上调用它,则evict会抛出异常

public void Reattach(Type type, BaseObject o, bool force)
{
    bool inSession = o.IsInSession();

    if (force && inSession)
    {
        GetSession().Evict(o);
        GetSession().Lock(type.ToString(), o, LockMode.None);
    }
    else {

        o = (BaseObject)GetSession().GetSessionImplementation().PersistenceContext.Unproxy(o);

        if (!inSession) {

            GetSession().Lock(type.ToString(), o, LockMode.None);
        }
    }
}

这是我的IsInSession()方法:

public virtual bool IsInSession() {

    ISession session = HibernateSessionManager.Instance.GetSession();

    var pers = session.GetSessionImplementation()
          .GetEntityPersister(GetType().ToString(), this);

    NHibernate.Engine.EntityKey key = new NHibernate.Engine.EntityKey(Id,
        pers, EntityMode.Poco);

    bool isInSession = false;

    try
    {
        object entity = session.GetSessionImplementation().PersistenceContext.GetEntity(key);

        if (entity != null)
        {
            isInSession = true;
        }
    }
    catch (NonUniqueObjectException) {}

    return isInSession;
}

1 个答案:

答案 0 :(得分:2)

我确实在你的一个映射中看到你有lazy =“false”... NHibernate默认使用延迟加载所以我会从你的映射中删除所有lazy =“false”和lazy =“true”语句并且只使用NHibernate默认值。

我创建了以下NUnit测试并且两个测试都通过了,所以我无法复制你的问题,这意味着它是你的lazy =“false”映射的东西或者其他一些问题...如果没有这个问题就很难诊断出来在你面前有完整的应用......

使用以下测试用例,我能够创建以下错误: “正在初始化[SampleApplication.Customer#19] - 无法懒惰地初始化角色集合:SampleApplication.Customer.Addresses,没有关闭会话或会话”

[Test]
public void Testing_A_Detached_Entity()
{
    // Arrange
    var sessionFactory = ObjectFactory.GetInstance<ISessionFactory>();

    Customer customer = null;

    using ( ISession session = sessionFactory.OpenSession() )
    {
        using ( ITransaction tx = session.BeginTransaction() )
        {
            customer = session.Query<Customer>()
                .Where( x => x.CustomerNbr == 19 )
                .FirstOrDefault();
        }
    }

    // Act
    TestDelegate actionToPerform = () =>
       {
           // Looping over this child collection should throw an Exception
           // because we no longer have an active NHibernate session
           foreach ( var address in customer.Addresses )
           {

           }
       };

    // Assert
    Assert.Throws<NHibernate.LazyInitializationException>( actionToPerform );
}

使用以下测试用例并使用NHibernate的session.Lock()方法,我能够成功重新附加分离的对象并获取我的子集合的计数。

[Test]
public void Testing_Reattaching_A_Detached_Entity()
{
    // Arrange
    var sessionFactory = ObjectFactory.GetInstance<ISessionFactory>();

    Customer customer = null;

    using ( ISession session = sessionFactory.OpenSession() )
    {
        using ( ITransaction tx = session.BeginTransaction() )
        {
            customer = session.Query<Customer>()
                .Where( x => x.CustomerNbr == 19 )
                .FirstOrDefault();
        }
    }

    // Act
    ISession newSession = sessionFactory.OpenSession();

    newSession.Lock( customer, LockMode.None );

    // Assert
    Assert.That( customer.Addresses.Count > 0 );
}