How to use async func inside Nethereum StreamingWebSocketClient subscription - c#

After getting log inside subscribe method I want to call async function, but Subscribe function only takes Action<FilterLog>, so using async-await keyword is not possible.
How can I use await keyword inside this subscription ?
code example:
public static async Task GetLogsTokenTransfer_Observable_Subscription()
{
using(var client = new StreamingWebSocketClient("wss://mainnet.infura.io/ws"))
{
var filterTransfers = Event<TransferEventDTO>.GetEventABI().CreateFilterInput();
var subscription = new EthLogsObservableSubscription(client);
subscription.GetSubscriptionDataResponsesAsObservable().Subscribe(log =>
{
var decoded = Event<TransferEventDTO>.DecodeEvent(log);
if (decoded != null)
{
MyAsyncMethodHere(); // Can not use await keyword !!!!
}
});
await client.StartAsync();
await subscription.SubscribeAsync(filterTransfers);
await Task.Delay(TimeSpan.FromMinutes(1));
await subscription.UnsubscribeAsync();
await Task.Delay(TimeSpan.FromSeconds(5));
}
}

I'm not sure that this is the best approach but in my applications, i'm using Observable.FromAsync.
Smth like this:
subscription.GetSubscriptionDataResponsesAsObservable()
.Select(log => Observable.FromAsync(async () => {
await ProcessEvent(log);
}))
.Concat()
.Subscribe();
The Concat method is pretty important here, because it's ensures that the will be no overlapping in task execution

Related

Point of Task.Run if it will be awaited immediately

I have this method:
public async Task DoSomething()
{
string token = string.Empty;
await Task.Run(() =>
{
using (var sc = _dbManager.CreateSession())
{
// somework...
}
});
using (var resp = await client.PostAsync(authenticationEndpoint,
new FormUrlEncodedContent(authenticateRequest), stoppingToken))
{
resp.EnsureSuccessStatusCode();
string stringResponse = await resp.Content.ReadAsStringAsync(stoppingToken);
var response = JsonConvert.DeserializeObject<Response>(stringResponse);
token = response.AccessToken;
}
}
Now I do not understand why I need to use await Task.Run? It's confusing for me to understand logic behind this, as I'm awaiting the task to finish anyway, so why should I put it in the Task in the first place?
My lead told me that this was the way to go, and also explained to me that if I don't put that in Task, the code will be executed again from the top, after await client.PostAsync.

UWP CommunityToolkit Messenger async handler

ITNOA
Hi,
I have a UWP project and I want to message passing between ViewModel and View with CommunityToolkit MVVM Toolkit.
As you can see in my ViewModel we have something like below
private async void CallbackClick()
{
while (true)
{
CallbackDto callback = callbackQueue.Pop();
if (callback is null)
break;
callbackQueue.Push(new CallbackDto(callback.Number, DateTimeOffset.UtcNow.ToUnixTimeSeconds()));
string normalizedAddress = callback.Number;
BSN.LinphoneSDK.Call outgoingCall = await LinphoneManager.Instance.NewOutgoingCall($"{normalizedAddress}");
await outgoingCall.WhenEnded();
// TODO: It is mandatory for backing to Dialer from InCall, but it is very bugous and must fix it
await Task.Delay(1000);
Task<CancellationToken> cancellationTokenTask = WeakReferenceMessenger.Default.Send<ContinueCallbackAnsweringRequestMessage>();
CancellationToken cancellationToken = await cancellationTokenTask;
if (cancellationToken.IsCancellationRequested)
break;
}
}
And I have register on message in Dialer.xaml.cs in NavigatedTo like below
WeakReferenceMessenger.Default.Register<ContinueCallbackAnsweringRequestMessage>(this, async (r, message) =>
{
var continueCallbackAnsweringDialog = new MessageDialog("آیا مایل به ادامه پاسخ‌دهی به تماس‌های درخواستی هستید؟");
TaskCompletionSource<CancellationToken> tcs = new TaskCompletionSource<CancellationToken>(TaskCreationOptions.RunContinuationsAsynchronously);
continueCallbackAnsweringDialog.Commands.Add(new UICommand(
"بلی",
new UICommandInvokedHandler((IUICommand command) =>
{
tcs.SetResult(new CancellationToken(false));
})));
continueCallbackAnsweringDialog.Commands.Add(new UICommand(
"خیر",
new UICommandInvokedHandler((IUICommand command) =>
{
tcs.SetResult(new CancellationToken(true));
})));
continueCallbackAnsweringDialog.DefaultCommandIndex = 0;
continueCallbackAnsweringDialog.CancelCommandIndex = 1;
await continueCallbackAnsweringDialog.ShowAsync();
message.Reply(tcs.Task);
});
In this scenario I got a InvalidOperationException on WeakReferenceMessenger.Default.Send. My question is how to handle this situation?
In another side, if I remove async and await keyword in handler, my code is work correctly. but the problem is I do not await on IAsyncOperation and I want to await on in it.
I add discussion on CommunityToolkit GitHub about that.
My question is how to handle this situation?
Since the MessageHandler won't (can't) be awaited by the toolkit, it returns when you call await ....
You could try something like this:
WeakReferenceMessenger.Default.Register<ContinueCallbackAnsweringRequestMessage>(
this, (r, m) => m.Reply(ShowDialogAndWaitForResult()));
...where ShowDialogAndWaitForResult() is a custom async method that returns a Task<T>, calls the ShowAsync() of the dialog and wait for the result.
Another option is implement your own blocking (non-async) dialog. Or consider another solution which don't involve a messenger.

Getting result from Task.Run

Hi guys i'm trying to get some results from Task.Run but i cannot figure it how
I have multiple methods that i would like to run in parallel and extract result:
This is one of the methods
protected override async Task<IList<EducationDTO>> GetEmployeesEducation(int userId)
{
IList<EducationDTO> userEducation = await EducationService.GetEducationsByUserId(userId);
return userEducation.Count > 0 ? userEducation : null;
}
Here where all methods will be run in parallel
public async Task<DTOs.EmployeeDTO> GetEmployeeInfo(int userId)
{
EmployeeDTO employee = new EmployeeDTO();
Task task = Task.Run(() => {
Parallel.Invoke(
async () => { await GetEmployeeLanguages(userId); },
// ...
});
task.Wait();
/// extract result and process how ???
return employee;
}
I have multiple methods that i would like to run in parallel and extract result
Actually, you want to run them concurrently. "Parallel" implies CPU-bound code, but your code is I/O-bound.
So, don't use Parallel at all; it's the wrong tool here:
public async Task<DTOs.EmployeeDTO> GetEmployeeInfo(int userId)
{
EmployeeDTO employee = new EmployeeDTO();
// Start all tasks
var languagesTask = GetEmployeeLanguages(userId);
var educationsTask = GetEmployeesEducation(userId);
var outExperiencesTask = GetEmployeesOutExperience(userId);
var ubiExperiencesTask = GetEmployeesUbiExperience(userId);
// Asynchronously wait for them all to complete
await Task.WhenAll(languagesTask, educationsTask, outExperiencesTask, ubiExperiencesTask);
// Retrieve results
employee.Languages = await languagesTask;
employee.Educations = await educationsTask;
employee.OutExperiences = await outExperiencesTask;
employee.UbiExperiences = await ubiExperiencesTask;
return employee;
}
There's no use creating a task and immediately waiting for it, so drop the StartNew.
Also no need to first Wait and then fetch the Result, Result implicitly waits. And the method isn't asynchronous at all and you can drop async Task<> and directly return the EmployeeDTO...
So this is my finding
public async Task<DTOs.EmployeeDTO> GetEmployeeInfo(int userId)
{
EmployeeDTO employee = new EmployeeDTO();
Task<EmployeeDTO> task = Task.Factory.StartNew(() =>
{
Parallel.Invoke(
() => { employee.Languages = GetEmployeeLanguages(userId).Result; },
() => { employee.Educations = GetEmployeesEducation(userId).Result; },
() => { employee.OutExperiences = GetEmployeesOutExperience(userId).Result; },
() => { employee.UbiExperiences = GetEmployeesUbiExperience(userId).Result; });
return employee;
});
task.Wait();
return task.Result;
}
If you have any other suggestions, or a better approach please share. Thank you.

ContinueWith not being called async

I'm trying to httpget some values before I execute the next line in the statement. I need to wait for this call to return so I can use the values I deserialize into a list.
Since I want the async call to finish first, I wrapped this in a Task. It worked and it's successfully retrieving the JSON. I then can't get it to go into the ContinueWith block. Why is it not going in there, even when the task is completed(?).
How I'm calling it:
Task f = Task.Run(() =>
{
var task = RetrieveDataAsync();
}).ContinueWith((antecedent) =>
{
pokemonListActivityListView.Adapter = new PokemonListAdapter(this, pokemonList);
pokemonListActivityListView.FastScrollEnabled = true;
pokemonListActivityListView.ItemClick += PokemonListActivityListViewOnItemClick;
});
RetrieveDataAsync method:
private async Task RetrieveDataAsync()
{
string dataUri = "http://pokemonapp6359.azurewebsites.net/Pkmn/GetAllPokemon";
using (var httpClient = new HttpClient())
{
var uri = new Uri(string.Format(dataUri, string.Empty));
//DisplayProgressBar(BeforeOrAfterLoadState.Before, progressBarView);
var response = await httpClient.GetAsync(uri);
//DisplayProgressBar(BeforeOrAfterLoadState.After, progressBarView);
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
pokemonList = JsonConvert.DeserializeObject<List<PokemonDTO>>(content);
//canPressButtons = true; //fix this when implement local db
Utilities.Utilities.ShowToast(this, "Successfully fetched data", ToastLength.Short, GravityFlags.Center);
return;
}
else
{
Utilities.Utilities.ShowToast(this, "Failed to fetch data", ToastLength.Short, GravityFlags.Center);
return;
}
}
}
Why is my code not going into the ContinueWith when I've got the JSON ? Thanks!
Instead of just assigning the hot task, you are not waiting on it to finish. You have to call ContinueWith on that task:
var task = RetrieveDataAsync();
task.ContinueWith( ... );
Or await the task:
var result = await RetrieveDataAsync();
... // continue
The problem is that you're ignoring the task returned from RetrieveDataAsync. If you return that task from your lambda expression, then it will behave as you expect.
On a side note, you shouldn't use ContinueWith; it's a dangerous API. Use await instead of ContinueWith:
await Task.Run(() => RetrieveDataAsync());
pokemonListActivityListView.Adapter = new PokemonListAdapter(this, pokemonList);
pokemonListActivityListView.FastScrollEnabled = true;
pokemonListActivityListView.ItemClick += PokemonListActivityListViewOnItemClick;

GetStringAsync with ContinueWith and WhenAll -- Not Waiting for all requests to finish

I'm trying to process each individual request as it finishes, which is what would happen in the ContinueWith after the GetStringAsync and then when they've all completed have an additional bit of processing.
However, it seems that the ContinueWith on the WhenAll fires right away. It appears as if the GetStringAsync tasks are faulting, but I can't figure out why.
When I use WaitAll instead of WhenAll and just add my processing after the WaitAll then my requests work just fine. But when I change it to WhenAll it fails.
Here is an example of my code:
using (var client = new HttpClient())
{
Task.WhenAll(services.Select(x =>
{
return client.GetStringAsync(x.Url).ContinueWith(response =>
{
Console.WriteLine(response.Result);
}, TaskContinuationOptions.AttachedToParent);
}).ToArray())
.ContinueWith(response =>
{
Console.WriteLine("All tasks completed");
});
}
You shouldn't use ContinueWith and TaskContinuationOptions.AttachedToParent when using async-await. Use async-await only, instead:
async Task<IEnumerable<string>> SomeMethod(...)
{
using (var client = new HttpClient())
{
var ss = await Task.WhenAll(services.Select(async x =>
{
var s = await client.GetStringAsync(x.Url);
Console.WriteLine(response);
return s;
};
Console.WriteLine("All tasks completed");
return ss;
}
}
Well, I found the issue. I'll leave it here in case anyone else comes along looking for a similar answer. I still needed to await the Task.WhenAll method.
So, the correct code would be:
using (var client = new HttpClient())
{
await Task.WhenAll(services.Select(x =>
{
return client.GetStringAsync(x.Url).ContinueWith(response =>
{
Console.WriteLine(response.Result);
}, TaskContinuationOptions.AttachedToParent);
}).ToArray())
.ContinueWith(response =>
{
Console.WriteLine("All tasks completed");
});
}
I still see a couple issues with your solution:
Drop the using statement - you don't want to dispose HttpClient.
Drop the ContinueWiths - you don't need them if your are using await properly.
The Task.WhenAny approach described in this MSDN article is a somewhat cleaner way to process tasks as they complete.
I would re-write your example like this:
var client = new HttpClient();
var tasks = services.Select(x => client.GetStringAsync(x.Url)).ToList();
while (tasks.Count > 0)
{
var firstDone = await Task.WhenAny(tasks);
tasks.Remove(firstDone);
Console.WriteLine(await firstDone);
}
Console.WriteLine("All tasks completed");
Edit to address comment:
If you need access to the service object as the tasks complete, one way would be to modify tasks to be a list of Task<ObjectWithMoreData> instead of Task<string>. Notice the lambda is marked async so we can await within it:
var client = new HttpClient();
var tasks = services.Select(async x => new
{
Service = x,
ResponseText = await client.GetStringAsync(x.Url)
}).ToList();
while (tasks.Count > 0)
{
var firstDone = await Task.WhenAny(tasks);
tasks.Remove(firstDone);
var result = await firstDone;
Console.WriteLine(result.ResponseText);
// do something with result.Service
}
Console.WriteLine("All tasks completed");

Categories