I must synchronize data coming from WCF WebServices in a SQLite database. This synchronization represents a dozen of WebServices, that can be "grouped" in 4 categories:
Each call to the WebService is done through HttpClient:
response = await client.PostAsync(webServiceName, content);
Each WebService has its own async method where the WebService response is deserialized:
public static async Task<string> PushForm(List<KeyValuePair<string, string>> parameters)
{
var response = await JsonParser.GetJsonFromUrl(WebServiceName.PushForm.Value, parameters);
Forms forms = new Forms();
try
{
forms = JsonConvert.DeserializeObject<Forms>(response);
return response;
}
catch (Exception e)
{
throw new Exception(e.Message);
}
}
Then I have a SynchronizationService class, where I regroup the calls to WebServices by categories:
public async Task<bool> SynchronizeServerData()
{
bool result = false;
try
{
result = true;
List<Table1> tables1 = await WebServices.GetListTable1(null);
if (tables1 != null)
{
ServiceLocator.Current.GetInstance<IRepository>().DeleteAll<Table1>(true);
ServiceLocator.Current.GetInstance<IRepository>().AddAll<Table1>(tables1);
}
List<Table2> tables2 = await WebServices.GetListTable2(null);
if (tables2 != null)
{
ServiceLocator.Current.GetInstance<IRepository>().DeleteAll<Table2>(true);
ServiceLocator.Current.GetInstance<IRepository>().AddAll<Table2>(tables2);
}
List<Table3> tables3 = await WebServices.GetListTable3(null);
if (tables3 != null)
{
ServiceLocator.Current.GetInstance<IRepository>().DeleteAll<Table3>(true);
ServiceLocator.Current.GetInstance<IRepository>().AddAll<Table3>(tables3);
}
...
}
catch (Exception e)
{
result = false;
}
return result;
}
And finally, in the main ViewModel, I call each of these methods:
public async void SynchronizeData(bool firstSync)
{
IsBusy = true;
var resUsers = await _synchronisation.SynchronizeUsersRights();
var resServer = await _synchronisation.SynchronizeServerData();
var resForm = await _synchronisation.SynchronizeForms();
var resViews = await _synchronisation.SynchronizeViews();
IsBusy = false;
}
But due to the use of "await" the performance is not good.
=> I would like to know if there is an easy way to "parallelize" the calls to optimize performance? Or is it possible to separate the data recovery from the SQLite update for this?
On the face of it there appear to be opportunities to run a few things concurrently, for instance in SynchronizeData
you could do this instead:
{
IsBusy = true;
Task resUsersTask = _synchronisation.SynchronizeUsersRights();
Task resServerTask = _synchronisation.SynchronizeServerData();
Task resFormTask = _synchronisation.SynchronizeForms();
Task resViewsTask = _synchronisation.SynchronizeViews();
await Task.WhenAll(resUsersTask, resServerTask, resFormTask, resViewsTask);
var resUsers = resUsersTask.Result;
var resServer = resServerTask.Result;
var resForm = resFormsTask.Result;
var resViews = resViewsTask.Result;
IsBusy = false;
}
...which would allow those 4 tasks to run concurrently.
You could do the same in SynchronizeServerData
; e.g.:
result = true;
Task tables1Task = WebServices.GetListTable1(null);
Task tables2Task = WebServices.GetListTable2(null);
Task tables3Task = WebServices.GetListTable3(null);
List<Table1> tables1 = await tables1Task;
// ...
List<Table2> tables2 = await tables2Task;
// ...
List<Table3> tables3 = await tables3Task;
// ...
This would allow the 3 tasks to run concurrently as well.
How much you actually gain from this might depend on things like does SQLite allow you to do multiple concurrent requests - I don't know the answer to that offhand.
Some other comments:
public async void SynchronizeData(bool firstSync)
async void
is almost always incorrect, except for event handlers and rare fire-and-forget methods, and usually async Task
is what you want. Search SO for any number of good answers on why.
Then lastly, what you really should do is profile the code to see where your real bottlenecks are.