Execute Async methods in parallel using Task.WhenAll - parallel-processing

I need to insert data to my database using InsertData(). Data is being fetched using another service. The below code is executing one after one. What is the best approach to execute this in parallel.
var taskslist = new List<Task>();
taskslist.Add(this.database.InsertData("data1", await this.service.GetData1()));
taskslist.Add(this.database.InsertData("data2", await this.service.GetData2()));
taskslist.Add(this.database.InsertData("data3", await this.service.GetData3()));
await Task.WhenAll(taskslist);

You could wrap your calls to await in another async method:
async Task GetAndInsert(string key, Func<Task<IList<model>>> getFunc)
=> await database.InsertData(key, await getFunc());
Then each can be run simultaneously:
var taskslist = new List<Task>();
taskslist.Add(GetAndInsert("data1", service.GetData1));
taskslist.Add(GetAndInsert("data2", service.GetData2));
taskslist.Add(GetAndInsert("data3", service.GetData3));
await Task.WhenAll(taskslist);
If you don't need to use the Tasks after they have completed, you could even pass them straight to Task.WhenAll rather than creating a list:
await Task.WhenAll(
GetAndInsert("data1", service.GetData1),
GetAndInsert("data2", service.GetData2),
GetAndInsert("data3", service.GetData3));

Related

Process.all(array.map(... doesn't work in parallel with page.goto(

I am using the pupperteer library for my bot and I would like to perform some operations in parallel.
In many articles, it is advised to use this syntax :
await Promise.all(array.map(async data => //..some operations))
I've tested this on several operations and it works but when I embed the code below in my .map promise
await page.goto(..
It did not work during Operation Promise and it considers this to be a synchronous operation.
I would like to know why it reacts like this?
I believe your error comes from the fact that you're using the same page object.
The following should work:
const currentPage = browser.pages().then(allPages => allPages[0]);
const anotherPage = await browser.newPage();
const bothPages = [currentPage, anotherPage];
await Promise.all(
bothPages.map(page => page.goto("https://stackoverflow.com"))
);

How do I save a storage file to a location that the user chooses in UWP?

I'm opening a file via:
await Windows.System.Launcher.LaunchFileAsync(storageFile, options);
The documentation for LaunchFileAsync says:
When the launch fails for any of the above reasons, the API succeeds
and returns FALSE from its asynchronous operation. Since it has no
ability to query whether the above restrictions apply to the current
launch, the calling app should not assume that the launch succeeded,
and should provide fallback mechanism in case it failed. A possible
solution would be to ask the user to save the file and direct the user
to open it in the desktop.
What's the most straightforward way to do that?
I tried:
var picker = new FolderPicker();
var pfolder = await picker.PickSingleFolderAsync();
StorageApplicationPermissions.FutureAccessList.Add(pfolder);
var folder = await StorageFolder.GetFolderFromPathAsync(pfolder.Path);
var file = await folder.CreateFileAsync(storageFile.Name);
using (var writer = await file.OpenStreamForWriteAsync())
{
await writer.WriteAsync(storageFile,0,0);
}
But unfortuantely writer.WriteAsync only takes Bytes[] and not the StorageFile. How do I get my StorageFile saved?

C# async calls and realm instances

I am using Realm with a Xamarin Forms project, and I have read about how realm entity instances can't be shared across threads.
Given the following code, is using the route obtained in line 100, and then accessed again on line 109 after the awaited call on 104, dangerous?
I am new to using Realm, but if this is true, then one must get a new instance of the Realm and any object being worked with after any/every awaited call. Seems onerous...
is using the route obtained in line 100, and then accessed again on line 109 after the awaited call on 104, dangerous?
Yes, on the next foreach iteration, you will end up with a different managed thread, and Realm will throw an different thread access exception.
The key is to use a SynchronizationContext so your await continuations are on the same thread (and, of course, since you will be in a different thread, skip the use of the Realm-based async methods)
Using Stephen Cleary's Nito.AsyncEx (he is the king of sync contexts 😜)
re: how can i force await to continue on the same thread?
var yourRealmInstanceThread = new AsyncContextThread();
await yourRealmInstanceThread.Factory.Run(async () =>
{
var asyncExBasedRealm = Realm.GetInstance();
var routes = asyncExBasedRealm.All<UserModel>();
foreach (var route in routes)
{
// map it
// post it
await Task.Delay(TimeSpan.FromMilliseconds(1)); // Simulate some Task, i.e. a httpclient request....
// The following continuations will be executed on the proper thread
asyncExBasedRealm.Write(() => route.Uploaded = true);
}
});
Using SushiHangover.RealmThread
I wrote a simple SynchronizationContext for Realm awhile back, it works for my needs and has a specialized API for Realm.
using (var realmThread = new RealmThread(realm.Config))
{
await realmThread.InvokeAsync(async myRealm =>
{
var routes = myRealm.All<UserModel>();
foreach (var route in routes)
{
// map it
// post it
await Task.Delay(TimeSpan.FromMilliseconds(1));
// The following continuations will be executed on the proper thread
myRealm.Write(() => route.Uploaded = true);
}
});
}
Note: For someone that does not understand SynchronizationContext well, I would highly recommend using Nito.AsyncEx as a generic solution that is well supported and due to the fact that is from Stephen Cleary... I use it in a vast majority of my projects.

Using ResumeAfter in proactive bot dialog

I have a couple of questions about the example that shows how to start a proactive dialog:
using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, activity))
{
var botData = scope.Resolve<IBotData>();
await botData.LoadAsync(token);
var task = scope.Resolve<IDialogTask>();
var interruption = dialog.Void<T, IMessageActivity>();
task.Call(interruption, null);
await task.PollAsync(token);
await botData.FlushAsync(token);
}
What is the point of calling dialog.Void?
How can I use a ResumeAfter? If i add a ResumeAfter handler and await the result i get an error indicating it was expecting Call and got Poll
Is this code supposed to block until the dialog is complete? Because it does not
How can I push a proactive dialog and await its result?

How do I use async and await

So here is the basic of what I am trying to do...
I have create a Web site that has web API 2 so I can do crud operations to that site.
I am trying to create the client side application in VS 2013. I have created a basic Console apps to start the project.
class PortalReposotry
{
private Uri _uri;
public PortalReposotry()
{
_uri = new Uri("http://localhost:21564/");
}
public async Task<CompanyAPI> GetCompany(string companyCode)
{
var client = new HttpClient();
client.BaseAddress = _uri;
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await client.GetAsync(String.Format("api/companies/{0}", companyCode));
CompanyAPI content = await response.Content.ReadAsAsync<CompanyAPI>();
return await Task.Run(() => content);
}
}
class Program
{
static void Main(string[] args)
{
PortalReposotry repo = new PortalReposotry();
CompanyAPI comp = repo.GetCompany("LNCR");
Console.ReadKey();
}
}
I have no Idea why I have to use a lamda expression to return the CompanyAPI object.
All I want returned in the CompanyAPI object not the task it is running on. I am very confused on the threading and why I have to run this under task... it makes where I have to wrap everything into a task.... Which I'll be honest I have no idea how to use or decuple the actual objects from it that I want.
If you can help me out, I could be going the wrong direction all together but this is what I have found so far.
I am very confused on the threading and why I have to run this under task
You don't have to use threading here (the Task.Run is unnecessary). The Task<T> type is a "future" - it represents an asynchronous operation that will have a result value of type T in the future. That's why you need to use tasks with asynchronous code (technically, you could use callbacks instead, but that's much more painful - tasks are easier).
I have created a basic Console apps to start the project.
Asynchronous console apps are a bit weird. You need to block the main thread so the application doesn't exit. This is unnecessary in a real UI client-side app (which I assume your sample project will eventually become). So for now you can just do a hack like this:
static void Main(string[] args)
{
MainAsync().Wait();
}
static async Task MainAsync()
{
PortalReposotry repo = new PortalReposotry();
CompanyAPI comp = await repo.GetCompanyAsync("LNCR");
Console.ReadKey();
}
The call to Wait() should only be done inside a Console app's Main method. You shouldn't ever call it in a UI application.
Since Task.Run isn't needed, you can clean up that method, too:
public async Task<CompanyAPI> GetCompanyAsync(string companyCode)
{
var client = new HttpClient();
client.BaseAddress = _uri;
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await client.GetAsync(String.Format("api/companies/{0}", companyCode));
return await response.Content.ReadAsAsync<CompanyAPI>();
}
in your GetCompany method, the await and Task.Run is unnecessary, you can just return the content.
public async Task<CompanyAPI> GetCompany(string companyCode)
{
var client = new HttpClient();
client.BaseAddress = _uri;
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await client.GetAsync(String.Format("api/companies/{0}", companyCode));
CompanyAPI content = await response.Content.ReadAsAsync<CompanyAPI>();
return content
}
then in your main method you would just call it like so. Disclaimer,
this will make it run synchronously. You can use the ContinueWith method of the Task object to add a "callback" to the task. https://msdn.microsoft.com/en-us/library/system.threading.tasks.task.continuewith(v=vs.110).aspx
CompanyAPI comp = repo.GetCompany("LNCR").Result;

Resources