【问题标题】:Session.Clear & Session.Merge IssuesSession.Clear & Session.Merge 问题
【发布时间】:2012-08-29 21:43:28
【问题描述】:

在我的应用程序中,我需要使用分离的对象并且已经实现了存储库模式。问题出现在以下代码示例中:

var shops = ShopRepository.GetAll();           //Session.Clear() called inside, CacheMode = CacheMode.Ignore
var employees = EmployeeRepository.GetAll();   //same here

// Case 1: Session.Save() works, Session.Merge() irgnores the changes

employees.First().WorksAt.Name = "le new Shop in Town";
EmployeeRepository.Save(employees.First());

// Case 2: Session.Merge() works, Session.Save() throws NonUniqueObjectException

employees.First().WorksAt = employees.Last().WorksAt;
EmployeeRepository.Save(employees.First());

需要先加载商店,因此用户可以通过 ComboBox(或类似的东西)设置“WorksAt” - 员工属性。

我的期望是,在调用 Session.Clear() 之后,休眠“忘记”所有实例(以及底层图表)。事实并非如此。我仍然在 Update() 操作中不断收到 NonUniqueObjectException。因此,我无法使用 SaveOrUpdate,尽管它可以很好地处理对对象图所做的各种更改。

Session.Merge() 只会完成一半的工作:

没有抛出异常,保存了新的引用,但是没有保存对商店属性所做的更改(因为合并只是从缓存中加载“旧”员工/商店对象并将员工属性复制到旧的实例)

我已经尝试过的:

  • 将级联设置为“合并”
  • 在配置中禁用二级缓存
  • Session.Lock(myObj, LockMode.None)
  • Selfmade semi-merge:通过 Repository.Save() 上的反射为 Session.Merge() 返回的对象重置所有引用
  • 覆盖 Equals() 和 GetHashCode() 方法

该模型包含约 50 个实体,因此在保存/更新会话中(重新)加载每个引用的 bo-instance 不是一种选择。目前我正在使用 nHibernate 3.3.0.4 和 FluentNHibernate 1.3。

是否有涵盖这两种情况的紧凑/优雅的解决方案? 也许禁用 NHibernate 的内部 id-tracking 会解决问题?

映射可能如下所示:

public class Shop
{
public virtual Int Id  { get; set; };
public virtual string Name  { get; set; };

public virtual ISet<Employee> AllEmployees{ get; set; }

  public Shop()
  {
    AllEmployees = new HashedSet<Employee>();
  }
}

public class ShopMap: ClassMap<Shop>
{
  public ShopMap()
  {
    Id(x => x.Id);
    Map(x => x.Name);
  }

  HasMany(c => c.AllEmployees).Cascade.None().NotFound.Ignore();
}

public class Employee
{
public virtual Int Id  { get; set; };
public virtual string FirstName  { get; set; };
public virtual string SureName  { get; set; };

public virtual Shop WorksAt{ get; set; }

  public Employee()
  {
  }
}

public class EmployeeMap: ClassMap<Employee>
{
  public EmployeeMap()
  {
    Id(x => x.Id);
    Map(x => x.FirstName);
    Map(x => x.SureName);

    References(c => c.WorksAt).Cascade.SaveUpdate().Fetch.Join().NotFound.Ignore();
  } 
}

更新

这是存储库基类中我的 Save() 方法的代码:

public static void Save(T pSaveObj)
{
  using (ISession session = GetSession())
  {
    using (ITransaction trans = session.BeginTransaction())
    {          
      // session.Save(pSaveObj);
      session.Save(session.Merge(pSaveObj));                                         
      session.Flush();
      session.Clear();
    }
  }
}

【问题讨论】:

    标签: c# nhibernate fluent


    【解决方案1】:

    嗯.. 你完全在滥用 NHibernate。会话中几乎从不调用 Clear 和 Flush。创建会话只是为了执行保存可以为您提供 0 级缓存的好处。事务在处理时隐式回滚(如果没有提交),所以你真的什么都不做。

    如果我没看错你的意图,你宁愿有这样的代码:

    public static void Save(T pSaveObj)
    {
      using (ISession session = GetSession())
      {
        using (ITransaction trans = session.BeginTransaction())
        {          
          session.SaveOrUpdate(pSaveObj);                                         
          trans.Commit()
        }
      }
    }
    
    • 我假设 pSaveObj 未附加到会话(如果已附加,则使用 session.Update)
    • 使用 NH 的方式仍然很糟糕
      • 如果您没有非常充分的理由使用存储库模式,忘记它并使用原始会话对象,它将为您省去数千个问题

    附:看看 NH 用 sql profiler 做什么。我觉得这是一种很好的学习方式。

    【讨论】:

    • 在实际应用中使用这种方法投入了一段时间后:pkmiec 是完全正确的,当涉及到 NH 时,真的不要使用 Repository-Pattern(或分离的业务对象)。改用工作单元并将匹配的 poco 添加到您的设计中
    猜你喜欢
    • 1970-01-01
    • 2011-04-25
    • 1970-01-01
    • 2015-06-20
    • 1970-01-01
    • 1970-01-01
    • 2010-09-15
    • 2019-09-05
    • 1970-01-01
    相关资源
    最近更新 更多