NHibernate陈旧状态问题

时间:2011-11-21 13:30:02

标签: nhibernate

我很好奇是否有人可以帮我解决nHibernate中陈旧状态的问题。

首先,.Net类代码:

public class Test
{
    public static Test Get(int testId) { return Factory.GetTest(testId); }

    public Test() { Related = new List<TestRelate>(); }

    public virtual int ID { get; protected set; }
    public virtual string Name { get; set; }
    public virtual IList<TestRelate> Related { get; set; }

    public virtual void Delete() { Factory.Delete(this); }
    public virtual void Save() { Factory.Save(this); }

}
public class TestRelate
{
    protected TestRelate() { }
    public TestRelate(Test test) { TestID = test.ID; }
    public virtual int ID { get; protected set; }
    public virtual int TestID { get; set; }
    public virtual string Data { get; set; }

    public virtual void Delete() { Factory.Delete(this); }
    public virtual void Save() { Factory.Save(this); }
}
class Factory
{
    public static Test GetTest(int testId)
    {
        ISession session = Session.HybridSessionBuilder.Instance;
        IList<Test> ret = null;
        ITransaction tx = null;

        tx = session.BeginTransaction();
        ret = session.CreateCriteria(typeof(Test))
            .Add(Expression.Eq("ID", testId))
            .List<Test>();
        tx.Commit();

        return ret.Count == 0 ? null : ret[0];
    }
    public static void Save<T>(T element)
    {
        ISession session = Session.HybridSessionBuilder.Instance;
        ITransaction tx = null;

        tx = session.BeginTransaction();
        session.Save(element);
        tx.Commit();
    }
    public static void Delete<T>(T element)
    {
        ISession session = Session.HybridSessionBuilder.Instance;
        ITransaction tx = null;

        tx = session.BeginTransaction();
        session.Delete(element);
        tx.Commit();
    }
}

然后是nHibernate映射XML:

<class name="Data.Test.Test, Data" table="test_info">
  <id name="ID" column="testid">
    <generator class="native" />
  </id>
  <property name="Name" />
  <bag name="Related" table="test_relate" lazy="false" cascade="none">
    <key column="testid"></key>
    <one-to-many class="Data.Test.TestRelate, Data"></one-to-many>
  </bag>
</class>
<class name="Data.Test.TestRelate, Data" table="test_relate">
  <id name="ID" column="relateid">
    <generator class="native" />
  </id>
  <property name="TestID" />
  <property name="Data" />
</class>

最后我遇到问题的代码:

Data.Test.Test Test = new Data.Test.Test();
Test.Name = "Hello World";
Test.Save();

Data.Test.TestRelate Relate = new Data.Test.TestRelate(Test);
Relate.Data = "How are you?";
Relate.Save();

Test = Data.Test.Test.Get(Test.ID);
int Count = Test.Related.Count;

问题是,当我运行此代码时,Test.Related列表始终为空。但是,如果我销毁NHibernate会话并再次加载测试,它会按预期填充列表。我意识到我可能会刷新所有缓存数据,但似乎应该有一个更清洁的解决方案来解决这个问题。有什么建议吗?

3 个答案:

答案 0 :(得分:1)

执行new Data.Test.TestRelate(Test)时,没有任何内容可以将新的TestRelate实例添加到所有者Test中的集合中。 (除非你在构造函数中这样做,但我假设你只在那里设置了TestId。)

您应该Add()将新的TestRelate实例添加到Test.Related。 Nhibernate会注意到集合中的更改,并在刷新会话时保存新项目。

答案 1 :(得分:1)

NHibernate在提交时不会自动填充一对多的集合。您应该只将TestRelate个实例添加到Related列表中,就像没有NHibernate一样,然后(如果设置“级联保存”映射)甚至只提交Test实例。

根本不需要在程序中使用TestID属性,因为此属性实际上只是关系数据库映射的副产品。

答案 2 :(得分:1)

好吧,所以我意识到我的方法是由于过去使用NHibernate的级联失败的尝试。我将回顾每一个问题以及我采取的措施来解决它。

  1. 如果我设置了级联保存,当我尝试将Related元素添加到新的Test元素时,NHibernate会失败,因为数据库中的TestID值不允许为空。将属性从整数类型更改为Test类型本身可以解决这种情况,因为NHibernate能够在保存新的Test元素后填充字段值。
  2. 尝试通过从列表中删除Related记录来删除它会导致错误,因为NHibernate在删除之前尝试将TestID字段更新为null。将inverse="true"属性添加到Bag映射元素可以解决此问题。
  3. 删除Test对象不会删除孤立的Related记录。将cascade属性设置为all-orphan-delete可以解决此问题。
  4. 这是所有新代码(Factory类没有变化):

    public class Test
    {
        public static Test Get(int testId) { return Factory.GetTest(testId); }
    
        public Test() { Related = new List<TestRelate>(); }
    
        public virtual int ID { get; protected set; }
        public virtual string Name { get; set; }
        public virtual IList<TestRelate> Related { get; set; }
    
        public virtual void Delete() { Factory.Delete(this); }
        public virtual void Save() { Factory.Save(this); }
    }
    public class TestRelate
    {
        protected TestRelate() { }
        public TestRelate(Test test) { Test = test; }
        public virtual int ID { get; protected set; }
        public virtual Test Test { get; set; }
        public virtual string Data { get; set; }
    
        public virtual void Delete() { Factory.Delete(this); }
        public virtual void Save() { Factory.Save(this); }
    }
    

    映射更改:

    <class name="Data.Test.Test, Data" table="test_info">
      <id name="ID" column="testid">
        <generator class="native" />
      </id>
      <property name="Name" />
      <bag name="Related" table="test_relate" lazy="false" cascade="all-delete-orphan" inverse="true">
        <key column="testid"></key>
        <one-to-many class="Data.Test.TestRelate, Data"></one-to-many>
      </bag>
    </class>
    <class name="Data.Test.TestRelate, Data" table="test_relate">
      <id name="ID" column="relateid">
        <generator class="native" />
      </id>
      <many-to-one name="Test" column="testid" />
      <property name="Data" />
    </class>
    

    以下代码现在按预期运行:

    Data.Test.Test Test; 
    Data.Test.TestRelate Relate;
    
    Test = new Data.Test.Test();
    Test.Name = "Hello World";
    Relate = new Data.Test.TestRelate(Test);
    Relate.Data = "How are you?";
    Test.Related.Add(Relate);
    Test.Save();
    
    Relate = new Data.Test.TestRelate(Test);
    Relate.Data = "Relate #2";
    Test.Related.Add(Relate);
    Test.Save();
    
    Test.Related.RemoveAt(0);
    Test.Save();
    
    Test = Data.Test.Test.Get(Test.ID);
    int Count = Test.Related.Count;
    
    Test.Delete();
    

    我能够从http://ayende.com收集大部分答案。我强烈推荐这个网站作为nHibernate问题的资源。