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 ---
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.
using version 4.3.2.0. I'll see if I can recreate the problem in a different project and post if I can.
Having trouble recreating this, so it's likely something funky with my class. No, I'm not using async rules, should I be?
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);
}
}
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); }; } }
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.
You must implement IContextManager and you testcode must set the
ApplicationContext.ContextManager (and)
ApplicationContext.WebContextManager (can be set to null)
Thank you both very much! Exactly what I needed
Copyright (c) Marimer LLC