You can choose the right way to run asynchronous work in MDriven: use the existing C# task API when adapting code, IAsyncSupportService to keep desktop clients responsive, and AsyncTicket to queue work for MDrivenServer.
What async means in this context
C# async and await are language features for working with operations that finish later. They do not, by themselves, define how MDriven application work is divided between the user-interface thread and background work.
In MDriven, choose the mechanism based on where the work runs and what result you need:
| Situation | Use | Example |
|---|---|---|
| You are adapting C# code that already exposes an asynchronous operation. | Use its task-based API. Await it when your calling method is asynchronous; synchronously obtain its result only when you must call it from synchronous code. | A library method returns a task whose result is needed before the next line can run. |
| You are writing a WPF or WinForms client and a button click starts loading, saving, or other business logic. | Use IAsyncSupportService. Put business logic on its AsyncThread and return to the UI thread only for direct UI work. | A button loads many objects from the persistence server, then updates a textbox. |
| You are building a Turnkey application. | Do not apply the desktop UI-thread pattern to browser interaction. For work that must happen later on the server, use AsyncTicket when its server-side queue model fits the requirement. | A saved Car needs a unique number assigned centrally by the server. |
| You need server-side work to execute later or in sequence with other requests. | Use AsyncTicket. | Queue number assignment so two browser sessions do not assign the same number. |
Adapting an existing C# asynchronous API
If an existing API already uses the C# async pattern, keep its task-based contract where you can. When you must obtain its result from synchronous code, call its result retrieval method as required by that API, for example GetAwaiter().GetResult() for a task.
// A synchronous caller that must wait for an existing task-based API.
var result = existingApi.GetValueAsync().GetAwaiter().GetResult();
Waiting synchronously means the caller waits for completion. It is not a way to move MDriven business logic away from a desktop UI thread. When the goal is a responsive WPF or WinForms application, use IAsyncSupportService instead.
Keep desktop clients responsive
In desktop applications, the UI thread handles user input and view redraws. Long-running loading, saving, persistence-server communication, and other business logic can prevent that thread from responding.
IAsyncSupportService coordinates a UI thread and an AsyncThread for one EcoSpace. When you are not using WECPOF, follow these rules:
- Run business logic in the AsyncThread. Persistence-server communication belongs there.
- Run direct UI-component work on the UI thread. Examples include opening a window, assigning a textbox value directly, or compiling a report.
- When a UI action starts business logic, dispatch that logic to the AsyncThread rather than performing it in the event handler.
The detailed setup, enablement rules, and service events are documented in Documentation:IAsyncSupportService. In particular, use its queue events to indicate that work is in progress and to handle exceptions from the AsyncThread.
Example: run business logic, then update the UI
The following pattern starts work from the UI, performs the business work in the AsyncThread, and returns to the main thread for the direct UI update:
IAsyncSupportService asyn = _RootHandle.EcoSpace.GetEcoService<IAsyncSupportService>();
asyn.PerformTaskAsync(() =>
{
// Business logic and persistence-server communication run here.
asyn.DispatchTaskToMainThread(() =>
{
// Direct UI-component work runs here.
});
});
Use PerformTaskNowIfInAsyncThread when code must run in the AsyncThread and may already be executing there:
IAsyncSupportService asyn = _RootHandle.EcoSpace.GetEcoService<IAsyncSupportService>();
asyn.PerformTaskNowIfInAsyncThread(() =>
{
// This work runs in the AsyncThread.
});
If async handling is turned off, PerformTaskAsync and DispatchTaskToMainThread execute directly. Code written with this pattern can therefore run with async handling enabled or disabled. For the documented recommendations and platform-specific settings, see Documentation:IAsyncSupportService.
Turnkey and browser interaction
Turnkey user interaction occurs in the browser, so the desktop UI-thread guidance above does not apply to keeping a browser interface responsive. Do not add desktop IAsyncSupportService patterns solely because a Turnkey view invokes an action.
This does not mean that all server work should run inline. Use AsyncTicket when the requirement is to let MDrivenServer perform work later. For example, a Car can be saved first, then a ticket can cause MDrivenServer to assign its registration number. Centralizing that assignment avoids two separate browser sessions selecting the same next number before either session saves.
AsyncTicket work is server-side deferred work, not a replacement for C# await and not a desktop UI-thread dispatcher. Its ticket status can report completion or an initial error; see Documentation:AsyncTicket for ticket attributes, delayed execution, priority, and the method or ViewModel execution options.
Related asynchronous behavior
DisplayQueue handles asynchronous databinding updates so repeated intermediate changes do not force a UI update for every change. For example, when code repeatedly increments a value in a loop, the UI does not need to redraw after each increment. DisplayQueue is separate from the choice of C# task handling, IAsyncSupportService, or AsyncTicket.
If you maintain code that uses asynchronous application threads, review Documentation:Threading bug for the documented safeguard against iterations giving up because of temporary locks.
