Actually, it is quite complicated. It only occurs once in a while in our production system, so I can't send you a small test project.
Furthermore, For our MVC web application I have created several caches in which I store the DataContext. In our software, a single user Session has its own DataContext.
To cleanup after the users Session expires, I created a SessionDisposableObjectCache, which caches disposable objects and disposes them when their session has timed out, as a safety net. But in reality, you should call UnregisterDisposableObject as soon as the object isn't needed anymore, to free up resources.
Most of the time, we want the same DataContext throughout the request (so the View can still use it). Some of our webpage request can take quite long (its an internal line of business application), during which our users can open a new Internet Explorer tab. This will most likely start a new UseCase and create a new DataContext. This would mess up the old, still pending, request.
Therefor, we have another cache, the RequestDataContextCache, which caches the DataContext for at least the time of the request, so each request will get, through it's lifetime, the DataContext it started with, even if the user started another request.
The RequestDataContextCache is responsible for removing a DataContext that is associated with a request that doesn't exists anymore, when a new DataContext was requested. It must remove the DataContext by calling the SessionDisposableObjectCache.UnregisterDisposableObject, so the SessionDisposableObjectCache will not try to dispose the DataContext at the Session End. The SessionDisposableObjectCache.UnregisterDisposableObject disposes the DataContext by calling its IDisposable.Dispose() interface
I don't know if any of this has something to do with the exception. It seems to me that Devart.Data.Linq.Provider.j.e() accesses a Dictionary at the same time it is accessed somewhere else, without both accesses being synchronised using a lock {}.
Or maybe it is because the DataContext has already been disposed, although in general multiple times disposing works (as it should when conforming to the IDisposable contract).
Apart for the already quoted stacktrace, it also happens when a new DataContext is created and an DataContext should be disposed:
Code: Select all
System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
at System.Collections.Generic.Dictionary`2.ValueCollection.Enumerator.MoveNext()
at Devart.Data.Linq.Provider.j.e()
at Devart.Data.Linq.Provider.DataProvider.Dispose(Boolean disposing)
at Devart.Data.Linq.DataContext.Dispose()
at Jumbo.Juist.Models.AppDataContext.System.IDisposable.Dispose() in D:\Projects\JUIST\Juist\Juist\Models\AppDataContextPartial.cs:line 65
at Jumbo.Utilities.SessionDisposableObjectCache.DisposeAndRemoveTimedOutObjects()
at Jumbo.Utilities.SessionDisposableObjectCache.RegisterDisposableObject(IDisposable obj)
In general, the code in UnregisterDisposableObject is quite simple (removed try/catch):
Code: Select all
obj.Dispose();
lock (_lock)
{
Remove(obj);
}
Remove removes the object from the Dictionary cache. By the way, the object is stored in a WeakReference, so the cache isn't preventing it from being garbage collected.
Please let me know, if I can provide you with other information or help in any way.
Kind regards,
Edwin.