2011-08-02 8 views
7

私はISession.Refresh()で(少なくとも私にとっては)奇妙な行動を経験してきました。なぜ、NHibernateは "GenericADOException:この場合、ISession.Refresh中にコレクションを初期化できませんでした"という例外をスローしますか?

私は遅延ロードされた子コレクションと、このコレクションに当てはまる読み取り専用プロパティーを持つエンティティを持っています。これらはすべて第2レベルのキャッシュに含まれています。私は、DBへのトランザクションをコミットした後、最新のデータを取得し、次のエラーを取得するのに長いセッションでISession.Refresh()を使用 :

:ここ

NHibernate.Exceptions.GenericADOException : could not initialize a collection: [Test.NUnit.DBTest.TestModel.ParentTestEntity.Children#d4251363-cf88-4684-b65a-9f330107afcf][SQL: SELECT children0_.ParentTestEntity_id as ParentTe4_1_, children0_.Id as Id1_, children0_.Id as Id42_0_, children0_.RowVersion as RowVersion42_0_, children0_.Parent_id as Parent3_42_0_ FROM "ChildTestEntity" children0_ WHERE children0_.ParentTestEntity_id=?] 
    ----> System.NullReferenceException : Object reference not set to an instance of an object. 
    at NHibernate.Loader.Loader.LoadCollection(ISessionImplementor session, Object id, IType type) 
    at NHibernate.Loader.Collection.CollectionLoader.Initialize(Object id, ISessionImplementor session) 
    at NHibernate.Persister.Collection.AbstractCollectionPersister.Initialize(Object key, ISessionImplementor session) 
    at NHibernate.Event.Default.DefaultInitializeCollectionEventListener.OnInitializeCollection(InitializeCollectionEvent event) 
    at NHibernate.Impl.SessionImpl.InitializeCollection(IPersistentCollection collection, Boolean writing) 
    at NHibernate.Collection.AbstractPersistentCollection.Initialize(Boolean writing) 
    at NHibernate.Collection.AbstractPersistentCollection.ReadSize() 
    at NHibernate.Collection.PersistentBag.get_Count() 
    DBTest\TestModel\EntiteTestCacheCollectionsParent.cs(25,0): at Test.NUnit.DBTest.TestModel.ParentTestEntity.get_Count() 
    at (Object , GetterCallback) 
    at NHibernate.Bytecode.Lightweight.AccessOptimizer.GetPropertyValues(Object target) 
    at NHibernate.Tuple.Entity.PocoEntityTuplizer.GetPropertyValuesWithOptimizer(Object entity) 
    at NHibernate.Tuple.Entity.PocoEntityTuplizer.GetPropertyValues(Object entity) 
    at NHibernate.Persister.Entity.AbstractEntityPersister.GetPropertyValues(Object obj, EntityMode entityMode) 
    at NHibernate.Event.Default.AbstractVisitor.Process(Object obj, IEntityPersister persister) 
    at NHibernate.Event.Default.DefaultRefreshEventListener.OnRefresh(RefreshEvent event, IDictionary refreshedAlready) 
    at NHibernate.Event.Default.DefaultRefreshEventListener.OnRefresh(RefreshEvent event) 
    at NHibernate.Impl.SessionImpl.FireRefresh(RefreshEvent refreshEvent) 
    at NHibernate.Impl.SessionImpl.Refresh(Object obj) 
    DBTest\NHibernateBehaviorTests.cs(610,0): at Test.NUnit.DBTest.NHibernateBehaviorTests.Test() 
    --NullReferenceException 
    at NHibernate.Engine.Loading.CollectionLoadContext.AddCollectionToCache(LoadingCollectionEntry lce, ICollectionPersister persister) 
    at NHibernate.Engine.Loading.CollectionLoadContext.EndLoadingCollection(LoadingCollectionEntry lce, ICollectionPersister persister) 
    at NHibernate.Engine.Loading.CollectionLoadContext.EndLoadingCollections(ICollectionPersister persister, IList`1 matchedCollectionEntries) 
    at NHibernate.Engine.Loading.CollectionLoadContext.EndLoadingCollections(ICollectionPersister persister) 
    at NHibernate.Loader.Loader.EndCollectionLoad(Object resultSetId, ISessionImplementor session, ICollectionPersister collectionPersister) 
    at NHibernate.Loader.Loader.InitializeEntitiesAndCollections(IList hydratedObjects, Object resultSetId, ISessionImplementor session, Boolean readOnly) 
    at NHibernate.Loader.Loader.DoQuery(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies) 
    at NHibernate.Loader.Loader.DoQueryAndInitializeNonLazyCollections(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies) 
    at NHibernate.Loader.Loader.LoadCollection(ISessionImplementor session, Object id, IType type) 

が簡略化モデルに問題があることを示し、ユニットテストですここで

[Test] 
    public void Test() 
    { 
     ISession session1 = NHibernateHelper.SessionFactory.OpenSession(); 
     ISession session2 = NHibernateHelper.SessionFactory.OpenSession(); 

     // Create a new entity tree and persist it 
     ParentTestEntity parentSession1 = new ParentTestEntity(); 
     parentSession1.AddChild(new ChildTestEntity()); 
     session1.Save(parentSession1); 
     session1.Flush(); 

     // Load the saved object into another session 
     ParentTestEntity parentSession2 = session2.Get<ParentTestEntity>(parentSession1.Id); 
     session2.Refresh(parentSession2); // Throws here 
    } 

が関与するエンティティです:

public class ParentTestEntity 
{ 
    public virtual Guid Id { get; private set; } 
    public virtual long RowVersion { get; private set; } 

    public virtual IList<ChildTestEntity> Children { get; protected set; } 

    public ParentTestEntity() 
    { 
     this.Children = new List<ChildTestEntity>(); 
    } 

    public virtual int Count 
    { 
     get 
     { 
      return Children.Count; 
     } 

     set { } 
    } 

    public virtual void AddChild(ChildTestEntity child) 
    { 
     if (this.Children == null) 
     { 
      this.Children = new List<ChildTestEntity>(); 
     } 

     this.Children.Add(child); 
     child.Parent = this; 
    } 
} 

public class ChildTestEntity 
{ 
    public virtual Guid Id { get; private set; } 
    public virtual long RowVersion { get; private set; } 

    public virtual ParentTestEntity Parent { get; set; } 
} 

そしてそのマッピング:

前にコレクションを列挙 Cache.ReadWrite()マッピング、
  • を取り除く

    Countプロパティのマッピングを削除
    • :私はどちらかのことがわかった私のテスト中に
      public class ParentTestEntityMap : ClassMap<ParentTestEntity> 
      { 
          public ParentTestEntityMap() 
          { 
           Cache.ReadWrite(); 
      
           Id(x => x.Id) 
            .GeneratedBy.GuidComb(); 
      
           Version(x => x.RowVersion); 
      
           HasMany(x => x.Children) 
            .Inverse() 
            .Cascade.All() 
            .Cache.ReadWrite(); 
      
           Map(x => x.Count); 
          } 
      } 
      
      public class ChildTestEntityMap : ClassMap<ChildTestEntity> 
      { 
          public ChildTestEntityMap() 
          { 
           Cache.ReadWrite(); 
      
           Id(x => x.Id) 
            .GeneratedBy.GuidComb(); 
      
           Version(x => x.RowVersion); 
      
           References(x => x.Parent) 
            .Not.Nullable(); 
          } 
      } 
      

      Refreshが正常に動作するには十分です。

      誰も私がリフレッシュ作業をするために何ができるか考えていますか?

      注:

      • 私は
      • 、NHibernateは2.1.2と3.1.0の両方でこの現象を再現することができ、私はCountで空のセッターは醜いですけど、それだけのマッピングを反映するためにここです実際のモデルのエンティティ。

    答えて

    1

    Get()の代わりにLoad()を使用すると、問題が発生します。

    これは一般的な解決策ではありません。Load()はわずかに異なるセマンティクスを持ち、対応する行が存在しない場合はスローします。

    これは、DBにロードされたエンティティが存在することがわかっているので、私たちのアーキテクチャで許容される制約です。

    ただし、Get()を含むすべてのソリューションは、歓迎します。

    0

    my answer for a similar question, "Cannot assign property value in entity's constructor"を参照してください。私はあなたがそこでそれを読むことができるので、ここですべての詳細には入りません。

    簡単な答えは、コンストラクタ内の仮想プロパティにアクセスしていることです。これはno-noです。次の変更は、問題を解決する必要があります。これらの行を交換してください...これらの行と

    public virtual IList<ChildTestEntity> Children { get; protected set; } 
    
    public ParentTestEntity() 
    { 
        this.Children = new List<ChildTestEntity>(); 
    } 
    

    ...:

    private IList<ChildTestEntity> _children; 
    
    public virtual IList<ChildTestEntity> Children 
    { 
        get { return _children; } 
        protected set { _children = value; } 
    } 
    
    public ParentTestEntity() 
    { 
        _children = new List<ChildTestEntity>(); 
    } 
    

    FxCopのとReSharperのは、この問題を自動的に特定することができる2つの異なるツールです。

    +0

    真実、それはモデルの悪い設計監督です(私はルールについて知っていましたが、当時それを考えなかった、私に思い出させてくれてありがとう)。しかしこれは問題にリンクされていません。テストは変更と全く同じ方法で失敗します。スタックトレースに示されているように、Countのゲッターにリンクしているようです。 –

    関連する問題