CSLA .NET

Vibrant discussion about CSLA .NET and using the framework to build great business applications.

MSTest multithreaded issue: Collection was modified; enumeration operation may not execute.

rated by 0 users
Answered (Verified) This post has 1 verified answer | 8 Replies | 2 Followers

Top 25 Contributor
199 Posts
decius posted on Thu, Apr 19 2012 5:52 PM

When I run my MSTests with multithreading enabled, I experience an erratic test smell. It always occurs during BusinessRules.CheckRules(); but I can't seem to figure out what's causing this for me. All of my business rules utilize the context.AddOutValue syntax, so I'm just not sure what my problem is. Can anyone help? Stacktrace below (just one example, it seems to occur on multiple business objects erratically). 

 

Test method Aps.SaasHr.Test.M3Migration.MigrantTest.ColumnMgrs_LazyOnOldTest threw exception: 

Csla.DataPortalException: DataPortal.Update failed (Csla.DataPortalException: ChildDataPortal.Create failed on the server ---> Csla.Reflection.CallMethodException: Child_Create method call failed ---> Csla.DataPortalException: ChildDataPortal.Fetch failed on the server ---> Csla.Reflection.CallMethodException: Child_Fetch method call failed ---> 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 System.Linq.Enumerable.WhereSelectListIterator`2.MoveNext()

   at System.Linq.Enumerable.<DistinctIterator>d__81`1.MoveNext()

   at Csla.Rules.BusinessRules.CheckRules()

   at Aps.SaasHr.Library.M3Migration.CalcCodeMap.Child_Fetch(String e, String dedType) in C:\Documents and Settings\markb\My Documents\Visual Studio 2010\Projects\Aps.SaasHr\Aps.Saas.Library\M3Migration\CalcCodeMap.cs:line 789

   at lambda_method(Closure , Object , Object[] )

   at Csla.Reflection.MethodCaller.CallMethod(Object obj, DynamicMethodHandle methodHandle, Boolean hasParameters, Object[] parameters)

   --- End of inner exception stack trace ---

-Mark

Answered (Verified) Verified Answer

Top 10 Contributor
9,270 Posts
Verified by decius

The context dictionaries in ApplicationContext are stored, by default, in an AppDomain level singleton. This will cause issues with multithreaded unit test runners. You can overcome this by supplying your own context provider that uses thread local storage instead.

The provider is easy to write and use, just implement an interface and tell csla to use your type. I don't remember the interface type off the top of my head, but you can look at the ApplicationContext code to find it.

Rocky

All Replies

Top 10 Contributor
1,765 Posts

Which version of CSLA are you using?

Do you use async rules?

Are you able to create a test project that replicates the issue?
This would help us identify the problem.

 

Jonny Bekkum, Norway CslaContrib Coordinator

Top 25 Contributor
199 Posts
decius replied on Fri, Apr 20 2012 2:06 PM

using version 4.3.2.0. I'll see if I can recreate the problem in a different project and post if I can. 

-Mark
Top 25 Contributor
199 Posts
decius replied on Fri, Apr 20 2012 2:31 PM

Having trouble recreating this, so it's likely something funky with my class. No, I'm not using async rules, should I be?

-Mark
Top 25 Contributor
199 Posts
decius replied on Fri, Apr 20 2012 5:33 PM

Okay, so it looks like this has SOMETHING to do with the ContextManager. I have this child object that's in a completely different part of the object graph, but it seems that its insert is causing issues. If I remove the SubmitChanges from this unrelated object, the erratic smell goes away. Is there anything that might not be threadsafe in the ContextManager? The code below belongs to a child in a child collection of the parent. The checkrules throwing exception is in a lazy-Syncronous child of the parent. Both perform data operations off the ContextManager.

I suppose in real life this isn't as much of a problem unless the app goes completely multithreaded, but it's really screwing with my tests, so it's quite annoying. 

This is all just a hunch, so I'll see if I can spin up a simple test that recreates this in a simple project.

 

 

private void Child_Insert(DefaultConfig parent)

{

using (var mgr = Csla.Data.ContextManager<DalLinq.M3MigrationDataContext>.GetManager("M3Migration"))

{

var data = new DalLinq.DefaultDetail()

{

DefaultConfigId = parent.Id,

DisplayText = DisplayText,

PropertyName = PropertyName,

IsSelected = IsSelected,

IsRequired = IsRequired

};

 

mgr.DataContext.DefaultDetails.InsertOnSubmit(data);

 

try

{

mgr.DataContext.SubmitChanges(ConflictMode.ContinueOnConflict);

}

catch (ChangeConflictException e)

{

foreach (ObjectChangeConflict occ in mgr.DataContext.ChangeConflicts)

{

// All database values overwrite current values with 

//values from database

occ.Resolve(RefreshMode.OverwriteCurrentValues);

}

}

 

LoadProperty(IdProperty, data.DefaultDetailId);

}

}

-Mark
Top 10 Contributor
1,765 Posts

Hi,

Try changing the code into

using (var mgr = Csla.Data.ContextManager<DalLinq.M3MigrationDataContext>.GetManager(System.Guid.NewGuid().ToString("N")))

When you call SubmitChanges in a child method you should always make sure that the "ContextManager" is owned by your method.
IE: If any parent code on the same thread uses that named "ContextManager" you would submit more changes to the database than you might expect.


The preferred solution to load the ID property (assuming that this is part of a "larger" transaction) would be like this:

    private void Child_Insert(DefaultConfig parent)
    {
      using (var mgr = Csla.Data.ContextManager<DalLinq.M3MigrationDataContext>.GetManager("M3Migration"))
      {
        var data = new DalLinq.DefaultDetail()
        {
          DefaultConfigId = parent.Id,
          DisplayText = DisplayText,
          PropertyName = PropertyName,
          IsSelected = IsSelected,
          IsRequired = IsRequired
        };
        
        mgr.DataContext.DefaultDetails.InsertOnSubmit(data);
 
        data.PropertyChanged += (o, e) =>
                                  {
                                    if (e.PropertyName = "DefaultDetailId")
                                      LoadProperty(IdProperty, ((DefaultDetail) o).DefaultDetailId);
                                  };
      }
    }

Jonny Bekkum, Norway CslaContrib Coordinator

Top 10 Contributor
9,270 Posts
Verified by decius

The context dictionaries in ApplicationContext are stored, by default, in an AppDomain level singleton. This will cause issues with multithreaded unit test runners. You can overcome this by supplying your own context provider that uses thread local storage instead.

The provider is easy to write and use, just implement an interface and tell csla to use your type. I don't remember the interface type off the top of my head, but you can look at the ApplicationContext code to find it.

Rocky

Top 10 Contributor
1,765 Posts

You must implement IContextManager and you testcode must set the

ApplicationContext.ContextManager (and)
ApplicationContext.WebContextManager (can be set to null)

Jonny Bekkum, Norway CslaContrib Coordinator

Top 25 Contributor
199 Posts
decius replied on Mon, Apr 23 2012 7:33 AM

Thank you both very much! Exactly what I needed 

-Mark
Page 1 of 1 (9 items) | RSS

Copyright (c) 2006-2010 Marimer LLC. All rights reserved.
Email admin@lhotka.net for support.
Powered by Community Server (Non-Commercial Edition), by Telligent Systems