Wrap a method to wait for callback - c#

I have client code (ViewModeL) that is calling a ConnectedThingService that is calling a CoapService to update the mode on a "thing". The update method on the CoapService returns a task and has parameters for success and error responses.
Task<Task> SetModeFrom(DeviceModel Device, CoAPResponseReceivedHandler responseHandler, CoAPErrorHandler errorHandler);```
CoAPResponseRecivedHandler and CoAPErrorHandler are delegates.
Here is the code that calls the CoapService:
public async Task<ApiResponse> UpdateModeAsync(int mode)
{
ApiResponse result = new ApiResponse();
await _coapService.SetControlModeFromCombo(device, (updateResp) =>
{
if (updateResp.Code.Value != CoAPMessageCode.CONTENT)
{
result.IsSuccessful = false;
result.ErrorMessages.Add("Failed setting mode");
}
else
{
var coapResponse =
JsonConvert.DeserializeObject<ControlModel>(updateResp.GetPayload());
result.IsSuccessful = true;
Mode = mode;
}
}, (e, associatedMsg) =>
{
result.IsSuccessful = false;
result.ErrorMessages.Add("Failed setting mode");
});
return result;
The problem is the call is returning before either of the delegates are firing and therefore I'm always returning false (the default) in the ApiResponse. Is there a way to make this call and wait for either of the 2 delegates to get called before returning back to the client, and do so in a way that is not blocking? And is it possible to have a timeout so that if neither of the 2 delegates fire after a timeout period, it returns false?
Edit: I inherited this code. The lowest level code is from CoAPSharp.com. Here is the code for SetControlModeFromCombo:
public async Task<Task> SetControlModeFromCombo(DeviceModel Device,
CoAPResponseReceivedHandler responseHandler, CoAPErrorHandler
errorHandler)
{
ushort msgID = Convert.ToUInt16(new Random().Next(1000, 9999));
var task = new TaskCompletionSource<DeviceModel>();
if (!string.IsNullOrWhiteSpace(Device.CODE))
{
var controlSettings = new ControlModel()
{
Mode = Device.ComboSettings.Mode,
};
await _coap.MakeRequest(Device.CODE, JsonConvert.SerializeObject(controlSettings.ControlMode()), Device.IPADDRESS, ControlModel.Endpoint, CoAPMessageType.CON, CoAPMessageCode.PUT, msgID, (CoAPResponse response) =>
{
responseHandler(response);
task.SetResult(Device);
}, (Exception e, AbstractCoAPMessage associatedMsg) =>
{
errorHandler(e, associatedMsg);
});
}
else
{
ThrowMissingCodeError(task);
}
return task.Task;
}
And here is the MakeRequest Method:
public async Task MakeRequest(string token, string outboundIP, string uri, byte type, byte code, ushort id, CoAPResponseReceivedHandler responseHandler, CoAPErrorHandler errorHandler)
{
try
{
_coapClient = new CoAPClientChannel();
await _coapClient.Initialize(outboundIP, _outboundPort);
_coapClient.CoAPResponseReceived += responseHandler;
_coapClient.CoAPError += errorHandler;
await _coapClient.Send(AssembleRequest(type, code, id, outboundIP, uri, token), outboundIP);
}
catch (Exception e)
{
Debug.WriteLine(e);
}
}

Related

How to run looping task untill other task is finished

I found many questions addressing how to sequence tasks and waiting until all tasks finish, but with this topic, I found only 1 question from 2016 with no answers.
I'm processing a large text file in my project and I want to indicate that this process is running with the text being displayed with changing number of dots after the "Processing" text. I got to the point, where the intended looping task is working until a long working task finishes and the proper field in the VM is updated, but I can't make looping task to be delayed so dots are changing in the way it's seen.
In other words - the same functionality as when a loader is displayed while data are being retrieved from the HTTP request.
public void SetRawTextFromAbsPath(string path)
{
if (!File.Exists(path))
{
return;
}
var rawText = "Processing";
bool IsReadingFileFinished = false;
Task<string> getRawTextFromAbsPath = Task.Run(() => {
var result = FileProcessingServices.GetRawFileText(path);
IsReadingFileFinished = true;
return result;
});
Task updateProgressText = Task.Run(async () =>
{
while (!IsReadingFileFinished)
{
rawText = await Task.Run(() => ProcessingTextChange(rawText));
SelectedFileRaw = rawText;
}
});
Task.WaitAll(getRawTextFromAbsPath, updateProgressText);
SelectedFileRaw = completeRawText.Result;
}
public string ProcessingTextChange(string text)
{
Task.Delay(100);
var dotsCount = text.Count<char>(ch => ch == '.');
return dotsCount < 6 ? text + "." : text.Replace(".", "");
}
After learning from all the answers, I come up with this solution:
private const string PROGRESS = "Progress";
private const int PROGRESS_DELAY = 200;
public async void RunProgressTextUpdate()
{
var cts = new CancellationTokenSource();
if (!IsRunning)
{
UpdateProgressTextTask(cts.Token);
string longTaskText = await Task.Run(() => LongTask(cts));
await Task.Delay(PROGRESS_DELAY);
ProgressText = longTaskText;
}
}
private void UpdateProgressTextTask(CancellationToken token)
{
Task.Run(async () =>
{
ProgressText = PROGRESS;
while (!token.IsCancellationRequested)
{
await Task.Delay(PROGRESS_DELAY);
var dotsCount = ProgressText.Count<char>(ch => ch == '.');
ProgressText = dotsCount < 6 ? ProgressText + "." : ProgressText.Replace(".", "");
}
});
}
private string LongTask(CancellationTokenSource cts)
{
var result = Task.Run(async () =>
{
await Task.Delay(5000);
cts.Cancel();
return "Long task finished.";
});
return result.Result;
}
Every way of creating Task and running them is overloaded to expect a CancellationToken. CancellationTokens are, unsurprinsignly, structs that allows us to cancel Tasks.
Having this two methods
public void DelayedWork()
{
Task.Run(async () =>
{
// Simulate some async work
await Task.Delay(1000);
});
}
public void LoopingUntilDelayedWorkFinishes()
{
Task.Run(() =>
{
int i = 0;
// We keep looping until the Token is not cancelled
while (true) // May be?
{
Console.WriteLine($"{++i} iteration ...");
}
});
}
We want LoopingUntilDelayedWorkFinishes to stop looping when DelayedWork finishes (well, naming was quite obvious).
We can provide a CancellationToken to our LoopingUntilDelayedWorkFinishes method. So it will keep looping until it is cancelled.
public void LoopingUntilDelayedWorkFinishes(CancellationToken token)
{
Task.Run(() =>
{
int i = 0;
// We keep looping until the Token is not cancelled
while (!token.IsCancellationRequested)
{
Console.WriteLine($"{++i} iteration ...");
}
}, token); // This is the overload expecting the Token
}
Okay, working. We can control this CancellationToken by extracting from a CancellationTokenSource, which controls its CancellationToken.
var cts = new CancellationTokenSource();
p.LoopingUntilDelayedWorkFinishes(cts.Token);
And now, we need our DelayedWork to cancel the token when it finishes.
public void DelayedWork(CancellationTokenSource cts)
{
Task.Run(async () =>
{
// Simulate some async work
await Task.Delay(1000);
// Once it is done, we cancel.
cts.Cancel();
});
}
That is how we could call the methods.
var cts = new CancellationTokenSource();
p.DelayedWork(cts);
p.LoopingUntilDelayedWorkFinishes(cts.Token);
The call order between DelayedWork and LoopingUntilDelayedWorkFinishes is not that important (in that case).
Maybe LoopingUntilDelayedWorkFinishes can return a Task and the await for it later on, I don't know. I just depends on our needs.
There are tons of ways to achieve this. The environment arround Task is so bast and the API is quite confusing sometimes.
Here's how you could do it. Maybe some smart use of async/await syntax would improve the solution I gave. But, here's the main idea.
Hope it helps.

Making parallel api calls and handling each response asynchronously in c#

I have a scenario where I need to make multiple api calls (same api with different parameters) parallely in c# (Xamarin iOS and Xamarin Android). And I don't want to wait for all tasks to complete, instead whenever a response comes I should process it and update the UI accordingly.
Method that needs to be called multiple times
public async Task<Response> GetProductsAsync(int categoryId, int pageNo = -1, int pageSize = -1)
{
try
{
string url = "";
if (pageNo == -1 || pageSize == -1)
url = $"catalog/v1/categories/{categoryId}/products";
else
url = $"catalog/v1/categories/{categoryId}/products?page-number={pageNo}&page-size={pageSize}";
var response = await client.GetAsync(url);
string responseString = await response.Content.ReadAsStringAsync();
GetParsedData(response.IsSuccessStatusCode, responseString);
}
catch (Exception e)
{
apiResponse.status = "internalError";
apiResponse.data = e.Message;
}
return apiResponse;
}
From calling function you may write the code as below
public void CallingFunctionToGetProductsAsync() {
Task.Run(async () =>
{
var response = await GetProductsAsync(1);
ProcessResponse(response);
});
Task.Run(async () =>
{
var response = await GetProductsAsync(2);
ProcessResponse(response);
});
}
This is how you can wait for multiple tasks asynchron and update the UI whenever any of them completes.
async Task GetSomeProductsAsync( IEnumerable<int> categoryIds )
{
List<Task<Response>> tasks = categoryIds
.Select( catId => GetProductsAsync( catId ) )
.ToList();
while ( tasks.Any() )
{
var completed = await Task.WhenAny( tasks );
tasks.Remove( completed );
var response = completed.Result;
// update the ui from this response
}
}
As a side note:
You should add ConfigureAwait(false) to your awaiting code in GetProducsAsync to avoid uneccessary sync with the caller thread (wich would be the UI here)
public async Task<Response> GetProductsAsync(int categoryId, int pageNo = -1, int pageSize = -1)
{
try
{
string url = "";
if (pageNo == -1 || pageSize == -1)
url = $"catalog/v1/categories/{categoryId}/products";
else
url = $"catalog/v1/categories/{categoryId}/products?page-number={pageNo}&page-size={pageSize}";
var response = await client.GetAsync(url).ConfigureAwait(false);
string responseString = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
GetParsedData(response.IsSuccessStatusCode, responseString);
}
catch (Exception e)
{
apiResponse.status = "internalError";
apiResponse.data = e.Message;
}
return apiResponse;
}
You can read more about it in Stephen Cleary's blog article: Don't block on Async Code

Function returning before awaited Task has completed

The order of async-await operations in this bit of code is a bit off. The await Task inside DownloadMetasBySortAsync isn't behaving like I thought it should. The task completes itself, but only after DownloadMetasBySortAsync has already returned null to DownloadMetasAsync.
I've tried adding Task.Wait() on the tasks in DownloadMetasAsync and the offending, awaited task. I tried ConfigureAwait(). DownloadMetasBySortAsync always kicks back null before the needed task has been finished.
What am I missing?
public async Task<List<MyMeta>> DownloadMetasAsync(SortType sortType)
{
ResetFlags();
_cloudMetas = await DownloadMetasBySortAsync(sortType);
Debug.Log((_cloudMetas != null) ? _cloudMetas.Count + " metas downloaded" : "NULL cloudmetas");
return _cloudMetas;
}
private async Task<List<MyMeta>> DownloadMetasBySortAsync(SortType sortType)
{
//Load Table
Table dbTable = null;
//Can't call LoadTable from main thread
await Task.Run(() => {
try {
dbTable = Table.LoadTable(DBClient, new TableConfig(_databaseName));
}catch( Exception ex ) {
_operationFailed = true;
ThrowError(ex);
}
});
if(dbTable == null ) { return null; }
//Set up secondary local index if needed
string sortIndex = (sortType == SortType.Creator) ? "date-creator-index" : null;
return await ScanTable(_dbName, sortIndex); //Scan table for list of metas
}
private async Task<List<MyMeta>> ScanTable(string dbName, string index)
{
List<MyMeta> metaList = null;
try {
Dictionary<string,AttributeValue> lastKeyEvaluated = null;
do {
var request = new ScanRequest
{
TableName = dbName,
IndexName = index,
ConsistentRead = true,
ExclusiveStartKey = lastKeyEvaluated
};
Debug.Log("Scanning...");
await Task.Run(() =>
{
DBClient.ScanAsync(request, (responseObj) =>
{
if( responseObj == null ) {
Debug.LogWarning("Response NULL");
}
else {
Debug.Log("Response received");
if(metaList == null ) { metaList = new List<MyMeta>(); }
var resultList = ProcessScanResult(responseObj.Response);
if( resultList != null && resultList.Count > 0 ) {
metaList.AddRange(resultList);
}
}
lastKeyEvaluated = responseObj.Response.LastEvaluatedKey;
});
});
} while( lastKeyEvaluated != null && lastKeyEvaluated.Count != 0 );
}
catch( Exception ex ) {
ThrowError(ex);
}
return metaList;
}
Since the DBClient.ScanAsync is not an async/await method but a callback type of async method it will return immediately after being called and the results will only come later. To handle this you can use TaskCompletionSource to make it into awaitable thing:
var task = new TaskCompletionSource<responseObjType>();
DBClient.ScanAsync(request, (responseObj) =>
{
task.SetResult(responseObj);
}
// Will wait for the callback to be called before continuing and get the results
var responseObj = await task.Task;
if( responseObj == null )
{
...
This way the method will wait until the callback is called, the data is sent back via the TaskCompletionSource object and your code can process it further. This assumes you don't specifically want to run the rest of the code inside the callback, for threading purposes or anything, and will return to the main code flow to do the rest. You can also do the processing in the callback if you want.

How to prevent a async task method get called concurrently

I have a async task method which will be accessed on both foreground and background. In foreground, where user call this method via a button whereas in the background I have a timer triggering the method in some interval.
I tried put lock but it gives exception await can not be inside the lock statement.
public static async Task<CommonResult<IEnumerable<AttendanceDTO>>> DownloadAttendanceAsync(string selectedDate, int siteId)
{
try
{
if (new AttendanceBLL().IsLocalDataAvailable(siteId, DateTime.Parse(selectedDate)))
return new CommonResult<IEnumerable<AttendanceDTO>>() { IsSuccess = true, Data = null };
var queue = new EntityQueueBLL();
//var latestUpdatedOn = queue.GetMaxUpdated(siteId, EntityIDEnum.Attendance);
var url = string.Format(Constants.PathPullAttendance, selectedDate, siteId);
var result = await new HttpRequestClient().GetRequest<IEnumerable<AttendanceDTO>>(url);
if (!result.IsSuccess)
return new CommonResult<IEnumerable<AttendanceDTO>>() { IsSuccess = false, Data = null, ErrorMessage = result.ErrorMessage };
if (result.Data != null && result.Data.Count() > 0)
{
var maxUpdatedOn = result.Data.Max(x => DateTime.Parse(x.UpdatedOn));
queue.Add(siteId, result.Data, result.Data.Count(), EntityIDEnum.Attendance, EntityType.Attendance, maxUpdatedOn);
}
return new CommonResult<IEnumerable<AttendanceDTO>> { IsSuccess = true, Data = result.Data };
}
catch (Exception e)
{
return new CommonResult<IEnumerable<AttendanceDTO>> { IsSuccess = false, Data = null, ErrorMessage = "Download Error" };
}
}
I tried put lock but it gives exception await can not be inside the lock statement.
The await-compatible equivalent of lock is SemaphoreSlim:
private static SempahoreSlim _mutex = new SemaphoreSlim(1);
public static async Task<CommonResult<IEnumerable<AttendanceDTO>>> DownloadAttendanceAsync(string selectedDate, int siteId)
{
await _mutex.WaitAsync();
try
{
...
}
catch (Exception e)
{
return new CommonResult<IEnumerable<AttendanceDTO>> { IsSuccess = false, Data = null, ErrorMessage = "Download Error" };
}
finally
{
_mutex.Release();
}
}
I use
AsyncLazy
For my initialization, which can be called from different threads. The task is only called once and other callers wait for it.
try the below:
await Task.Delay(2000);

C# ContinueWith / Return

I'm a bit new to async programming in C#, and struggling with a small but frustrating challenge.
I have a ASP.Net MVC WEB API that runs nicely. on the other side I have created this WEB API client method:
public async Task<DTOVArt> GetVArtFormArt(DTOArt art)
{
using (var client = GetHttpClient())
{
var response = await client.GetAsync("api/APIVArt/Get/" + art.ART_ID);
if (!response.IsSuccessStatusCode) return null;
var arts = await response.Content.ReadAsAsync<DTOVArt>();
return arts;
}
}
which seems to be work very fine...
This method is called from a WPF view model, an that's where my problem comes..
private DTOVObs TransFormAaretsGang(DTOAaretsGang aaretsGang)
{
var dtovObs = new DTOVObs();
using (var artservice = new ArtService())
{
artservice.GetVArtFormArt(new DTOArt() {ART_ID = aaretsGang.ART_ID}).ContinueWith(t =>
{
dtovObs.Familie_id = t.Result.Familie_id;
dtovObs.Gruppe_id = t.Result.Gruppe_id;
dtovObs.ART_ID = t.Result.ART_ID;
if (aaretsGang.Dato != null) dtovObs.Aarstal = aaretsGang.Dato.Value.Year;
return dtovObs;
});
}
return dtovObs;
}
The problem is that this last methods performs the return statement before it hits the ContinueWith statement block, that actually sets the values inside the class that should be returned.
Any attempt to do any kind of Wait() or using .Result instead of ContinueWith just blocks everything.
And if I do the return inside the ContinueWith block, the C# compiler says the method is missing a return statement, which is true.
That's the nature of async. Because your call is async, it will be executed later and the code below just continue executing.
Try adding an await and just remove the ContinueWith if this is the root of the call, usually it's an event handler:
private async Task<DTOVObs> TransFormAaretsGang(DTOAaretsGang aaretsGang)
{
var dtovObs = new DTOVObs();
DTOVArt Result = await artservice.GetVArtFormArt(new DTOArt() {ART_ID = aaretsGang.ART_ID});
dtovObs.Familie_id = Result.Familie_id;
dtovObs.Gruppe_id = Result.Gruppe_id;
dtovObs.ART_ID = Result.ART_ID;
if (aaretsGang.Dato != null)
dtovObs.Aarstal = aaretsGang.Dato.Value.Year;
return dtovObs;
}
If you still want to return an asynch Task so that any caller that calls this method can await the result, try:
private async Task<DTOVObs> TransFormAaretsGang(DTOAaretsGang aaretsGang)
{
using (var artservice = new ArtService())
{
return artservice.GetVArtFormArt(new DTOArt() {ART_ID = aaretsGang.ART_ID}).ContinueWith(t =>
{
dtovObs.Familie_id = t.Result.Familie_id;
dtovObs.Gruppe_id = t.Result.Gruppe_id;
dtovObs.ART_ID = t.Result.ART_ID;
if (aaretsGang.Dato != null) dtovObs.Aarstal = aaretsGang.Dato.Value.Year;
return dtovObs;
});
}
}
When you use async / await, you don't have to use ContinueWith anymore. ContinueWith means: wait until the previous is finished and use the result to do the next.
async await does this for you.
Suppose you have an async function. All async functions return either Task (for void return) or Task<TResult> if the return is of type TResult
private async Task<int> SlowAdditionAsync(int a, int b)
{
await Task.Delay(TimeSpan.FromSeconds(5); // causing the slow part
return a + b;
}
usage:
private async Task PerformSlowAddition()
{
int a = ...;
int b = ...;
int x =await SlowAditionAsync(a, b);
// the statement after the await, the task is finished, the result is in x.
// You can used the result as if you used Continuewith:
DisplayAddition(x);
}
Or if you want to do something else during the calculation:
private async Task PerformSlowAddition()
{
int a = ...;
int b = ...;
var taskSlowAddition = SlowAditionAsync(a, b);
DoSomethingElse(); // while the calculator does its thing
// now we need the result:
int x = await taskSlowAddition;
// no need to use ContinueWith, the next statement will be executed:
DisplayAddition(x);
}
Remember:
All functions that use a function that returns a Task or Task should be declared async
all async functions return Task is they return void or Task if they return TResult.
There is one exception to Task return: event handlers return void
After calling an async function, you can do other things.
When you need the result use await
you can only await a Task or a Task
The value of await Task is the TResult
There is only one async function that doesn't have to return a task and that is the eventhandler:
private async void OnButton1_Clicked(object sender, ...)
{
var taskX = DosomethingAsync(...)
DoSomethingElse();'
// now we need the result of taskX:
var x = await TaskX;
ProcessReault(x)
}
Note that although the event handler doesn't return a task it is still async
If you have some statements that needs to run in the background while your user interface keeps responsive, use Task.Factory.StartNew() or the more modern one Task.Run():
private int SlowCalculation(int a, int b)
{
// do something really difficult and slow
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5));
return a + b;
}
// make it async:
private async Task<int> SlowCalculationAsync(int a, int b)
{
return await Task.Run( () => SlowCalculation(a, b));
}
usage:
private async Task CalculateAsync()
{
int a = ...;
int b = ...;
int x = await SlowCalculationAsync(a, b);
Display(x);
}
private async void OnButton1_clicked(object sender, ...)
{
await CalculateAsync();
}

Categories

Resources