0

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

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)
  • 自制半合并:通过 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();
    }
  }
}
4

1 回答 1

0

好吧..您完全在滥用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仍然是不好的方式
    • 如果您没有非常充分的理由使用存储库模式,忘记它并使用原始会话对象,它将为您节省数千个问题

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

于 2012-08-29T13:43:28.600 回答