使用会话和延迟加载

时间:2013-01-02 10:20:50

标签: c# nhibernate

使用NHibernate和C#,我遇到删除加载了属性延迟加载的对象的问题。

对象是。

public class Item
{
    public virtual Guid ItemID { get; set; }    
    public virtual string Name { get; set; }
    public virtual decimal Price { get; set; }
    public virtual string Image { get; set; }
    public virtual Iesi.Collections.Generic.ISet<Tax> Taxes { get; set; }
}

“Taxes”Collection在这里加载了延迟加载。所以我需要保持会话开放(据我所知)。加载“Item”对象时,我使用以下查询。

    public Item FindByID(Guid itemID)
    {
        //using(var session = NHibernateHelper.OpenSession())
        //using (var tr = session.BeginTransaction())
        //{
        //    return session.Get<Item>(itemID);
        //}

        var session = NHibernateHelper.OpenSession();
        return session.Get<Item>(itemID);
    }

在上面的FindByID()代码中,您可以看到我已经注释掉了一些从数据库中提取对象后关闭会话的代码。这是因为,我需要打开会话,因为Taxes对象集合应该是懒惰的。开放代码部分显示在返回找到的对象后会话仍处于打开状态。

否,当我使用以下代码使用上面加载的对象执行删除时。

    public void RemoveItem(Item item)
    {            
        using(var session = NHibernateHelper.OpenSession())
        using (var tr = session.BeginTransaction())
        {
            try
            {
                session.Delete(item);
                tr.Commit();
            }
            catch (Exception Ex)
            {
                throw Ex;
            }
        }
    }

我收到以下错误..

  

非法尝试将集合与两个开放会话相关联

错误很明显,fetched对象已打开会话,当我打开另一个会话删除同一个对象时,会产生此错误。

请有人指导我摆脱这个错误。感谢。

3 个答案:

答案 0 :(得分:3)

错误在您的设计中。您正在打开一个会话以获取该对象,并将其保持打开状态。然后,您打开另一个会话以删除托管对象,但您打算自动关闭此会话。那你为什么不用FindByID方法关闭会话?

您需要考虑您的设计,因为上述内容不一致 - 或者正如Morten所说,提出一个更深入的问题(即如何设计)。

由于您需要访问延迟实例,因此您最好使用Morten的方法并使用每个Web请求的会话或通过设置自己的请求上下文(其中包含会话)(如果您不创建Web)应用)。如果您还没有基于上下文的设计,则更简单的方法是在事件开始时创建会话(即用户想要删除项目)并将会话传递给DAO方法:

public Item FindByID(Session session, Guid itemID)
{
    using (var tr = session.BeginTransaction())
    {
        return session.Get<Item>(itemID);
    }
}

public void RemoveItem(Session session, Item item)
{
    using (var tr = session.BeginTransaction())
    {
         session.Delete(item);
         tr.Commit();
    }
}

您可以更进一步,在DAO之外创建事务,这将更快,并将充分利用事务(即稍后您可能有请求通过您的通用执行依赖于彼此的数据库操作的DAO)。

答案 1 :(得分:1)

您需要确保始终只有一个会话处于活动状态。有两种流行的方法:

  • 拥有一个始终转发请求的服务层。此服务层负责会话处理(和事务处理)。必须在会话层中处理所有业务逻辑,以便始终拥有会话。您必须在UI中加载所需的所有内容,并将其作为响应的一部分返回。参见例如http://davybrion.github.com/Agatha/表示使用nhibernate的简单请求响应服务层。
  • 每个网络请求都有一个会话。这通常涉及在调用控制器方法之前打开会话(和事务),然后提交事务。通常,这是通过使用您放在需要会话的方法上的属性来处理的。我不能给出一个完整的例子,但我知道NHibernate 3.0 Cookbook中描述了一种流行的方法。

答案 2 :(得分:0)

假设您在Web应用程序中使用了每个请求的会话模式,最简单的解决方案是修改NHibernateHelper.OpenSession方法以返回请求的当前会话。为了使这项工作,您不能在using块中包装会话访问,而是在global.asax中管理会话生存期。

如果你有很多你不想重构的现有代码,我会采用这种方法。如果你有时间重构,那么你应该使用依赖注入将ISession注入你的知识库/查询对象。