I'm trying to make it possible for a client to make several searches at the same time without it affecting the response time of the seach page. To do this i was hoping to use .NET 4.5's task-based operations in WCF but I'm having troubles understanding the logic.
What I want my end result to be is to do multiple calls to the WCF API and then merge these into one big result and order it by each search items relevence.
This is how my code looks like today:
public partial class WebForm1 : System.Web.UI.Page
{
private static SwepubSearchServiceClient _client;
private static List<SearchItem> _searchItems = new List<SearchItem>();
protected void Page_Load(object sender, EventArgs e)
{
_client = new SwepubSearchServiceClient();
var result1 = GetAsyncSearchItems("query1", "subQuery1");
var result2 = GetAsyncSearchItems("query2", "subQuery2");
var result3 = GetAsyncSearchItems("query3", "subQuery3");
// What to do here?
}
private static async Task<SearchItem[]> GetAsyncSearchItems(string query = "", string subQuery = "")
{
var task = _client.DoSearchSimpleAsync(query, subQuery);
return await task;
}
}
Basicly, what I need to know is what to do with each result (result1, result2, result3) after i get them back. I though i was going to be able to call result1.Result after each method call but by then they aren't yet computed so it won't do any good.
While I was trying stuff before my GetAsyncSearchItems method looked something like this:
private static async void GetAsyncSearchItems()
{
var task1 = _client.DoSearchSimpleAsync("query1", "subQuery1");
var task2 = _client.DoSearchSimpleAsync("query2", "subQuery2");
var task3 = _client.DoSearchSimpleAsync("query3", "subQuery3");
var result1 = await task1;
var date1 = DateTime.Now.TimeOfDay;
var result2 = await task2;
var date2 = DateTime.Now.TimeOfDay;
var result3 = await task3;
var date3 = DateTime.Now.TimeOfDay;
var list = new List<SearchItem>();
list.AddRange(result1);
list.AddRange(result2);
list.AddRange(result3);
}
When i did this i was able to retrieve the result in the list object but I wasn't able to return it to my Page_Load since async method can only return void, Task or Task so it was of no use.
Or am I misunderstanding something? Should I work inside the static method with the result?
I'm probobly missing something basic here but I'm hoping someone can stear me in the right direction. I would really apreachiate it!
You can do it like this:
var task1 = _client.DoSearchSimpleAsync("query1", "subQuery1");
var task2 = _client.DoSearchSimpleAsync("query2", "subQuery2");
var task3 = _client.DoSearchSimpleAsync("query3", "subQuery3");
await Task.WhenAll(task1, task2, task3);
var result1 = task1.Result;
var date1 = DateTime.Now.TimeOfDay;
var result2 = task2.Result;
var date2 = DateTime.Now.TimeOfDay;
var result3 = task3.Result;
var date3 = DateTime.Now.TimeOfDay;
The ".NET 4.5 way" of doing it, as discussed in this Q&A, is to make your Page_Load method async. Then you can call your async method, and ASP.NET will do the rest towards the client:
protected async void Page_Load(object sender, EventArgs e)
{
...
var results = await GetAsyncSearchItems(...)
... do something with the results
}
private static async SearchItem[] GetAsyncSearchItems()
{
... await stuff
return list;
}
More resources:
The Magic of using Asynchronous Methods in ASP.NET 4.5 plus an important gotcha - blog post by Scott Hanselman
Here's a video tutorial on Async in ASP.NET given in that answer I linked to.
You need to add your three results, result1, result2 and result3 to an array of Tasks.
Then you can do this:
Task.WaitAll(tasks);
where tasks is the array.
http://msdn.microsoft.com/en-us/library/dd270695(v=vs.110).aspx
After the WaitAll completes, you can retrieve the lists of SearchItems from each task.
Your GetAsyncSearchItems method should look like this (following the Task-based Asynchronous Pattern):
private static async Task<List<SearchItem>> GetSearchItemsAsync()
{
var task1 = _client.DoSearchSimpleAsync("query1", "subQuery1");
var task2 = _client.DoSearchSimpleAsync("query2", "subQuery2");
var task3 = _client.DoSearchSimpleAsync("query3", "subQuery3");
var results = await Task.WhenAll(task1, task2, task3);
var list = new List<SearchItem>();
list.AddRange(results[0]);
list.AddRange(results[1]);
list.AddRange(results[2]);
return list;
}
Once you have that method, you can write Page_Load as such:
protected async void Page_Load(object sender, EventArgs e)
{
_client = new SwepubSearchServiceClient();
var results = await GetSearchItemsAsync();
...
}
While async void works in this scenario, the ASP.NET team prefers you use RegisterAsyncTask, as such:
protected void Page_Load(object sender, EventArgs e)
{
_client = new SwepubSearchServiceClient();
RegisterAsyncTask(new PageAsyncTask(PageLoadAsync));
}
private async Task PageLoadAsync()
{
var results = await GetSearchItemsAsync();
...
}
You may find my async intro helpful.
Related
Let's imagine some abstract code
private void Main()
{
var workTask1 = DoWork1();
var workTask2 = DoWork2();
var workTask3 = DoWork3();
await Task.WhenAll(workTask1, workTask2, workTask3);
AnalyzeWork(workTask1.Result, workTask2.Result, workTask3.Result);
}
private async Task<object> DoWork1()
{
var someOperationTask1 = someOperation1();
var someOperationTask2 = someOperation2();
await Task.WhenAll(someOperationTask1, someOperationTask2);
return new object
{
SomeOperationResult1 = someOperationTask1.Result,
SomeOperationResult2 = someOperationTask2.Result,
};
}
private async Task<object> DoWork2()
{
var someOperationTask3 = someOperation3();
var someOperationTask4 = someOperation4();
await Task.WhenAll(someOperationTask3, someOperationTask4);
return new object
{
SomeOperationResult3 = someOperationTask3.Result,
SomeOperationResult4 = someOperationTask4.Result,
};
}
private async Task<object> DoWork3()
{
var someOperationTask5 = someOperation5();
var someOperationTask6 = someOperation6();
await Task.WhenAll(someOperationTask5, someOperationTask6);
return new object
{
SomeOperationResult5 = someOperationTask5.Result,
SomeOperationResult6 = someOperationTask6.Result,
};
}
Where 3 methods are being run parallelly and each of them consists of 2 parallel's operations. And result of 3 methods is passed to some method.
My question is there are any restrictions? Is it ok to have nested Task.WhenAll and what's difference between nested Task.WhenAll and one level Task.WhenAll operations?
The only restrictions are the available memory of your system. The Task.WhenAll method attaches a continuation to each incomplete task, and this continuation is detached when that task completes. A continuation is a lightweight object similar to a Task. It's quite similar to what you get when you invoke the Task.ContinueWith method. Each continuation weights more or less about 100 bytes. It is unlikely that it will have any noticeable effect to your program, unless you need to Task.WhenAll tens of millions of tasks (or more) at once.
If you want a visual demonstration of what this method looks like inside, below is a rough sketch of its implementation:
// For demonstration purposes only. This code is full of bugs.
static Task WhenAll(params Task[] tasks)
{
var tcs = new TaskCompletionSource();
int completedCount = 0;
foreach (var task in tasks)
{
task.ContinueWith(t =>
{
completedCount++;
if (completedCount == tasks.Length) tcs.SetResult();
});
}
return tcs.Task;
}
I'm trying to send a HTTP POST request multiple times but not in parallel. I've created another class and put this function into it:
public async Task<string> sendReqAsync()
{
requestTime = DateTime.Now;
var task = await client.PostAsync("https://api.example.com", content).ContinueWith(async (result) =>
{
responseTime = DateTime.Now;
return await result.Result.Content.ReadAsStringAsync();
});
return task.Result;
}
And finally it'll be called in an click event for a button:
private async void ManualSendBtn_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
// Here "client" is object of my class that have been initilized in the main WPF function
await client.sendReqAsync().ContinueWith((result) =>
{
var parsedResponse = JObject.Parse(result.Result);
this.Dispatcher.Invoke(() =>
{
ordersListView.Items.Add(new OrderDetailsItem
{
IsSuccessful = bool.Parse(parsedResponse["IsSuccessfull"].ToString()),
ServerMessage = parsedResponse["MessageDesc"].ToString(),
RequestTime = client.requestTime.ToString("hh:mm:ss.fff"),
ResponseTime = client.responseTime.ToString("hh:mm:ss.fff"),
ServerLatency = (client.responseTime - client.requestTime).Milliseconds.ToString()
});
});
});
}
But it works just for the first click (event occurs) and for next click it gives me:
Inner Exception 1: AggregateException: One or more errors occurred.
Inner Exception 2: AggregateException: One or more errors occurred.
Inner Exception 3: ObjectDisposedException: Cannot access a disposed
object.
Edit: Below is the edited code as suggested, but the error is not fixed.
MyHTTPClient
public class MyHTTPClient
{
private static readonly HttpClient client = new HttpClient();
StringContent content;
public DateTime requestTime;
public DateTime responseTime;
public void configureReq(string oauthToken)
{
var postBodyJSONObject = new
{
body = "value"
};
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", oauthToken);
content = new StringContent(JsonConvert.SerializeObject(postBodyJSONObject), Encoding.UTF8, "application/json");
}
public async Task<string> sendReqAsync()
{
requestTime = DateTime.Now;
var result = await client.PostAsync("https://example.com", content);
responseTime = DateTime.Now;
return await result.Content.ReadAsStringAsync();
}
}
Also here is my window initializer function:
MyHTTPClient client; // Somewhere in the class
public MainWindow() {
client = new MyHTTPClient();
client.configureReq(AUTHORIZATION);
}
First of all: You shouldn't mix different asynchronous models of programming.
In your code you are trying to combine async/await with legacy ContinueWith approach.
You can read more about async/await approach here and about async/await and ContinueWith differences here (Thanks to MickyD for leaving link to this in the comments).
So to cut a long story short your code should look like this:
public async Task<string> SendRequestAsync()
{
requestTime = DateTime.Now;
var response = await client.PostAsync("api.example.com", content);
var responseTime = DateTime.Now;
return await response.Content.ReadAsStringAsync();
}
Note that now code looks much more like synchronous and is a way readable than callbacks with ContinueWith.
You can find more about asynchronous programming in C# on MSDN. I suggest you to read and try to exectue some code in console app before writing it in yourmain app.
In addition to fixing the SendRequestAsync method, as shown by Neistow's answer, you must fix the button's click handler too:
private async void ManualSendBtn_PreviewMouseLeftButtonUp(object sender,
MouseButtonEventArgs e)
{
string response = await client.SendRequestAsync();
var parsedResponse = JObject.Parse(response);
ordersListView.Items.Add(new OrderDetailsItem
{
IsSuccessful = bool.Parse(parsedResponse["IsSuccessfull"].ToString()),
ServerMessage = parsedResponse["MessageDesc"].ToString(),
RequestTime = client.RequestTime.ToString("hh:mm:ss.fff"),
ResponseTime = client.ResponseTime.ToString("hh:mm:ss.fff"),
ServerLatency = (client.ResponseTime - client.RequestTime).Milliseconds.ToString()
});
}
There is no reason to mess with the Dispatcher.Invoke. The code after the await will continue running on the UI thread, as it happens by default in all Windows Forms and WPF applications.
I wrote a visual studio 2015 c# program for reading from a custom Ethernet device some data. I need to use async await instructions because the data will be read all together on scheduled times.
I use a custom .NET library for read data; this is my code:
private void timerPollingData_Tick(object sender, EventArgs e)
{
readDevice_01();
readDevice_02();
// and so on for all devices ...
}
private async void readDevice_01()
{
var result = await getDataDevice_01();
// Save data
}
private async void readDevice_02()
{
var result = await getDataDevice_02();
// Save data
}
private Task<string> getDataDevice_01()
{
MyCustomLibrary readDevice = new MyCustomLibrary.Master();
return Task.Factory.StartNew(() => readDevice.ReadHoldingRegister(... some parameters ...).ToString());
}
private Task<string> getDataDevice_02()
{
MyCustomLibrary readDevice = new MyCustomLibrary.Master();
return Task.Factory.StartNew(() => readDevice.ReadHoldingRegister(... some parameters ...).ToString());
}
My doubt:
what is the best practice for handle exception of each Task? I need to understand what devices are unplug from Ethernet or switch off and then STOP the TASK used to retrieve data from it.
Thanks a lot in advance for your help.
You should avoid async void; use async Task for everything except async event handlers. For more information, see my article on async best practices.
Also, you should not use StartNew; it's a low-level, very dangerous API with inappropriate default parameter values. Use Task.Run instead of StartNew. For more information, see my blog post on StartNew is dangerous.
I need to use async await instructions because the data will be read all together on scheduled times.
Asynchrony is one form of concurrency, which you can use with Task.Run if your device API does not have asynchronous methods:
private async void timerPollingData_Tick(object sender, EventArgs e)
{
var task1 = readDevice_01();
var task2 = readDevice_02();
// and so on for all devices ...
await Task.WhenAll(task1, task2, ...);
}
private async Task readDevice_01()
{
var result = await Task.Run(() => getDataDevice_01());
// Save data
}
private string getDataDevice_01()
{
MyCustomLibrary readDevice = new MyCustomLibrary.Master();
return readDevice.ReadHoldingRegister(... some parameters ...).ToString();
}
If your API had a ReadHoldingRegisterAsync method, then this would be more naturally expressed as:
private async void timerPollingData_Tick(object sender, EventArgs e)
{
var task1 = readDevice_01Async();
var task2 = readDevice_02Async();
// and so on for all devices ...
await Task.WhenAll(task1, task2, ...);
}
private async Task readDevice_01Async()
{
var result = await getDataDevice_01Async();
// Save data
}
private async Task<string> getDataDevice_01Async()
{
MyCustomLibrary readDevice = new MyCustomLibrary.Master();
var result = await readDevice.ReadHoldingRegisterAsync(... some parameters ...);
return result.ToString();
}
My doubt: what is the best practice for handle exception of each Task? I need to understand what devices are unplug from Ethernet or switch off and then STOP the TASK used to retrieve data from it.
The best practices for getting exceptions from tasks are to await them.
You don't have to worry about stopping a task after it raised an exception. By the time the task reports its exception, it has already stopped.
I am trying to figure out how to use async/await correctly in the Data Layer of my application to do multiple database calls in parallel.
I am using Entity Framework and Linq to do my data calls.
The Data Layer can be called from any application type (MVC Web App, Web API Project, Win From, Windows Service etc.) - therefore the result of the method should be a single object.
public static async void GetLedger(long id, DateTime StartDate, DateTime EndDate)
{
var task_1 = DoComplexDBCallAsync1();
var task_2 = DoComplexDBCallAsync2();
var task_3 = DoComplexDBCallAsync3();
var task_4 = DoComplexDBCallAsync4();
var task_5 = DoComplexDBCallAsync5();
await Task.WhenAll(task_1, task_2, task_3, task_4, task_5);
var result = ProcessAndMergeResultsFromAllDBCalls(task_1.Result, task_2.Result, task_3.Result, task_4.Result, task_5.Result);
return result;
}
private static async Task<List<SomeComplexType>> DoComplexDBCallAsync1(long id)
{
using (var metadata = DataAccess.getDesktopMetadata())
{
var accounts = await (from row in metadata.db_GLAccount
where row.busID == id
select row).ToListAsync();
return accounts;
}
}
How would I change the above to return a single object instead of a Task<>?
Would I need to wrap it in another method which calls the GetLedger async method via a Task run or RunSynchronously() and returns the results?
If so how would the code look and is there anything I need to be aware of?
What is best practice in this scenario?
You would use await as follows
public static async Task<SomeObject> GetLedger(long id, DateTime StartDate, DateTime EndDate)
{
var task_1 = DoComplexDBCallAsync1();
var task_2 = DoComplexDBCallAsync2();
var task_3 = DoComplexDBCallAsync3();
var task_4 = DoComplexDBCallAsync4();
var task_5 = DoComplexDBCallAsync5();
var result = ProcessAndMergeResultsFromAllDBCalls(await task_1, await task_2, await task_3, await task_4, await task_5);
return result;
}
The 'await' keyword will automatically get the result of the Task and apply it to the variables when the DB calls return. MSDN
You would then call
var result = await GetLedger(id, StartDate, EndDate);
which would proceed after everything is complete.
I wonder if there is a code there that refresh my DataServiceCollection that is loaded in a WPF client using BeginExecute async await Task as show below:
public static async Task<IEnumerable<TResult>> ExecuteAsync<TResult>(this DataServiceQuery<TResult> query)
{
//Thread.Sleep(10000);
var queryTask = Task.Factory.FromAsync<IEnumerable<TResult>>(query.BeginExecute(null, null), (asResult) =>
{
var result = query.EndExecute(asResult).ToList();
return result;
});
return await queryTask;
}
I call the extension method as the following:
public async void LoadData()
{
_ctx = new TheContext(the URI);
_dataSource = new DataServiceCollection<TheEntity>(_ctx);
var query = _ctx.TheEntity;
var data = await Task.Run(async () => await query.ExecuteAsync());
_dataSource.Clear(true);
_dataSource.Load(data);
}
LoadData is Called in a ViewModel
Change a field value by hand using SQL Management Studio
Call LoadData again <<<< Does not refresh!!!
Meanwhile, If I use Load method the data is refreshed without any problems as:
var query = _ctx.TheEntity;
_dataSource.Load(query);
Another issue is that I do not know how to Cancel client changes. Last question is whether the MergeOption has any effect to BeginExecute..EndExecute or it only works with Load method?
I suspect that the data context does not like being accessed from multiple threads.
I recommend that you first remove all processing from your extension method:
public static Task<IEnumerable<TResult>> ExecuteAsync<TResult>(this DataServiceQuery<TResult> query)
{
return Task.Factory.FromAsync(query.BeginExecute, query.EndExecute, null);
}
Which you should use without jumping onto a background thread:
public async Task LoadDataAsync()
{
_ctx = new TheContext(the URI);
_dataSource = new DataServiceCollection<TheEntity>(_ctx);
var query = _ctx.TheEntity;
var data = await query.ExecuteAsync();
_dataSource.Clear(true);
_dataSource.Load(data);
}