How do I get started with asynchronous operations in general and their XPO use in particular?
Check out the Asynchronous Programming Patterns and Task-based Asynchronous Pattern articles in Microsoft .NET Guide. To get started with XPO asynchronous APIs specifically, consider the following resources:
- Code examples in our GitHub demos;
- Code examples in our offline XpoTutorials demo ("c:\Users\Public\Documents\DevExpress Demos XX.X\Components\WinForms\CS\XpoTutorials\AsyncLoadingTaskBased\AsyncLoadingTaskBased.cs" );
- Online Documentation for Session, XPQueryExtensions.
Do all ADO.NET providers support async operations and how XPO deals with it?
Not all ADO.NET providers supported by XPO support asynchronous operations. Also, even though XPO can pass System.Threading.CancellationToken from XPO async APIs to the underlying ADO.NET provider APIs, certain providers can ignore this token. To force XPO to always wrap synchronous methods in Task.Run calls, you can set the static XpoDefault.DataStoreAsyncBehavior property to WrapAlways. This can be used in desktop apps to create a non-blocking pseudo-asynchronous UI for providers like Oracle that do not natively implement async methods yet. Do not use this approach in Web apps.
Do these new Task-based APIs support cancellation?
All XPO methods accept System.Threading.CancellationToken. For more information on how to cancel async operations with XPO, refer to the attached XpoAsyncCancel.zip project (research its Form1.cs file).
Do these new Task-based APIs provide progress notifications?
We do not provide any means for reporting progress by the following reasons:
- Standard asynchronous APIs (IDbConnection, IDbCommand, IDataReader, etc.) of ADO.NET drivers supported by XPO do not support IProgress<T> or similar means.
- Even if we had provided XPO method overloads accepting the IProgress<T> parameters, it would not be much usable: the progress would stay at 0% most of the method execution time and then immediately go to 100%. For instance, 0%…0%…0%…0%…0%…99%…100%. That is because the most heavy operations are done at the database level and their progress could not be reliably determined.
Can I use async/await with direct SQL queries (Session methods like ExecuteScalar, ExecuteQuery, GetObjectsFromQuery, etc.)?
Yes, this is supported in v19.1+. Also, note that not all ADO.NET providers supported by XPO support asynchronous operations.
In v18.2, wrap these method calls into the Task.Run method manually, if required. Note that the asynchronous operation cannot be canceled under this approach.
Can I use async/await with XPCollection and XPView?
The XPCollection.LoadAsync method supports async/await in v.20.1+. XPView will not support async/await. We recommend that you use XPQuery<T> instead - it is a more efficient way to query data.
Should we use the ThreadSafeDataLayer to use async/await XPO methods?
In general, the requirement to use the ThreadSafeDataLayer remains the same as if synchronous operations were used in the same context. If the existing code works with the SimpleDataLayer and you just change it to use asynchronous methods, you can continue using the SimpleDataLayer.
The only thing that should be avoided is the parallel execution of two asynchronous operations in the context of one Session instance. Use the await operator if you need to call an asynchronous method several times.
C#await session.DoSomethingAsync();
await session.DoSomethingAsync();
What do you recommend for manipulations with large amounts of data in non-UI threads?
XPO cannot work with objects created in UI threads from non-UI threads. Data manipulations with objects in a session often occur in the same thread where the commit operation was initiated. If you process a large number of objects, these data manipulations can lock or freeze the UI. Example: you commit thousands of persistent objects using CommitChangesAsync. If this causes noticeable usability issues, you can consider the following options:
- Do not process thousands of objects within a session. Keep short-living sessions and save your changes in small batches.
- Execute data manipulations with thousands of objects in a separate thread, which creates a separate session. In this case, you can also unbind editors and the data grid from XPO objects, call the CommitChanges method in a background thread (do not use the asynchronous CommitChangesAsync method) and then rebind controls to updated data.
Does XAF's IObjectSpace support async/await?
Yes, you can perform asynchronous operations in XAF WinForms apps using the IObjectSpaceAsync interface. You can also configure asynchronous data loading in WinForms List and Detail Views with the UseAsyncLoading property. In versions older than v20.1, you can use the IObjectSpace > GetObjectsQuery<T> method with ToListAsync, ToArrayAsync, CountAsync and other suitable IQueryable methods. You can also wrap IObjectSpace method calls into the Task.Run method manually, if required. Note that the asynchronous operation cannot be canceled with this approach.
I'm having a bit of a problem.
I'm using WCF, and connection pooling, and SQL server depen dency notifications (and SQL provider descendant for CTE support).
Basically, everything available :)
Connection is made thus (DbSqlServerProvider is the name of MSSqlProvider descendant with CTE):
public override IDataStore CreateDataStore(ConnectionInfo ci) { DbSqlServerProvider.Register(); var xpoConnectionString = ci.User.IsBlank() ? DbSqlServerProvider.GetConnectionString(ci.Server, ci.Database) : DbSqlServerProvider.GetConnectionString(ci.Server, ci.User, ci.Password, ci.Database); xpoConnectionString = String.Concat(xpoConnectionString, ";", "MultipleActiveResultSets=true"); var sqlParser = new ConnectionStringParser(xpoConnectionString); sqlParser.RemovePartByName(DbSqlServerProvider.XpoProviderTypeParameterName); var sqlConnectionString = sqlParser.GetConnectionString(); var sqlConnection = new SqlConnection(sqlConnectionString); var sqlStore = new DbSqlServerProvider(sqlConnection, AutoCreateOption.DatabaseAndSchema); DbEngine.FSPath = SqlClrAssembly.Check(sqlConnection); var poolConnectionString = XpoDefault.GetConnectionPoolString(xpoConnectionString); var poolStore = XpoDefault.GetConnectionProvider(poolConnectionString, AutoCreateOption.DatabaseAndSchema); var cacheConfiguration = new DataCacheConfiguration(DataCacheConfigurationCaching.NotInList); var objectsToDispose = new IDisposable[] { sqlConnection }; var root = MSSql2005SqlDependencyCacheRoot.CreateSqlDependencyCacheRoot(poolStore, sqlConnection, sqlStore, cacheConfiguration, out objectsToDispose); var node = new DataCacheNodeLocal(root) { MaxCacheLatency = TimeSpan.Zero }; node.ProcessCookie(DataCacheCookie.Empty); return node; }
So far, so good …
On the client, on the other side of the WCF code is:
public override IDataStore CreateDataStore(ConnectionInfo ci) ... var client = new CachedDataStoreClient(binding, new EndpointAddress(uri)); ... var root = client as ICachedDataStore; var node = new DataCacheNode(root) { ... }; ... node.ProcessCookie(DataCacheCookie.Empty); return node; }
Actually, above is wrapped in 'XDataStoreWrapper' which just wrappes above node as IDataStore and adds some logging.
This worked until 19.1.
Now, under certain circumstances, I get the exception:
Your 'XDataStoreWrapper' Connection Provider does not support the 'DevExpress.Xpo.DB.IDataStoreAsync' interface. Please implement this interface, or use another compatible Connection Provider.
Now, I *cannot* forward implementation of IDataStoreAsync in wrapper as DataCacheNode (nor ICachedDataStore) do NOT implement it. So I basically wrote the following dummy wrappers:
public sealed class XDataStoreWrapper : IDataStore, IDataStoreSchemaExplorer, ICommandChannel, ICachedDataStore, ICacheToCacheCommunicationCore, IDataStoreAsync, ... ... public IDataStore Store { get; } public XDataStoreWrapper(IDataStore nested) => Store = nested; ... ... public Task<ModificationResult> ModifyDataAsync(CancellationToken token, params ModificationStatement[] statements) => Store is IDataStoreAsync sa ? sa.ModifyDataAsync(token, statements) : Task.Factory.StartNew(() => Store.ModifyData(statements), token); public Task<SelectedData> SelectDataAsync(CancellationToken token, params SelectStatement[] statements) => Store is IDataStoreAsync sa ? sa.SelectDataAsync(token, statements) : Task.Factory.StartNew(() => Store.SelectData(statements), token); ... ...
… but it doesn't feel quite right.
Basically what I'm asking is since you provide class CachedDataStoreService (which implements ICachedDataStoreService), shouldn't there also be (say) CachedDataStoreServiceAsync implementing ICachedDataStoreServiceAsync.
Or am I missing something obvious?
Actually, it seems I'm overcomplicating it.
Basically, I try to avoid the exception:
Connection Provider does not support the 'DevExpress.Xpo.DB.IDataStoreAsync' interface. Please implement this interface, or use another compatible Connection Provider.
So, I change this
var client = new CachedDataStoreClient(binding, new EndpointAddress(uri)); var node = new DataCacheNode(client); return node;
… to this …
var client = new CachedDataStoreClientAsync(binding, new EndpointAddress(uri)); var node = new DataCacheNode(client); return node;
But that doesn't seem to help, as ICachedDataStoreAsync which client implements is 'lost' when chaining to DataCacheNode?
So, is there something like DataCacheNodeAsync planned for 19.1 or should fallback to Task.Run substitues in IDataStore wrapper classes via Task.Factory.Start as above in the first post?
@Dusan Pupis: Thank you for testing our preview build! We will answer you in a separate ticket created on your behalf:
Trying to avoid the exception: Connection Provider does not support the 'DevExpress.Xpo.DB.IDataStoreAsync' interface. in v19.1 CTP.
>> Q: Can I use async/await with XPCollection and XPView?
>> A: No, XPCollection.LoadAsync and XPView.LoadAsync will not return Task and thus will not support async/await. We recommend that you use XPQuery<T> instead - it is a more efficient way to query data.
I have remark regarding this answer. Actually, one can implement their own Extension methods based on the TaskCompletionSource to make XPCollection.LoadAsync und XPView.LoadAsync awaitable. There is a nice, albeit old answer from Manuel Grundner in Q555310 explaining this. I have been using my own extension methods for a long time now and they work sufficiently well. Here is an example of the XPCollection.LoadAsync using TaskCompletionSource:
/// <summary> /// Asynchronously loads persistent objects of a specific type from the data store into the collection, and returns an awaitable Task. /// </summary> /// <param name="collection">The instance on which this extension method is executed.</param> /// <returns>An awaitable Task whose result holds the collection's contents.</returns> public static Task<ICollection> LoadCollectionAsync(this XPBaseCollection collection) { var tcs = new TaskCompletionSource<ICollection>(); AsyncLoadObjectsCallback h = null; h = (result, exception) => { h = null; if (exception != null) tcs.SetException(exception); else if (result.Length > 0) tcs.SetResult(result[0]); else tcs.SetResult(null); }; if (!collection.IsLoaded && collection.LoadAsync(h)) return tcs.Task; return Task.Factory.StartNew(() => (ICollection) collection); }
@Christoph: We considered this approach, but eventually declined it, because the caller thread must have SynchronizationContext. For instance, your approach will not work in Console or ASP.NET Core apps. It will still work in WinForms, WPF or UWP apps. The Task-based solution we provided does not require SynchronizationContext and will work with any apps. For more information, refer to the following public community resources:
https://blog.stephencleary.com/2017/03/aspnetcore-synchronization-context.html
https://devblogs.microsoft.com/pfxteam/await-synchronizationcontext-and-console-apps/
>> Q: Can I use async/await with XPCollection and XPView?
>> A: No, XPCollection.LoadAsync and XPView.LoadAsync will not return Task and thus will not support async/await. We recommend that you use XPQuery instead - it is a more efficient way to query data.
Could you please provide an example how to use XPQuery in async mode - espacially how to use XPQuery<T>.ExecuteAsync<S>(…)
Hello Klaus,
You can find two examples with ToListAsync and CountAsync at https://community.devexpress.com/blogs/xpo/archive/2018/10/16/xpo-async-await-method-support-v18-2.aspx.
The ExecuteAsync method is normally not called from customer code (unless you implement generic expressions or a custom LINQ provider). This is an asynchronous version of the standard IQueryProvider.Execute method. For more information, see Microsoft documentation.
If you want to use this method with XPO, please describe your use-case scenario in greater detail so that we could provide you with suitable examples, or describe XPO specifics, if any.
Hello Dennis,
thank you. Currently ToListAsync will fit my needs.
Great!