For example, I have the following methods:
private async Task<T> Read<T>(string id, string endpoint)
{
//....
}
private async Task<List<T>> List<T>(int start, int count, string endpoint, List<FilterData> filterData = null)
{
//....
}
(and more with different properties)
But all of these method can throw BillComInvalidSessionException
If I called method throws this exception, I want to execute some logic and recall called method.
I.e.:
private async Task<T> ReadWithRetry<T>(string id, string endpoint)
{
try
{
return await Read<T>(id, endpoint);
}
catch (BillComInvalidSessionException)
{
SessionId = new Lazy<string>(() => LoginAsync().Result);
return await ReadWithRetry<T>(id, endpoint);
}
}
private async Task<List<T>> ListWithRetry<T>(int start, int count, string endpoint, List<FilterData> filterData = null)
{
try
{
return await List<T>(start, count, endpoint, filterData);
}
catch (BillComInvalidSessionException)
{
SessionId = new Lazy<string>(() => LoginAsync().Result);
return await ListWithRetry<T>(start, count, endpoint, filterData);
}
}
How to create one common method, which will execute the same logic, but get different methods as parameters?
You can achieve this by using a generic delegate:
private async Task<T> Retry<T>(Func<Task<T>> func)
{
try
{
return await func();
}
catch (BillComInvalidSessionException)
{
SessionId = new Lazy<string>(() => LoginAsync().Result);
return await Retry(func);
}
}
And then your retry methods would turn to:
private async Task<T> ReadWithRetry<T>(string id, string endpoint)
{
return await Retry(async () => await Read<T>(id, endpoint));
}
private async Task<List<T>> ListWithRetry<T>(int start, int count, string endpoint, List<FilterData> filterData = null)
{
return await Retry(async () => await List<T>(start, count, endpoint, filterData));
}
Related
I'm implementing Binance's API.
The documentation says:
WebSocket connections have a limit of 5 incoming messages per second. A message is considered:
A PING frame
A PONG frame
A JSON controlled message (e.g. subscribe, unsubscribe)
For ex. there is a simple web socket wrapper such as the one from the official Binance Connector. According to the limitation above, SendAsync should be restricted 5 messages per second. If a few threads call SendAsync 5 times at the same time (including PING frame which is built-in the ClientWebSocket class), it's going to fail. How can I solve the issue with that limitation gracefully? Using bounded channels is a solution?
public class BinanceWebSocket : IDisposable
{
private IBinanceWebSocketHandler handler;
private List<Func<string, Task>> onMessageReceivedFunctions;
private List<CancellationTokenRegistration> onMessageReceivedCancellationTokenRegistrations;
private CancellationTokenSource loopCancellationTokenSource;
private Uri url;
private int receiveBufferSize;
public BinanceWebSocket(IBinanceWebSocketHandler handler, string url, int receiveBufferSize = 8192)
{
this.handler = handler;
this.url = new Uri(url);
this.receiveBufferSize = receiveBufferSize;
this.onMessageReceivedFunctions = new List<Func<string, Task>>();
this.onMessageReceivedCancellationTokenRegistrations = new List<CancellationTokenRegistration>();
}
public async Task ConnectAsync(CancellationToken cancellationToken)
{
if (this.handler.State != WebSocketState.Open)
{
this.loopCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
await this.handler.ConnectAsync(this.url, cancellationToken);
await Task.Factory.StartNew(() => this.ReceiveLoop(loopCancellationTokenSource.Token, this.receiveBufferSize), loopCancellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
}
}
public async Task DisconnectAsync(CancellationToken cancellationToken)
{
if (this.loopCancellationTokenSource != null)
{
this.loopCancellationTokenSource.Cancel();
}
if (this.handler.State == WebSocketState.Open)
{
await this.handler.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, null, cancellationToken);
await this.handler.CloseAsync(WebSocketCloseStatus.NormalClosure, null, cancellationToken);
}
}
public void OnMessageReceived(Func<string, Task> onMessageReceived, CancellationToken cancellationToken)
{
this.onMessageReceivedFunctions.Add(onMessageReceived);
if (cancellationToken != CancellationToken.None)
{
var reg = cancellationToken.Register(() =>
this.onMessageReceivedFunctions.Remove(onMessageReceived));
this.onMessageReceivedCancellationTokenRegistrations.Add(reg);
}
}
public async Task SendAsync(string message, CancellationToken cancellationToken)
{
byte[] byteArray = Encoding.ASCII.GetBytes(message);
await this.handler.SendAsync(new ArraySegment<byte>(byteArray), WebSocketMessageType.Text, true, cancellationToken);
}
public void Dispose()
{
this.DisconnectAsync(CancellationToken.None).Wait();
this.handler.Dispose();
this.onMessageReceivedCancellationTokenRegistrations.ForEach(ct => ct.Dispose());
this.loopCancellationTokenSource.Dispose();
}
private async Task ReceiveLoop(CancellationToken cancellationToken, int receiveBufferSize = 8192)
{
WebSocketReceiveResult receiveResult = null;
try
{
while (!cancellationToken.IsCancellationRequested)
{
var buffer = new ArraySegment<byte>(new byte[receiveBufferSize]);
receiveResult = await this.handler.ReceiveAsync(buffer, cancellationToken);
if (receiveResult.MessageType == WebSocketMessageType.Close)
{
break;
}
string content = Encoding.UTF8.GetString(buffer.ToArray());
this.onMessageReceivedFunctions.ForEach(omrf => omrf(content));
}
}
catch (TaskCanceledException)
{
await this.DisconnectAsync(CancellationToken.None);
}
}
}
Second way which I'm not 100% sure it solves it
SendAsync is being called in a loop using Channels. SingleReader is set to true, which means there will be only one consumer at a time. It technically should solve the issue, but I'm not 100% sure because the channel might only be limiting the amount in the buffer.
private readonly Channel<string> _messagesTextToSendQueue = Channel.CreateUnbounded<string>(new UnboundedChannelOptions()
{
SingleReader = true,
SingleWriter = false
});
public ValueTask SendAsync(string message)
{
Validations.Validations.ValidateInput(message, nameof(message));
return _messagesTextToSendQueue.Writer.WriteAsync(message);
}
public void Send(string message)
{
Validations.Validations.ValidateInput(message, nameof(message));
_messagesTextToSendQueue.Writer.TryWrite(message);
}
private async Task SendTextFromQueue()
{
try
{
while (await _messagesTextToSendQueue.Reader.WaitToReadAsync())
{
while (_messagesTextToSendQueue.Reader.TryRead(out var message))
{
try
{
await SendInternalSynchronized(message).ConfigureAwait(false);
}
catch (Exception e)
{
Logger.Error(e, L($"Failed to send text message: '{message}'. Error: {e.Message}"));
}
}
}
}
catch (TaskCanceledException)
{
// task was canceled, ignore
}
catch (OperationCanceledException)
{
// operation was canceled, ignore
}
catch (Exception e)
{
if (_cancellationTotal.IsCancellationRequested || _disposing)
{
// disposing/canceling, do nothing and exit
return;
}
Logger.Trace(L($"Sending text thread failed, error: {e.Message}. Creating a new sending thread."));
StartBackgroundThreadForSendingText();
}
}
I would try to keep it as simple as possible and use Semaphore Slim to achieve this, I have created a class to perform this task.
public class ThrottlingLimiter
{
private readonly SemaphoreSlim _semaphore;
private readonly TimeSpan _timeUnit;
public ThrottlingLimiter(int maxActionsPerTimeUnit, TimeSpan timeUnit)
{
if (maxActionsPerTimeUnit < 1)
throw new ArgumentOutOfRangeException(nameof(maxActionsPerTimeUnit));
if (timeUnit < TimeSpan.Zero || timeUnit.TotalMilliseconds > int.MaxValue)
throw new ArgumentOutOfRangeException(nameof(timeUnit));
_semaphore = new SemaphoreSlim(maxActionsPerTimeUnit, maxActionsPerTimeUnit);
_timeUnit = timeUnit;
}
public async Task WaitAsync(CancellationToken cancellationToken = default)
{
await _semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
ScheduleSemaphoreRelease();
}
private async void ScheduleSemaphoreRelease()
{
await Task.Delay(_timeUnit).ConfigureAwait(false);
_semaphore.Release();
}
}
Now to Use this class, all you have to do is set your limit and timeSpan
public async Task SendData(List<string> allMessages)
{
// Limiting 5 calls per second
ThrottlingLimiter throttlingLimiter = new ThrottlingLimiter(5, TimeSpan.FromSeconds(1));
await Task.WhenAll(allMessages.Select(async message =>
{
await throttlingLimiter.WaitAsync();
try {
await SendInternalSynchronized(message);
// I am not sure what this SendInternalSynchronized returns but I would return some thing to keep a track if this call is successful or not
}
catch (Exception e)
{
Logger.Error(e, L($"Failed to send text message: {message}'. Error: {e.Message}"));
}
});
}
so basically what will happen here is, no matter how big your list is, the ThrottlingLimiter will only send 5 messages per second and wait for the next second to send the next 5 messages.
so, in your case, get all the data from your call to
await _messagesTextToSendQueue.Reader.WaitToReadAsync();
store that into a list or any collection and pass that to the SendData function.
I would like to reuse the following static generic Profile function in my application:
private static async Task<T> Profile<T>(Func<Task<T>> func, string operation)
{
Console.WriteLine($"{operation} is called");
return await func();
}
And I have the following interface to implement:
public interface ICustomerOperations
{
Task<Customer> GetCustomerAsync(string id);
Task DeleteCustomerAsync(string id);
}
I can use Profile method with GetCustomerAsync without any problem.
public async Task<Customer> GetCustomer(string id)
{
return await Profile(() => _customerOperations.GetCustomerAsync(id), $"GetCustomerAsync");
}
However when I try to use Profile with DeleteCustomerAsync
public async void DeleteCustomer(string id)
{
await Profile(() => _customerOperations.DeleteCustomerAsync(id), $"DeleteCustomerAsync");
}
Build failed:
The type arguments for method 'CrmService.Profile(Func<Task>,
string)' cannot be inferred from the usage. Try specifying the type
arguments explicitly.
So my question is how can I reuse Task<T> with void?
You can create an overload without duplicating the method:
private static Task Profile(Func<Task> func, string operation)
{
return Profile<object>(async () => { await func(); return null; }, operation);
}
A simple solution is to duplicate your method:
private static async Task Profile(Func<Task> func, string operation)
{
Console.WriteLine($"{operation} is called");
await func();
}
I think duplicating your method is cleaner than doing some hacks like this:
public async void DeleteCustomer(string id)
{
await Profile(async () => { await _customerOperations.DeleteCustomerAsync(id); return 0; }, $"DeleteCustomerAsync");
}
I'm trying to make this code working:
protected async Task RunIsolated<TServ1, TServ2>(Action<TServ1, TServ2> action)
{
await RunInScope(action, typeof(TServ1), typeof(TServ2));
}
protected async Task<TResult> RunIsolatedForResult<TService, TResult>(Func<TService, TResult> func)
{
return (TResult) await RunInScope(func, typeof(TService));
}
private Task<object> RunInScope(Delegate d, params object[] args)
{
using (var scope = _serviceProvider.CreateScope())
{
object[] parameters = args.Cast<Type>().Select(t => scope.ServiceProvider.GetService(t)).ToArray();
return Task.FromResult(d.DynamicInvoke(parameters));
}
}
this work for sync version of code, like this:
await RunIsolated<Service>(serv => serv.SaveAsync(item).Wait());
but don't work (db operation throw an exception) for async version of the same code
await RunIsolated<Service>(async serv => await serv.SaveAsync(item));
Is it somehow possible to convert async Action or Func to Delegate and invoke it without loosing async state?
You need to create new overload which accepts Func<Task>. Right now, anonymous async function you pass here
await RunIsolated<Service>(async serv => await serv.SaveAsync(item));
Is treated as Action, which means that is async void method basically, with all corresponding drawbacks. Instead you have to do something like this (simplified to use basic Action and Func, adjust as needed to your situation):
protected Task RunIsolated(Action action) {
return RunInScope(action);
}
protected Task RunIsolated(Func<Task> action) {
return RunInScope(action);
}
protected Task<TResult> RunIsolatedForResult<TResult>(Func<Task<TResult>> action) {
return RunInScopeWithResult<TResult>(action);
}
protected Task<TResult> RunIsolatedForResult<TResult>(Func<TResult> action) {
return RunInScopeWithResult<TResult>(action);
}
private async Task RunInScope(Delegate d, params object[] args) {
// do some stuff
using (var scope = _serviceProvider.CreateScope()) {
object[] parameters = args.Cast<Type>().Select(t => scope.ServiceProvider.GetService(t)).ToArray();
var result = d.DynamicInvoke(parameters);
var resultTask = result as Task;
if (resultTask != null) {
await resultTask;
}
}
}
private async Task<TResult> RunInScopeWithResult<TResult>(Delegate d, params object[] args) {
// do some stuff
using (var scope = _serviceProvider.CreateScope()) {
object[] parameters = args.Cast<Type>().Select(t => scope.ServiceProvider.GetService(t)).ToArray();
var result = d.DynamicInvoke(parameters);
var resultTask = result as Task<TResult>;
if (resultTask != null) {
return await resultTask;
}
return (TResult) result;
}
}
I have these functions
public async Task<List<Machine>> GetMachines()
{
await Initialize();
await SyncMachines();
return await machineTable.ToListAsync();
}
public async Task InsertMachines(List<Machine> machines)
{
await Initialize();
await Task.WhenAll(machines.Select(m => machineTable.InsertAsync(m)));
await SyncMachines();
}
I am writing a parent class to put these functions in, such that the functions getMachines() and InsertMachines() become getObject and InsertObject where the List<Machine> can be a list of any objects, so they can return and accept any type of list
How do I declare functions like this?
According to your description, I created my azure sync table class as follows, you could refer to it:
public class AzureCloudSyncTable<TModel> where TModel : class
{
public static MobileServiceClient MobileService = new MobileServiceClient(
"https://{your-mobile-app-name}.azurewebsites.net"
);
private IMobileServiceSyncTable<TModel> syncTable = MobileService.GetSyncTable<TModel>();
#region Offline sync
private async Task InitLocalStoreAsync()
{
if (!MobileService.SyncContext.IsInitialized)
{
var store = new MobileServiceSQLiteStore("localstore.db");
store.DefineTable<TModel>();
await MobileService.SyncContext.InitializeAsync(store);
}
await SyncAsync();
}
private async Task SyncAsync()
{
await PushAsync();
await syncTable.PullAsync(typeof(TModel).Name, syncTable.CreateQuery());
}
private async Task PushAsync()
{
try
{
await MobileService.SyncContext.PushAsync();
}
catch (MobileServicePushFailedException ex)
{
if (ex.PushResult != null)
{
foreach (var error in ex.PushResult.Errors)
{
await ResolveConflictAsync(error);
}
}
}
}
private async Task ResolveConflictAsync(MobileServiceTableOperationError error)
{
//var serverItem = error.Result.ToObject<TModel>();
//var localItem = error.Item.ToObject<TModel>();
//// Note that you need to implement the public override Equals(TModel item)
//// method in the Model for this to work
//if (serverItem.Equals(localItem))
//{
// // Items are the same, so ignore the conflict
// await error.CancelAndDiscardItemAsync();
// return;
//}
//// Client Always Wins
//localItem.Version = serverItem.Version;
//await error.UpdateOperationAsync(JObject.FromObject(localItem));
// Server Always Wins
//await error.CancelAndDiscardItemAsync();
}
#endregion
#region public methods
public async Task<List<TModel>> GetAll()
{
await InitLocalStoreAsync();
await SyncAsync();
return await syncTable.ToListAsync();
}
public async Task Insert(List<TModel> items)
{
await InitLocalStoreAsync();
await Task.WhenAll(items.Select(item => syncTable.InsertAsync(item)));
await PushAsync();
}
#endregion
}
Additionally, for more details about Handling Conflict Resolution, you could refer to adrian hall's book here.
What I would like to write is the following:
async void Foo()
{
var result = await GetMyTask().IgnoreCancelAndFailure();
ProcessResult(result);
}
Instead of:
void Foo()
{
GetMyTask().ContinueWith(task => ProcessResult(task.Result),
TaskContinuationOptions.OnlyOnRanToCompletion);
}
However I don't know how to implement the method IgnoreCancelAndFailure, which would have the following signature:
//On cancel or failure this task should simply stop and never complete.
Task<T> IgnoreCancelAndFailure<T>(this Task<T> task)
{
throw new NotImplementedException();
}
If possible, how should I implement IgnoreCancelAndFailure?
You could do something like that, but you need to know what you want the method to return in case of failure, since a return value is expected:
public static async Task<T> IgnoreCancelAndFailure<T>(this Task<T> task)
{
try
{
return await task;
}
catch
{
return ???; // whatever you want to return in this case
}
}
If it's a Task with no result, just leave the catch empty (or perhaps log the exception... swallowed exceptions make for hard debugging)
If you just want to execute ProcessResult only when GetMyTask succeeds, you can do this:
async void Foo()
{
try
{
var result = await GetMyTask();
ProcessResult(result);
}
catch(Exception ex)
{
// handle the exception somehow, or ignore it (not recommended)
}
}
You will never be able to stop your code from continuing expect when killing the thread or process. keep in mind that the await task can be considered a function call that will always have to return a value or throw an exception.
The closest way to shorten your code is creating a wrapper function that uses the ProcessResult method as Action argument.
Something like that:
public static async Task IgnoreCancelAndFailure<T>(this Task<T> task, Action<T> resultProcessor)
{
task.ContinueWith(t => resultProcessor(t.Result),
TaskContinuationOptions.OnlyOnRanToCompletion);
}
async void Foo()
{
GetMyTask().IgnoreCancelAndFailure(ProcessResult);
}
I think I found the answer. The following seems to do the trick. It uses the awaitable pattern. Could you guys confirm that this isn't evil?
class User
{
async void Foo()
{
var result = await GetMyTask().IgnoreCancelAndFailure();
ProcessResult(result);
}
}
public static class TaskExtenstions
{
public static SilentTask<T> IgnoreCancelAndFailure<T>(this Task<T> task)
{
return new SilentTask<T>(task);
}
}
public class SilentTask<T>
{
private readonly Task<T> _inner;
public SilentTask(Task<T> inner)
{
_inner = inner;
}
public SilentAwaiter GetAwaiter()
{
return new SilentAwaiter(_inner);
}
public class SilentAwaiter : INotifyCompletion
{
private readonly TaskAwaiter<T> _inner;
private readonly Task<T> _task;
public SilentAwaiter(Task<T> task)
{
_task = task;
_inner = task.GetAwaiter();
}
public bool IsCompleted
{
get
{
return _task.Status == TaskStatus.RanToCompletion;
}
}
public void OnCompleted(Action continuation)
{
_inner.OnCompleted(() =>
{
if (IsCompleted)
{
continuation();
}
});
}
public T GetResult()
{
return _inner.GetResult();
}
}
}