I am new to this Asynchronous world So bear with my knowledge.
Currently; we are working on implementing Graph API for getting exchange data like Mail Folders, messages etc. Since Graph API follows APM (Asynchronous Programming Model) we ran in to situation “Sync over Asyc”.
_GraphServiceClient.Me.MailFolders.Request.GetAsync()
Background
Let me put the situation; Mail Sending and different mails related code lies in our Root Class Library which has Synchronous methods.
Those methods will be called from different classes of the same library (i.e. executing workflow step will send mail etc) and from ASP.NET Web application, Windows Forms application, Console Application. Now, we can not mark mail sending class’s method to Async as Async virus will spread and we can not make all caller to be Async. There will be hell lot of code to refactor.
From Past few days; I have been reading below related insight full articles of (Stephen Toub & Stephen Cleary”) and few SO posts.
- https://learn.microsoft.com/en-us/archive/msdn-magazine/2015/july/async-programming-brownfield-async-development
- https://devblogs.microsoft.com/pfxteam/should-i-expose-synchronous-wrappers-for-asynchronous-methods/
- https://devblogs.microsoft.com/pfxteam/should-i-expose-asynchronous-wrappers-for-synchronous-methods/
- Calling async methods from non-async code
So, after reading relevant articles above and others over web; I think below approach I need to use. Introduce the Asynchelper wrapper which executes the Async method in another thread of thread pool (using Task.Run) and block the current thread.
GraphService Class: which makes all graph communication and get relevant results.
static class GraphService
{
public async static Task<List<MailFolder>> GetMailFolders(GraphServiceClient graphClient)
{
UserMailFoldersCollectionPage mFolders = await graphClient.Me.MailFolders.Request.GetAsync();
return mFolders.CurrentPage;
}
}
AsyncHelper
using System;
using System.Threading.Tasks;
internal static class AsyncHelper
{
// Private ReadOnly _myTaskFactory As TaskFactory = New TaskFactory(CancellationToken.None, TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.[Default])
public static TResult RunSync<TResult>(Func<Task<TResult>> func)
{
// Dim cultureUi = CultureInfo.CurrentUICulture
// Dim culture = CultureInfo.CurrentCulture
// Return _myTaskFactory.StartNew(Function()
// Thread.CurrentThread.CurrentCulture = culture
// Thread.CurrentThread.CurrentUICulture = cultureUi
// Return func()
// End Function).Unwrap().GetAwaiter().GetResult()
return Task.Run(() =>
{
return func();
}).ConfigureAwait(false).GetAwaiter().GetResult();
}
public static void RunSync(Func<Task> func)
{
// Dim cultureUi = CultureInfo.CurrentUICulture
// Dim culture = CultureInfo.CurrentCulture
// _myTaskFactory.StartNew(Function()
// Thread.CurrentThread.CurrentCulture = culture
// Thread.CurrentThread.CurrentUICulture = cultureUi
// Return func()
// End Function).Unwrap().GetAwaiter().GetResult()
Task.Run(() =>
{
return func();
}).ConfigureAwait(false).GetAwaiter().GetResult();
}
}
Usage in Root Class library:
List<MailFolder> mFolders = AsyncHelper.RunSync(async () => await GraphService.GetMailFolders(_GraphServiceClient));
Could you please help me in this architecture design issue?
- When you do not have any choice; which is the best approach to use for “Sync over Async” and also “Async over Sync”?
- I think “Async” & “Await” operator does not cause additional thread to be created; then how asynchrony takes place in the same thread. Technically; how "Async" & "Await" achieve asynchronous behaviour with out creating a new thread? Actually; I am bit confused between Task.Run & Async await.
- In GraphService Class as described above; it returns only current page data. We need to fetch all mail folders so that i guess we need to perform loop and again execute the same requrest to fetch all mail folders So i think; we might need to follow Asynchelper.RunSync option in that class if we need to follow that approach.
Thanks in Advance. Please Suggest.