Problem
I'm getting the following error:
Entering the 'GetObjectsNonReenterant' state from the 'GetObjectsNonReenterant' state is prohibited. The current state is 'GetObjectsNonReenterant'. Most probably, you are trying to initiate an object loading operation while the previous object loading operation isn't yet completed. Please postpone your operation until the Session.ObjectsLoaded or IXPObject.OnLoaded event is fired (while Session.IsObjectsLoading is true).
How can I diagnose this issue?
Solutions
As the exception message says, the problem is that a new loading operation is started while another operation is in progress. Here's the list of known possible mistakes that may lead to this situation.
1. You've injected additional business logic into the getter or setter of a persistent property (non-persistent is fine) without checking and this logic includes another persistent object access or modification to other properties. It is imperative to ensure that the object is not being saved or loaded before performing this logic by checking the IsLoading and IsSaving properties. For example:
C#if (!IsSaving && !IsLoading){
DoSomeBusinessRule();
}
Visual BasicIf (Not IsSaving) AndAlso (Not IsLoading) Then
DoSomeBusinessRule()
End If
Note that there are corresponding properties in the Session object. You can check: IsObjectsLoading and IsObjectsSaving. Another reason for this state check is that property setters and getters are used not only when you modify the object, but also when an existing object is loaded from storage.
2. You should not manipulate data from different threads or do this as described at What's up with BeginInvoke?
3. If you calculate a property value based on detail collections or delayed reference properties within the OnSaving method or property getters when a persistent object is being saved or loaded, it may be required to pre-load the collection or reference properties on which this calculation depends. To do this, consider one of following solutions:
- Decorate your reference property with the ExplicitLoadingAttribute;
- Call the Session.Prefetch method from within the overridden OnLoaded method of your persistent class or other suitable places (XAF example: How do I prefetch related details data to increase performance for calculated fields.
These solutions usually result in better performance in such scenarios since they avoid the N+1 Select Problem. This is, however, not 100% guaranteed and can even cause performance degradation in certain scenarios (e.g., when explicitly loaded collections or object graphs contain a lot of complex objects).
4. This exception can also be caused by a known .NET Framework specificity described in the Reentrancy, Avalon, Longhorn and the Client section of the Apartments and Pumping in the CLR blog. In short, an STA thread can process system messages ignoring the "lock" statement executed on it. This situation may happen when a data loading operation executed in the main thread takes too long and the .NET Framework decides to refresh the UI. You can detect this situation by locating the WMPaint method call in the exception call stack. To bypass this intermittent and quite rare issue, use the STASafeDataStore wrapper on top of the usual XPO connection provider:
C#string connectionString = MSSqlConnectionProvider.GetConnectionString("localhost", "database");
IDataStore provider = new DevExpress.Xpo.Providers.STASafeDataStore(XpoDefault.GetConnectionProvider(connectionString, autoCreateOption));
This solution works in single-threaded apartment.
There is also a solution to this issue full of restrictions - use the multithreaded apartment (MTA) for the application (the default is STAThread), which you should normally avoid in your apps.
C#static class Program {
[MTAThread]
static void Main() { ... }
}
Beware that MTAThread lacks some important features like drag-and-drop, OLE, etc. and is generally NOT recommended in WinForms (MSDN). Similarly, we do not support it in our controls and are not responsible for any side effects in this mode, so use it at your own risk if no aforementioned solutions are possible.
To use the STASafeDataStore class in an XAF application, implement a custom IXpoDataStoreProvider and create it in the overridden XafApplication.CreateDefaultObjectSpaceProvider method. For more details, refer to the XPO-specific customizations > Implementing IXpoDataStoreProvider section in the How to customize the underlying database provider options and data access behavior in XAF article. This is an example of the CreateWorkingStore method for your reference. You can implement other methods similarly, but the STASafeDataStore wrapper is unnecessary there.
To use the STASafeDataStore component in an XAF app, create the following ConnectionStringDataStoreProvider descendant:
C#using System;
using DevExpress.ExpressApp.Xpo;
using DevExpress.Xpo.DB;
using DevExpress.Xpo;
using DevExpress.Xpo.Providers;
namespace dxT1066749.Win {
public class STASafeConnectionStringDataStoreProvider : ConnectionStringDataStoreProvider, IXpoDataStoreProvider {
public STASafeConnectionStringDataStoreProvider(string connectionString) : base(connectionString) { }
public new IDataStore CreateWorkingStore(out IDisposable[] disposableObjects) {
IDataStore result = null;
disposableObjects = null;
result = XpoDefault.GetConnectionProvider(ConnectionString, AutoCreateOption.SchemaAlreadyExists, out disposableObjects);
return new STASafeDataStore(result);
}
}
}
Then, use this class's instance in the UseCustomDataStoreProvider (.NET 6) or CreateDefaultObjectSpaceProvider (.NET Framework) method:
C#builder.ObjectSpaceProviders
.AddSecuredXpo((application, options) => {
options.ConnectionString = connectionString;
options.UseCustomDataStoreProvider(new STASafeConnectionStringDataStoreProvider(connectionString));
})
C#namespace dxT1066749.Win {
public partial class dxT1066749WindowsFormsApplication : WinApplication {
protected override void CreateDefaultObjectSpaceProvider(CreateCustomObjectSpaceProviderEventArgs args) {
IObjectSpaceProvider provider = new XPObjectSpaceProvider(new STASafeConnectionStringDataStoreProvider(args.ConnectionString), false);
args.ObjectSpaceProviders.Add(provider);
args.ObjectSpaceProviders.Add(new NonPersistentObjectSpaceProvider(TypesInfo, null));
}
5. This error may also occur while debugging and setting break points in Visual Studio under some rare circumstances, as in the following ticket: Entering state 'GetObjectsNonReenterant' from state 'GetObjectsNonReenterant' is prohibited when using DataStoreService over WCF and debugging/inspecting variables in code editor. In this particular case, since both WCF server and client apps are being debugged at the same time, they are also stopped by Visual Studio when the execution hits a breakpoint. Since the WCF server is stopped, the query cannot be executed correctly. Visual Studio also kills the thread in which the SQL query is executed without waiting for the timeout. Due to the abnormal termination, the session stays in the 'loading' state unexpectedly. You can avoid the error in this case by starting the WCF server without the debugger attached.
See Also:
XPO Best Practices
How to troubleshoot the "Reentrancy or cross thread operation detected" error
(1) works - fantastic, Dennis!
Hi there :)
We have a project that are using Xpo 10.2 … How can we use the STASafeDataStore ?
Best regards
Hello John,
I've created a separate ticket on your behalf (T506768: Can I use STASafeDataStore in version 10.2?). It has been placed in our processing queue and will be answered shortly.
Ahh great - thank you very much :)
Hi Dennis,
We had the same problem with DevExpress 9.3.7. I've just updated the DevExpress to 11.2.10 and it looks like that the problem is solved. Is ther any problem reported about this exception and DevExpress 11.2.10?
Best regards.
@David Maelfait: I've created a separate ticket on your behalf (T520559: Questions about the "Entering the 'GetObjectsNonReenterant' state…" error). It has been placed in our processing queue and will be answered shortly.
Could this solution be expanded with this solution about getting this exception when debugging and using a WCF service: https://www.devexpress.com/support/center/Question/Details/T499844#comment-fe9813d0-9716-4e76-92b1-5aa33d888785
@Blacksnow Developer: I've mentioned this specific scenario in the 5th point of this KB article. In fact, it was the first time when we hit this in the last 7+ years or so, but it is still better to keep this information in one place. Thanks!
Thanks Dennis,
usefull solution.
The problem is solved.
I put my code here, maybe other people can use it.
public XPServerCollectionSource GetRegistrationsContent(string errorString, int levelId, string TableName, DateTime From, DateTime Until, XPServerCollectionSource current)
{
XPServerCollectionSource result = current;
try
{
if (TableName == "Registrations" && (current == null || (current != null && !current.Session.IsObjectsLoading && !current.Session.IsObjectsSaving)))
{
CriteriaOperator criteria = CriteriaOperator.Parse("[Rac_LevelID] = ? AND [Rac_LogDateTime] >= ? AND [Rac_LogDateTime] <= ?", new object[] { levelId, From, Until });
if (current == null)
{
IDataStore dataStore = XpoDefault.GetConnectionProvider(ATRegistrationsConnectionString, AutoCreateOption.DatabaseAndSchema);
IDataStore provider = new STASafeDataStore(dataStore);
XpoDefault.DataLayer = new SimpleDataLayer(provider);
XPClassInfo classInfo = XpoDefault.Session.GetClassInfo(typeof(Registration));
result = new XPServerCollectionSource(XpoDefault.Session, classInfo, criteria);
}
else
{
current.FixedFilterCriteria = criteria;
current.Session.DropIdentityMap();
current.Reload();
result = current;
}
}
}
catch (Exception e)
{
OnErrorEv(0, errorString, e);
}
return result;
}
All of your propositions is misunderstood.
You should resolve in DevExpress and release the update in the new version.
I got the same error and nothing helps.
Hello,
Thank you for contacting us. This diagnostic message is intended to prevent further errors when XPO is used incorrectly - e.g., if an application starts loading objects when the previous loading operation is not finished. Such cases need to be resolved on an application level and cannot be fixed on our side. Please create a ticket in our Support Center and attach a sample project and exception details (Obtain an Exception's Call Stack). Our team will be happy to help.