I have to use an API that mandates a class that implements its callback interface as a parameter to an authentication method.
public class CallBack : ICallBack
{
public object Response;
public void OnSuccess(object response)
{
Response = response;
}
public void OnException(Exception ex) { }
}
Authentication Call
public async Task<bool> LoginAsync(string username, string password)
{
CallBack callback = new CallBack();
await Task.Factory.StartNew(
() => userService.Authenticate(username, password, callback),
TaskCreationOptions.AttachedToParent);
return callback.Response is User ? true : false;
}
The problem is that LoginAsync finishes before the callback is invoked. I hoped that by starting the Authenticate call using TaskCreationOptions.AttachedToParent, it would propagate down to any child tasks started in Authenticate but it does not.
You should use a TaskCompletionSource object to wrap your callback based async method into a awaitable task.
I assume your ICallBack is like this :
public interface ICallBack
{
void OnSuccess(object response);
void OnException(Exception ex);
}
So you can implement LoginAsync like this :
public async Task<bool> LoginAsync(string username, string password)
{
var tcs = new TaskCompletionSource<object>();
ICallBack callback = new CallBackAsync(tcs);
userService.Authenticate(username, password, callback);
var result = await tcs.Task;
return result is User ? true : false;
}
public class CallBackAsync : ICallBack
{
private TaskCompletionSource<object> _tcs;
public CallBackAsync(TaskCompletionSource<object> tcs)
{
_tcs = tcs;
}
public void OnSuccess(object response)
{
_tcs.TrySetResult(response);
}
public void OnException(Exception ex) {
_tcs.TrySetException(ex);
}
}
For the quick explanation, when you use Task.Factory.StartNew(), the completion of the task is raised at the end of the lambda expression. But in your case this occurs before CallBack.OnSuccess call. So the result is not set.
The TaskCompletionSource class allow you to fully control when the task completion must occurs.
Related
Here I am trying to remove ConnectionId from connected connectedId list UserHandler.ConnectedUser in OnDisconnectedAsync method of my hub.
But the problem is that when ever user gets logout from application or close that window the OnDisconnectedAsync method on hub does not get hit.
Any help with my code will be grate. Thank you.
Below is my hub
public class ChatHub : Hub
{
public List<string> SendingUserList = new List<string>();
public async Task SendMessage(string to, string message)
{
var senderConnectionId = Context.ConnectionId;
SendingUserList.Add(to);
SendingUserList.Add(senderConnectionId);
foreach (var connectionId in SendingUserList)
{
await Clients.Client(connectionId).SendAsync("ReceiveMessage", message);
}
}
public override Task OnConnectedAsync()
{
Clients.All.SendAsync("ReciveUser", UserHandler.UserName, Context.ConnectionId);
return base.OnConnectedAsync();
}
public override async Task OnDisconnectedAsync(Exception e)
{
UserHandler.ConnectedUser.Remove(Context.ConnectionId));
await base.OnDisconnectedAsync(e);
}
}
public static class UserHandler
{
public static HashSet<string> ConnectedUser = new HashSet<string>();
public static string UserName { get; set; }
}
Below is my remaining code
protected override async Task OnInitializedAsync()
{
UserHandler.UserName = httpContextAccessor.HttpContext.User.Identity.Name;
hubConnection = new HubConnectionBuilder()
.WithUrl(NavigationManager.ToAbsoluteUri("/chathub"))
.Build();
hubConnection.ServerTimeout = TimeSpan.FromMinutes(60);
hubConnection.On<string>("ReceiveMessage", BroadcastMessage);
hubConnection.On<string,string>("ReciveUser", RefreshUserList);
await hubConnection.StartAsync();
StateHasChanged();
}
private void RefreshUserList(string connectedUserId, string connectionId )
{
UserHandler.ConnectedUser.Add(connectionId);
connectedUserList = UserHandler.ConnectedUser;
StateHasChanged();
}
private void BroadcastMessage(string message)
{
var encodedMsg = $"{message}";
messages.Add(encodedMsg);
StateHasChanged();
}
public async ValueTask DisposeAsync()
{
await hubConnection.DisposeAsync();
}
You'll need to implement the IAsyncDisposable interface in the Hub's consumer (Razor Component), like this:
#implements IAsyncDisposable
Add these methods:
public void Dispose()
{
hubConnection.DisposeAsync();
}
public async ValueTask DisposeAsync()
{
await hubConnection.DisposeAsync();
}
Now, when you close the client's page the OnDisconnectedAsync method will be called.
I am trying to create a helper class for sending some information periodically to backend server.
Attaching the code below.
public class HeartBeatService
{
private CancellationToken _cancellationToken;
private CancellationTokenSource _cancellationTokenSource;
public void StartHeartBeatService(TimeSpan timeSpan)
{
_cancellationTokenSource = new CancellationTokenSource();
_cancellationToken = _cancellationTokenSource.Token;
Task.Run(async () =>
{
while (!_cancellationToken.IsCancellationRequested)
{
SendHeartBeatToAzure();
try
{
await Task.Delay(timeSpan, _cancellationToken);
}
catch
{
break;
}
}
});
}
public void SuspendHeartBeatService()
{
_cancellationTokenSource?.Cancel();
}
private async void SendHeartBeatToAzure()
{
var platformService = ServiceLocator.Get<IPlatformService>();
var location = await platformService?.GetPositionAsync();
if (!double.IsNaN(location.Item1) && !double.IsNaN(location.Item2))
{
Debug.WriteLine($"Triggering Heartbeat with location{location.Item1},{location.Item2}");
//TODO Invoke heartbeat api call.
}
}
}
The code for sending the information to server is working fine.
But there is some issue with CancellationToken which is not working/it is not cancelling.
not sure what's wrong with the implementation.
Change the signature of the SendHeartBeatToAzure to return a Task, so that it can be awaited:
private async Task SendHeartBeatToAzure()
Then await the task returned by the method inside the loop. To achieve a stable and consisted heartbeat, it is a good idea to create the Task.Delay task before calling the method:
Task.Run(async () =>
{
while (true)
{
var delayTask = Task.Delay(timeSpan, _cancellationToken);
await SendHeartBeatToAzure();
await delayTask;
}
});
As a side note, you should probably store the task returned by Task.Run as a readonly property of the HeartBeatService class, so that the status of the task can be monitored.
I using some third-party class that have a long time work call DoLongWork().
When the user want to stop the "DoLongWork" we need to call the method StopWork().
When the DoLongWork method is working the UI need to show some loading bar.
I want to create a third-party proxy class.
In this class I will create a method called StartWork - this method will return Task and when the user is cancel the task using CancellationToken two actions will made:
1) the third-party method "StopWork" will called
2) the UI will stop the loading bar.
I try this but there is some problems catching the cancellation in the third-party proxy class and bubbling the cancellation to the ViewModel class.
public class MyViewModel
{
private CancellationTokenSource _cancellationTokenSource;
private ThirdPartyServiceProxy _thirdPartyServiceProxy = new ThirdPartyServiceProxy();
public bool IsUIInLoadingMode { get; set; }
public async void Start()
{
try
{
_cancellationTokenSource = new CancellationTokenSource();
IsUIInLoadingMode = true;
await _thirdPartyServiceProxy.StartWork(_cancellationTokenSource.Token);
_cancellationTokenSource = null;
}
catch (OperationCanceledException)/*Issue - This never called*/
{
IsUIInLoadingMode = false;
}
}
public void Stop()
{
_cancellationTokenSource?.Cancel();
}
}
public class ThirdPartyServiceProxy
{
private ThirdPartyService _thirdPartyService = new ThirdPartyService();
public Task StartWork(CancellationToken token)
{
var task = Task.Factory.StartNew(() =>
{
_thirdPartyService.DoLongWork();
},token);
//?? - Handle when task canceld - call _thirdPartyService.StopWork();
return task;
}
}
There's a couple of common ways to observe cancellation tokens: periodic polling with ThrowIfCancellationRequested and registering callbacks with Register.
In this case, polling isn't possible, since you don't control the code in DoLongWork. So you'll have to register a callback, which is more work.
public void DoWork(CancellationToken token)
{
token.ThrowIfCancellationRequested();
using (token.Register(() => _thirdPartyService.StopWork()))
_thirdPartyService.DoLongWork();
}
This wrapper assumes that DoLongWork will throw OperationCanceledException if canceled by StopWork.
The wrapper can then be invoked as:
await Task.Run(() => _thirdPartyServiceProxy.StartWork(_cancellationTokenSource.Token));
As a side note, I have switched to Task.Run; this is because StartNew is dangerous.
Apart from .NET 4.5.1 there is a new option on the TransactionScope which enables to use async flow. This allows to write the following client code
using(var txt = new TransactionScope(..., TransactionScopeAsyncFlowOption.Enabled)
{
await sender.SendAsync();
}
So far so good. But when I need to implement a volatile IEnlistmentNotification I'm struggling to do that. Let's imagine the following scenario, assumption: My underlying infrastructure is completely async from bottom to top
public class MessageSender : ISendMessages
{
public async Task SendAsync(TransportMessage message, SendOptions options)
{
await sender.SendAsync(message);
}
}
So what I want to achieve is to introduce a volatile IEnlistmentNotification like this:
internal class SendResourceManager : IEnlistmentNotification
{
private readonly Func<Task> onCommit;
public SendResourceManager(Func<Task> onCommit)
{
this.onCommit = onCommit;
}
public void Prepare(PreparingEnlistment preparingEnlistment)
{
preparingEnlistment.Prepared();
}
public void Commit(Enlistment enlistment)
{
await this.onCommit();
enlistment.Done();
}
public void Rollback(Enlistment enlistment)
{
enlistment.Done();
}
public void InDoubt(Enlistment enlistment)
{
enlistment.Done();
}
}
and the new sender
public class MessageSender : ISendMessages
{
public async Task SendAsync(TransportMessage message, SendOptions options)
{
// Dirty: Let's assume Transaction.Current is never null
Transaction.Current.EnlistVolatile(new SendResourceManager(async () => { await sender.SendAsync(message) }));
}
}
Note: Of course this code doesn't compile. It would require me to declare the commit method async void. Which is aweful.
So my question is: How can I write an enlistment which can internally await an asynchronous operation?
As long as EnlistVolatile isn't a heavy CPU bound time consuming operation, you can create a thin Task based wrapper over EnlistVolatile using Task.FromResult:
public static class TranscationExtensions
{
public static Task EnlistVolatileAsync(this Transaction transaction,
IEnlistmentNotification
enlistmentNotification,
EnlistmentOptions enlistmentOptions)
{
return Task.FromResult(transaction.EnlistVolatile
(enlistmentNotification,
enlistmentOptions));
}
}
and then consume it inside your method:
public class MessageSender : ISendMessages
{
public Task SendAsync(TransportMessage message, SendOptions options)
{
return Transaction.Current.EnlistVolatileAsync
(new SendResourceManager(async () =>
{ await sender.SendAsync(message) }));
}
}
which can be awaited higher in your callstack:
MessageSender sender = new MessageSender();
await sender.SendAsync(message, options);
I have a kind of bus that implements this interface:
public interface IBus
{
void Publish<T>(T t);
void Subscribe<T>(Guid subscriptionId, Action<T> action);
void Unsubscribe<T>(Guid subscriptionId);
}
Here is an example on how I use it:
public void PrintName()
{
IBus bus = new Bus();
var id = Guid.NewGuid();
bus.Subscribe<ReplyUserName>(id, replyUserName =>
{
bus.Unsubscribe<ReplyUserName>(id);
Console.WriteLine(replyUserName.UserName);
});
Bus.Publish(new RequestUserName());
}
And here are the RequestUserName and ReplyUserName classes:
public class RequestUserName {}
public class ReplyUserName
{
public string UserName { get; set; }
}
However I would like to write an extension method that would wrap this with async:
public static class BusExtension
{
public static async Task<TResult> Request<TRequest, TResult>(this IBus bus, TRequest request)
{
// TODO...
}
}
So that I will be able to write the previous code in such a way:
public async void PrintName()
{
IBus bus = new Bus();
var replyUserName = await bus.Request<RequestUserName, ReplyUserName>(new RequestUserName());
Console.WriteLine(replyUserName.UserName);
}
what should I write instead of the TODO?
You can use TaskCompletionSource<T> to wrap anything into an await-compatible method.
public static Task<TResult> Request<TRequest, TResult>(this IBus bus, TRequest request)
{
var tcs = new TaskCompletionSource<TResult>();
var id = Guid.NewGuid();
bus.Subscribe<TResult>(id, result =>
{
bus.Unsubscribe<TResult>(id);
tcs.TrySetResult(result);
});
bus.Publish(request);
return tcs.Task;
}
Note, however, that you should ensure that the task is completed. If there's any chance that the bus won't respond to the request, you should have a timer or something that faults the TaskCompletionSource.
You could implement this as follows:
var taskCompletionSource = new TaskCompletionSource<TResult>();
bus.Subscribe<TResult>(id, result =>
{
bus.Unsubscribe<TResult>(id);
taskCompletionSource.SetResult(result);
});
bus.Publish(request);
return taskCompletionSource.Task;
You might also want to check out Reactive Extensions (Rx) as your IBus interface looks similar to the ISubject interface (http://msdn.microsoft.com/en-us/library/hh211669.aspx). The Reactive Extensions library already provides convenient extension methods similar to the one you are attempting to implement.