使用集合保存子对象时出现InvalidOperationException

本文关键字:InvalidOperationException 对象 集合 保存 | 更新日期: 2023-09-27 18:19:50

我有一个类Foo,其属性类型为ISet。Bar类又具有Foo属性和MiniBars的ISet。使用NHibernate,我希望持久化Foo的一个实例,然后,仍然在同一个NHibernat事务中,将Bar的一个例子添加到Foo对象的Bars属性,将Foo对象添加到Bar对象的Foo属性,然后在我提交事务时,看到Bar对象也被持久化。

然而,我从NHibernate内部的某个地方得到了一个异常:"System.InvalidOperationException:集合已修改;枚举操作可能无法执行。"

我得出的结论是,这与迷你酒吧的ISet有关。在Bar的默认构造函数中,该集合是使用设置的

MiniBars = new HashedSet<MiniBar>();

如果我删除这行代码,或者从Bar.hbm.xml中删除MiniBars属性的映射,一切都会正常工作。

无效代码:

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.CCascade:中指出了此函数

    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中的一个bug,但我认为在使用NHibernat时更可能是我做错了什么。任何想法都将不胜感激!

使用集合保存子对象时出现InvalidOperationException

您描述的错误发生在枚举集合的过程中,并在执行此操作时尝试修改集合

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()));或者将集合修改延迟到完成枚举之后。

我建议查看异常的来源(我将假设它指向某个集合枚举),然后围绕该点查看该集合的一些修改。

相关文章:
  • 没有找到相关文章