Problem
The "Reentrancy or cross thread operation detected" exception occurs when you use a SimpleDataLayer object in multiple threads simultaneously or query additional data during unfinished database operations. While you can temporarily suppress this exception in development with DevExpress.Xpo.SimpleDataLayer.SuppressReentrancyAndThreadSafetyCheck = true
, we strongly advise against using this option in production. Instead, please review and fix your code with one of the following solutions.
Solutions
Data manipulations in background threads
- Switch from SimpleDataLayer to ThreadSafeDataLayer. In XAF .NET 6+ applications, set the
options.ThreadSafe
parameter of thebuilder.ObjectSpaceProviders.AddXpo
orbuilder.ObjectSpaceProviders.AddSecuredXpo
method from the Startup.cs or ApplicationBuilder.cs file. In XAF .NET Framework applications, set thethreadSafe
parameter of theXPObjectSpaceProvider
orSecuredObjectSpaceProvider
object instantiated in theXafApplication.CreateDefaultObjectSpaceProvider
method. Note that ThreadSafeDataLayer does not allow you to modify metadata dynamically with XPDictionary.CreateClass, XPClassInfo.CreateMember, and similar methods. To initialize ThreadSafeDataLayer, it is necessary to build metadata in advance using the XPDictionary.CollectClassInfos method. In XAF, please review limitations related to custom persistent fields.
OR - Create a new SimpleDataLayer in a background thread to work with the database safely. For more information, see How to: Connect to a Data Store.
OR - Use async/await XPO API when you want to avoid UI freeze for a better UX. With this API, you will not need to start a background thread. Note that parallel execution of two asynchronous operations should be avoided in the context of one Session instance.
You do not manipulate data in background threads explicitly
Reason #1. Asynchronous data sources load data in a background thread
To avoid this error with XPInstantFeedbackSource and XPInstantFeedbackView components, switch to ThreadSafeDataLayer or create a new SimpleDataLayer instance in the ResolveSession event handler.
Reason #2. Additional data is retrieved during unfinished database operations
Reentrancy errors may even occur with a single thread or ThreadSafeDataLayer when persistent references or collections are loaded until XPO finishes previous database queries - for instance, when you bind a WinForms grid control to XPO data and make database queries in CustomDrawXXX event handlers (based on the WM_Paint message). For more information on these scenarios and solutions, refer to points #3 and #4 at Troubleshooting - How to resolve the "Entering the 'GetObjectsNonReenterant' state…" error.
Reason #3. Your custom or third-party code may use SimpleDataLayer in a background thread indirectly
For instance, you may not find the SimpleDataLayer
string in your solution, but you may have XPObjectSpaceProvider or SecuredObjectSpaceProvider references that set threadSafe=false explicitly or implicitly. To locate and troubleshot this problematic code, do the following:
- Configure your Visual Studio as described at Collect and Analyze the Diagnostic Information and reproduce this error in Visual Studio.
- Review each stack trace in the Debug | Windows | Threads window. Stack traces with references to SimpleDataLayer will lead to the problematic code that uses a data access layer in a background thread.
- If diagnostic information shows that this error relates to DevExpress code, attach a small debuggable sample project, describe your use-case scenario in detail, and attach screeenshots of exception callstacks from all threads.
#SuppressReentrancyAndThreadSafetyCheck
Hello Uriah,
thank you very much for advice. Ways 1 and 3 show don't work for me, because of custom ConnectionProvider and DataStoreProvider (see attachment in first post)
But your hint with XPObjectSpaceProvider was correct one. So i passed the XafApplication
private NcremDataStoreProvider provider = null; protected override void OnCreateCustomObjectSpaceProvider(CreateCustomObjectSpaceProviderEventArgs args) { base.OnCreateCustomObjectSpaceProvider(args); provider = new NcremDataStoreProvider(); args.ObjectSpaceProvider = new XPObjectSpaceProvider(provider, true); }
Thanks
Yevgeniy
You are welcome, Yevgeniy! Please do not hesitate to contact us again in the future.
HelloDevExpress,
switch from
string connectionString = "Server=tcp:…,1433;Initial Catalog…";
dataLayer = XpoDefault.GetDataLayer(defaultConnectionString, AutoCreateOption.DatabaseAndSchema);
to TheadSafeDataLayer described here: https://docs.devexpress.com/XPO/DevExpress.Xpo.ThreadSafeDataLayer
and have some questions:
a)
Can I still set/change the connection string of an UnitOfWork?
Before:
string connectionString = "Server=tcp:…,1433;Initial Catalog…";
myUnitOfWorkConnectionString = connectionString;
After:
string connectionString = ConfigurationManager.ConnectionStrings["MyConnectionString"].ConnectionString;
connectionString = XpoDefault.GetConnectionPoolString(connectionString);
myUnitOfWorkConnectionString = connectionString;
b)
Could you please update your info here
https://supportcenter.devexpress.com/ticket/details/a2944
Explicitly set the XpoDefault.DataLayer property in the entry point of your application.
or is this still correct?
c)
What about the ConnectionHelper.cs files generated using XPO wizard.
They still have code like
public const string ConnectionString = @"Server=tcp:…,1433;Initial Catalog…";
public static void Connect(DevExpress.Xpo.DB.AutoCreateOption autoCreateOption, bool threadSafe = false)
Thank you very much for your help.
Regards. Walter.
Hello Walter,
In order to better serve and track multiple questions in your inquiry, I have taken the liberty of separating the issues you addressed. Please refer to these tickets for further correspondence on these items:
How to change UnitOfWork's connection string dynamically when using IDataLayer
Is it recommended to set XpoDefault.DataLayer at startup
Should I use the ConnectionHelper.Connect method in multithread applications
Is there any plans for a thread-safe data layer that also supports dynamic metadata? Right now I use
SimpleDataLayer
and set the deprecatedSuppressReentrancyAndThreadSafetyCheck
property totrue
. Whenever this property is finally taken down, I will have a huge problem on my hands.Some context: I wrote classes and derived controls that makes it a breeze to create read-only, data bound list controls such as list boxes, grids and combo boxes with only little pieces of informations such as the name of a Sql Server view. They however rely heavily on a shared, long-living
SimpleDataLayer
instance upon which metadata is added on-the-go, like when a user navigates to a form that requires the information in a particular view to be obtained.There's a lot of potential for reusing metadata. Stuff like list of products or customers comes around a lot from one UI to another. For this particular reason, I came to find out it's more efficient to work with just one datalayer and multiple sessions using that same datalayer.
I wished I could use
ThreadSafeDataLayer
, but the requirement that every single view used in every single data control in the whole application (hundreds of different UIs) is too steep. I need to keep it dynamic and keep the ability to query the data from a background thread if need be - of course while taking measures and full responsibility to prevent the UI thread from reacting to changes coming from a different thread.I'd like a workaround that won't require me to review and rewrite code in hundreds of forms.
Hello David,
I created a separate ticket on your behalf: T1066262: Is there any plans for a thread-safe data layer that also supports dynamic metadata?. We placed it in our processing queue and will process it shortly.