2012-01-10 71 views
1

我有一個Foo類,具有ISet類型的屬性。 Bar類又有一個Foo屬性和一個MiniBars集。使用NHibernate我希望堅持一個Foo的實例,然後,但仍然在相同的NHibernate事務中,將Bar的實例添加到Foo對象的Bars屬性,將Foo對象添加到Bar對象的Foo屬性,然後當我提交事務時,Bar對象也會被持久化。InvalidOperationException當保存子對象與集合

但是,我從NHibernate的某個地方得到一個Excepetion:「System.InvalidOperationException:集合被修改;枚舉操作可能不會執行。」

我得出結論,這與MiniBars的ISet有關。在酒吧的默認構造函數這一集是使用

MiniBars = new HashedSet<MiniBar>(); 

如果我刪除此行的代碼,或刪除Bar.hbm.xml的迷你吧屬性的映射,一切工作像預想的那樣。

的不工作代碼:

using (var tx = session.BeginTransaction()) 
{ 
    Foo foo = new Foo(); 
    Foo.Id = 1; 
    session.Save(foo); 

    Bar bar = new Bar 
    { 
    Foo = foo; // The setter for Foo also adds Bar to the set Foo.Bars 
    } 

    tx.Commit(); // I wish this to save both foo and bar 
} 

拋出的異常的堆棧跟蹤:

System.InvalidOperationException : Collection was modified; enumeration operation may not execute. 
at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource) 
at System.Collections.Generic.List`1.Enumerator.MoveNextRare() 
at System.Collections.Generic.List`1.Enumerator.MoveNext() 
at NHibernate.Engine.Cascade.CascadeCollectionElements(Object child, CollectionType collectionType, CascadeStyle style, IType elemType, Object anything, Boolean isCascadeDeleteEnabled) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Engine\Cascade.cs: line 231 
at NHibernate.Engine.Cascade.CascadeCollection(Object child, CascadeStyle style, Object anything, CollectionType type) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Engine\Cascade.cs: line 201 
at NHibernate.Engine.Cascade.CascadeAssociation(Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Engine\Cascade.cs: line 185 
at NHibernate.Engine.Cascade.CascadeProperty(Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Engine\Cascade.cs: line 148 
at NHibernate.Engine.Cascade.CascadeOn(IEntityPersister persister, Object parent, Object anything) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Engine\Cascade.cs: line 126 
at NHibernate.Event.Default.AbstractFlushingEventListener.CascadeOnFlush(IEventSource session, IEntityPersister persister, Object key, Object anything) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Event\Default\AbstractFlushingEventListener.cs: line 207 
at NHibernate.Event.Default.AbstractFlushingEventListener.PrepareEntityFlushes(IEventSource session) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Event\Default\AbstractFlushingEventListener.cs: line 195 
at NHibernate.Event.Default.AbstractFlushingEventListener.FlushEverythingToExecutions(FlushEvent event) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Event\Default\AbstractFlushingEventListener.cs: line 48 
at NHibernate.Event.Default.DefaultFlushEventListener.OnFlush(FlushEvent event) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Event\Default\DefaultFlushEventListener.cs: line 18 
at NHibernate.Impl.SessionImpl.Flush() in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Impl\SessionImpl.cs: line 1472 
at NHibernate.Transaction.AdoTransaction.Commit() in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Transaction\AdoTransaction.cs: line 187 
at Company.Product.Test.DatabaseDependentFixtureBase.FillDatabase() in DatabaseDependentFixtureBase.cs: line 121 
at Company.Product.Test.DatabaseDependentFixtureExample.Temp() in DatabaseDependentFixture.cs: line 40 

堆棧跟蹤指出,從NHibernate.Engine.Cascade此功能:

private void CascadeCollectionElements(object child, CollectionType collectionType, CascadeStyle style, IType elemType, object anything, bool isCascadeDeleteEnabled) 
    { 
     // we can't cascade to non-embedded elements 
     bool embeddedElements = eventSource.EntityMode != EntityMode.Xml 
           || ((EntityType) collectionType.GetElementType(eventSource.Factory)).IsEmbeddedInXML; 

     bool reallyDoCascade = style.ReallyDoCascade(action) && embeddedElements 
           && child != CollectionType.UnfetchedCollection; 

     if (reallyDoCascade) 
     { 
      log.Info("cascade " + action + " for collection: " + collectionType.Role); 

      foreach (object o in action.GetCascadableChildrenIterator(eventSource, collectionType, child)) 
       CascadeProperty(o, elemType, style, anything, isCascadeDeleteEnabled); 

      log.Info("done cascade " + action + " for collection: " + collectionType.Role); 
     } 

     var childAsPersColl = child as IPersistentCollection; 
     bool deleteOrphans = style.HasOrphanDelete && action.DeleteOrphans && elemType.IsEntityType 
          && childAsPersColl != null; //a newly instantiated collection can't have orphans 

     if (deleteOrphans) 
     { 
      // handle orphaned entities!! 
      log.Info("deleting orphans for collection: " + collectionType.Role); 

      // we can do the cast since orphan-delete does not apply to: 
      // 1. newly instantiated collections 
      // 2. arrays (we can't track orphans for detached arrays) 
      string entityName = collectionType.GetAssociatedEntityName(eventSource.Factory); 
      DeleteOrphans(entityName, childAsPersColl); 

      log.Info("done deleting orphans for collection: " + collectionType.Role); 
     } 
    } 

在這個函數中有一個foreach循環,但我看不到在廁所內的集合被改變了頁。我儘可能地遵循了方法調用,但是由於我沒有查看過NHibernate源代碼,所以它非常令人難以置信。

我想它可能是NHibernate中的一個錯誤,但我認爲這更可能是我在使用NHibernate時出錯。任何想法,將不勝感激!

+0

對此的任何解決方案?我有完全一樣的問題。 – KyorCode 2012-09-20 05:14:55

+0

不,不幸的不是。我在寫完這個問題後不久就離開了這個問題,所以恐怕我不記得我們是如何解決這個問題的,或者即使我們這樣做了。 – madcap 2012-09-21 06:34:18

回答

2

所描述的錯誤發生在您正在枚舉集合的過程中,並在此過程中嘗試修改集合時。這裏有這樣一個例子:

List<string> someCollection = new List<string>(); 

someCollection.Add("Hello"); 
someCollection.Add("World"); 
someCollection.Add("Hello"); 
someCollection.Add("World"); 

// Enumerate the collection 
foreach (string item in someCollection) 
{ 
    // If the item is "World", remove it from the collection 
    if ("World".Equals(item)) 
    { 
     someCollection.Remove(item); // This will throw an InvalidOperationException. 
    } 
} 

在上面的例子中,當我們試圖從收集我們在枚舉過程中刪除項目拋出異常。這是一個無效的操作。爲了克服這個問題,你需要改變集合的修改方式。替代方法的例子是列舉該集合的副本(即foreach (var item in someCollection.ToArray()));或者直到枚舉完成後才推遲收集修​​改。

我會建議看異常的來源(我將假設指向一些集合枚舉),然後環視該點對該集合進行一些修改。

+0

謝謝。我想我應該寫下''tx.Commit()'從NHibernate拋出異常。我沒有列舉我自己代碼中的任何集合。 – madcap 2012-01-11 07:45:36

+0

您可能不是自己在執行枚舉,但仍可能在相同集合的某些內部枚舉過程中導致集合修改。查看異常中的堆棧跟蹤,並確切地看*拋出異常的代碼片段。然後你可以使用[.NET Reflector](http://bit.ly/wnTYIm)來查看NHibernate代碼(如果這是拋出它的代碼)以查看有問題代碼的反彙編視圖。這可能有助於找出原因。 – 2012-01-11 08:21:38