I'm looking for ideas about using delegate with async and await. Is it good or not? I've searched on Google but none of them are similar to mine.
I'm defining a method to change a message status (isRead = true) with SignalR:
enum MessageStatus
{
Failure,
Success
}
delegate Task<MessageStatus> MsgDelegate(string id);
public async Task ChangeMessageStatus(string id)
{
string error = string.Empty;
MsgDelegate msg = async (x) =>
{
try
{
using (var db = new VinaChanelDbContext())
{
var message = db.Messages.SingleOrDefault(m => m.Id == x);
if (message != null)
{
message.IsRead = true;
}
return await db.SaveChangesAsync() > 0 ?
MessageStatus.Success : MessageStatus.Failure;
}
}
catch (Exception e)
{
error = e.Message;
return MessageStatus.Failure;
}
};
switch (await msg(id))
{
case MessageStatus.Success:
Clients.Caller.updateStatus(true);
break;
case MessageStatus.Failure:
Clients.Caller.errorMessage(error);
Clients.Caller.updateStatus(false);
break;
}
}
Is my code weird? Should I use it?
Why use a delegate? What's wrong with:
enum MessageStatus
{
Failure,
Success
}
public async Task ChangeMessageStatus(string id)
{
string error = string.Empty;
var status = MessageStatus.Failure;
try
{
using (var db = new VinaChanelDbContext())
{
var message = db.Messages.SingleOrDefault(m => m.Id == id);
if (message != null)
{
message.IsRead = true;
if (await db.SaveChangesAsync() > 0)
{
status = MessageStatus.Success;
}
}
}
}
catch (Exception e)
{
error = e.Message;
}
switch (status)
{
case MessageStatus.Success:
Clients.Caller.updateStatus(true);
break;
case MessageStatus.Failure:
Clients.Caller.errorMessage(error);
Clients.Caller.updateStatus(false);
break;
}
}
You might want to break that top part out into a private method, but I'm not sure why you would want to make it a delegate...
Related
I am using SignalR to do RTM and RT- Call Invitation, I created a HubConnectionServices class stored its obj in a public accessible static variable and use it to send messages. For some reason, my app crashes, or does not navigate, when navigation is called.
public class HubConnectionService
{
public readonly HubConnection hubConnection;
public Page currentPage;
public bool currentPageis;
public HubConnectionService(string token)
{
hubConnection = new HubConnectionBuilder().WithUrl(ApiSettings.HubConnection + "/video", (opts) =>
{
opts.Headers.Add("Authorization", token);
opts.HttpMessageHandlerFactory = (message) =>
{
if (message is HttpClientHandler clientHandler)
{
clientHandler.ServerCertificateCustomValidationCallback +=
(sender, certificate, chain, sslPolicyErrors) => { return true; };
}
return message;
};
}).Build();
}
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum ConnectionResult
{
Connected, Disconnected
}
public async Task<ConnectionResult> ConnectAsync()
{
try
{
await hubConnection.StartAsync();
return ConnectionResult.Connected;
}
catch (Exception problem)
{
Debug.WriteLine(problem.Message);
return ConnectionResult.Disconnected;
}
}
public ConnectionResult AddRecivePoints()
{
try
{
// when disconnected
hubConnection.Closed += async (error) =>
{
GlobalVariables.isConnected = false;
while (hubConnection.State != HubConnectionState.Connected)
{
_ = await ConnectAsync();
}
GlobalVariables.isConnected = true;
};
// receive message
_ = hubConnection.On("ReceiveMessage", (Func<string, HubMessage, Task>)(async (user, message) =>
{
await SwitchCases(message);
}));
return ConnectionResult.Connected;
}
catch (Exception)
{
return ConnectionResult.Disconnected;
}
}
private async Task SwitchCases(HubMessage message)
{
switch (message.Type)
{
case HubMessageType.VideoCallInvitation:
{
if (GlobalVariables.UserIsAvailable)
{
GlobalVariables.UserIsAvailable = false;
await Application.Current.MainPage.Navigation.PushAsync(new ReciviedCall(hubMessage: message));
}
break;
}
case HubMessageType.NormalMessage:
Page cpage = Application.Current.MainPage.Navigation.NavigationStack[Application.Current.MainPage.Navigation.NavigationStack.Count - 1];
bool page = cpage?.GetType().Name == nameof(ChatPrivate);
message.IsNotUserCreated = true;
message.IsUserCreated = false;
bool sender = GlobalVariables.CurrentOpenChatUserEmail == message.Sender;
if (page && sender)
{
GlobalVariables.privateChatMessages.Add(message);
}
else
{
DependencyService.Get<INotification>().CreateNotification(message.Sender,message.Data);
//CrossLocalNotifications.Current.Show(message.Data, message.Sender, 0, DateTime.Now);
}
break;
case HubMessageType.UnKnownError:
break;
case HubMessageType.CallAccepted:
break;
case HubMessageType.CallRejected:
if (Application.Current.MainPage.Navigation.NavigationStack.Count > 1)
{
currentPage = Application.Current.MainPage.Navigation.NavigationStack[Application.Current.MainPage.Navigation.NavigationStack.Count - 1];
currentPageis = currentPage.GetType().Name == nameof(RoomPage);
if (currentPageis)
{
_ = await Application.Current.MainPage.Navigation.PopAsync();
}
}
break;
case HubMessageType.NewUserJoinedCall:
break;
case HubMessageType.UserNotAvailableForVideoCall:
bool isInVideoCallPage = Application.Current.MainPage is RoomPage;
if (isInVideoCallPage)
{
_ = await Application.Current.MainPage.Navigation.PopAsync();
}
GlobalVariables.UserIsAvailable = false;
break;
case HubMessageType.UserNotOnline:
break;
default:
break;
}
}
public async Task Disconnect()
{
await hubConnection.StopAsync();
}
public async Task<string> SendMessage(string toUser, HubMessage msg)
{
try
{
await hubConnection.InvokeAsync("SendChatMessageAsync", msg, toUser);
return "OK";
}
catch (Exception X)
{
return X.Message;
}
}
}
Now when I log in:
try
{
GlobalVariables.connectionService = new HubConnectionService("Bearer " + loginresult.IdentityToken);
ConnectionResult connectionResult = await GlobalVariables.connectionService.ConnectAsync();
// 10 attempts to connect to chat hub
for (int i = 0; i < 10; i++)
{
if (connectionResult == ConnectionResult.Disconnected)
{
await GlobalVariables.connectionService.Disconnect();
connectionResult = await GlobalVariables.connectionService.ConnectAsync();
}
else
{
_ = GlobalVariables.connectionService.AddRecivePoints();
break;
}
}
if (connectionResult == ConnectionResult.Disconnected)
{
await DisplayAlert("Error", "Chat service failed restart is required", "Okay");
}
}
catch (Exception x)
{
await DisplayAlert("Error", x.Message, "Okay");
}
Sending Messages
_ = await GlobalVariables.connectionService.SendMessage(toUser, hubMessage);
The only places where I was able to pinpoint the bug is when I tries to reconnect.
When I call -> Close Call -> then receive call
At both these mentiond flows
case HubMessageType.VideoCallInvitation: block won't execute, but the navigation gets stuck half way with partially black screen. I think its something to do with threads, stuck in these for weeks
I am struggling with converion back to generic collection type in generic method with return type.
In this answer is clearly written to avoid
switching on a type in a generic
But in this answer is explained how to do it.
I admit, that I used generics to avoid writing three times the same code for three different return types. Right now the compiler is telling me, that
not all code paths return a value
Example of caller methods caller:
public ObservableCollection<LoadedJockey> Jockeys { get; private set; }
Jockeys = await _updateDataService.UpdateDataAsync(Jockeys, DataUpdateModules.JPlFrom, DataUpdateModules.JPlTo, "updateJockeysPl");
My generic method looks like this:
public async Task<ObservableCollection<T>> UpdateDataAsync<T>(ObservableCollection<T> genericCollection, int idFrom, int idTo, string jobType) where T : IConvertible
{
//variables reset here
_loopCounterProgressBar = 0;
_idFromProgressBar = idFrom;
_idToProgressBar = idTo;
if (typeof(T) == typeof(LoadedHorse))
{
//do something here
}
else if (typeof(T) == typeof(LoadedJockey))
{
//do something here
}
else if (typeof(T) == typeof(LoadedHistoricalRace))
{
//do something here
}
//initial
SemaphoreSlim throttler = new SemaphoreSlim(_degreeOfParallelism);
List<Task> tasks = new List<Task>();
TokenSource = new CancellationTokenSource();
CancellationToken = TokenSource.Token;
OnProgressBarTick();
//run loop
for (int i = idFrom; i < idTo; i++)
{
int id = i;
tasks.Add(Task.Run(async () =>
{
try
{
if (CancellationToken.IsCancellationRequested)
return;
await throttler.WaitAsync(TokenSource.Token);
if (jobType.Contains("Horses"))
{
await //call service method
}
else if (jobType.Contains("Jockeys"))
{
await //call service method
}
else if (jobType.Contains("Historic"))
{
await //call service method
}
}
catch (Exception e)
{
}
finally
{
_loopCounterProgressBar++;
EventHandler<UpdateBarEventArgs> progressBarTick = _updateProgressEventHandler;
OnProgressBarTick();
throttler.Release();
}
}));
}
try
{
await Task.WhenAll(tasks);
}
catch (OperationCanceledException)
{
}
finally
{
//save results in file when finish
}
}
//here I wanted to return some result depending on T
if (typeof(T) == typeof(LoadedHorse))
{
return (ObservableCollection<T>)Convert.ChangeType(Horses, typeof(ObservableCollection<T>));
}
else if (typeof(T) == typeof(LoadedJockey))
{
return (ObservableCollection<T>)Convert.ChangeType(Jockeys, typeof(ObservableCollection<T>));
}
else if (typeof(T) == typeof(LoadedHistoricalRace))
{
return (ObservableCollection<T>)Convert.ChangeType(Races, typeof(ObservableCollection<T>));
}
}
As you can see, I expect to receive 3 different types of T. And I think that I covered all of them. My solution is based on this example. I am guessing, that I may Convert wrongly my type to T, but I do not how I am supposed to do it.
As Camilo Terevinto adviced, I added at the end of if else chain else { throw new ArgumentException() } and compiler was pleased.
I want to make a Wifimanager class for UWP (Raspberry pi 3)
I looked at some tutorials and figured i was good to go.
So I came up with this:
public class WifiManager
{
private WiFiAdapter adapter;
public List<WifiNetwork> Networks;
public WifiManager()
{
Initialize();
}
private async void Initialize()
{
var access = await WiFiAdapter.RequestAccessAsync();
if (access != WiFiAccessStatus.Allowed)
{
throw new WifiAdaperAccessDeniedException();
}
else
{
var result = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(WiFiAdapter.GetDeviceSelector());
if (result.Count >= 1)
{
adapter = await WiFiAdapter.FromIdAsync(result[0].Id);
}
else
{
throw new NoWifiAdapterFoundException();
}
}
}
public async Task GetAvailableNetWorksAsync()
{
try
{
if (adapter != null)
{
await adapter.ScanAsync();
}
}
catch (Exception err)
{
throw new WifiAdaperAccessDeniedException();
}
Networks = new List<WifiNetwork>();
foreach(var network in adapter.NetworkReport.AvailableNetworks)
{
Networks.Add(new WifiNetwork(network, adapter));
}
}
}
However when I try to get the Available Networks using the async function the adapter is null. When I remove the if statement around await adapter.ScanAsync(); I get an AccessViolation.
I dont have much experience with async tasks and such, but the tutorials i found did not gave me the explaination i needed to fix this.
The problem is that you are calling an async method from the constructor. Since you do no wait for the result and are probably calling GetAvailableNetWorksAsync directly after the constructor the adapter variable has not been set yet..
You need to take it out of the constructor like this:
public class WifiManager
{
private WiFiAdapter adapter;
public List<WifiNetwork> Networks;
public async Task InitializeAsync()
{
var access = await WiFiAdapter.RequestAccessAsync();
if (access != WiFiAccessStatus.Allowed)
{
throw new WifiAdaperAccessDeniedException();
}
else
{
var result = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(WiFiAdapter.GetDeviceSelector());
if (result.Count >= 1)
{
adapter = await WiFiAdapter.FromIdAsync(result[0].Id);
}
else
{
throw new NoWifiAdapterFoundException();
}
}
}
public async Task GetAvailableNetWorksAsync()
{
try
{
if (adapter != null)
{
await adapter.ScanAsync();
}
}
catch (Exception err)
{
throw new WifiAdaperAccessDeniedException();
}
Networks = new List<WifiNetwork>();
foreach(var network in adapter.NetworkReport.AvailableNetworks)
{
Networks.Add(new WifiNetwork(network, adapter));
}
}
}
Now your calling code probably looks like this:
var wifiManager = new WifiManager();
await wifiManager.GetAvailableNetWorksAsync();
change that to
var wifiManager = new WifiManager();
await wifiManager.InitializeAsync();
await wifiManager.GetAvailableNetWorksAsync();
Take away: do not call async methods in a constructor since you cannot await completion using the await keyword and using .Wait() or .Result might give other troubles . Applies for 99% of all situations.
Another approach could be something like:
public class WifiManager
{
private WiFiAdapter adapter;
public List<WifiNetwork> Networks;
pivate async Task InitializeAsync()
{
var access = await WiFiAdapter.RequestAccessAsync();
if (access != WiFiAccessStatus.Allowed)
{
throw new WifiAdaperAccessDeniedException();
}
else
{
var result = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(WiFiAdapter.GetDeviceSelector());
if (result.Count >= 1)
{
adapter = await WiFiAdapter.FromIdAsync(result[0].Id);
}
else
{
throw new NoWifiAdapterFoundException();
}
}
}
public async Task GetAvailableNetWorksAsync()
{
try
{
if (adapter == null)
{
await InitializeAsync();
}
if (adapter != null)
{
await adapter.ScanAsync();
}
}
catch (Exception err)
{
throw new WifiAdaperAccessDeniedException();
}
Networks = new List<WifiNetwork>();
foreach(var network in adapter.NetworkReport.AvailableNetworks)
{
Networks.Add(new WifiNetwork(network, adapter));
}
}
}
I have class the --- core of the class skeleton is give below:-
class Pingdom
{
public static string Pingdom(List<Config> configtypes)
{
StringBuilder response = new StringBuilder();
bool status = false;
foreach(var c in configtypes)
{
switch(c.Type)
{
case ConfigTypes.Database:
{
status = PingdomDB(c.ConnectionType);
}
break;
case ConfigTypes.API:
{
status = PingdomAPI(c.Endpoint);
}
break;
}
}
if (status)
return "Ping";
else
return "No Ping";
}
-------------------------------------------------------
.......................................................
}
Now, instead of the class being static I would like for it to be in such way that I can take more of an asynchronous approach in a more robust manner.
Essentially, obtain the list of configurations but process them asynchronously.
How to go about this approach?
class Pingdom {
public static string PingMe(List<Config> configtypes)
{
bool status = true;
List<Task> tasks2 = new List<Task>();
foreach (Config config in configtypes)
{
if (config.Type == ConfigTypes.Database)
{
tasks2.Add(Task.Factory.StartNew(() => { status = status && PingdomDB(config.ConnectionType); }, TaskCreationOptions.LongRunning));
}
else if (config.Type == ConfigTypes.API)
{
tasks2.Add(Task.Factory.StartNew(() => { status = status && PingdomAPI(config.ConnectionType); }, TaskCreationOptions.LongRunning));
}
}
Task.WaitAll(tasks2.ToArray(), System.Threading.Timeout.Infinite);
if (status)
return "Ping";
else
return "No Ping";
}
}
I have a method, shown below, which calls a service.
How can I run this method through thread?
public List<AccessDetails> GetAccessListOfMirror(string mirrorId,string server)
{
List<AccessDetails> accessOfMirror = new List<AccessDetails>();
string loginUserId = SessionManager.Session.Current.LoggedInUserName;
string userPassword = SessionManager.Session.Current.Password;
using (Service1Client client = new Service1Client())
{
client.Open();
accessOfMirror = client.GetMirrorList1(mirrorId, server, null);
}
return accessOfMirror;
}
In C# 3.5 or 4.0 you can do this.
var task = Task.Factory.StartNew<List<AccessDetails>>(() => GetAccessListOfMirror(mirrorId,server))
.ContinueWith(tsk => ProcessResult(tsk));
private void ProcessResult(Task task)
{
var result = task.Result;
}
In C# 4.5 there's the await/async keywords which is some sugar for above
public async Task<List<AccessDetails>> GetAccessListOfMirror(string mirrorId,string server)
var myResult = await GetAccessListOfMirror(mirrorId, server)
Try something like this:
public async Task<List<AccessDetails>> GetAccessListOfMirror(string mirrorId, string server)
{
List<AccessDetails> accessOfMirror = new List<AccessDetails>();
string loginUserId = SessionManager.Session.Current.LoggedInUserName;
string userPassword = SessionManager.Session.Current.Password;
using (Service1Client client = new Service1Client())
{
client.Open();
Task<List<AccessDetails>> Detail = client.GetMirrorList1(mirrorId, server, null);
accessOfMirror = await Detail;
}
return accessOfMirror;
}
Below is a helper class I use, it references RX.NET.
If you include that in your project, then you can thread stuff very simply - the code above you could spin off to a separate thread as follows:
int mirrorId = 0;
string server = "xxx";
ASync.Run<List<AccessDetails>>(GetAccessListOfMirror(mirrorId,server), resultList => {
foreach(var accessDetail in resultList)
{
// do stuff with result
}
}, error => { // if error occured on other thread, handle exception here });
Worth noting: that lambda expression is merged back to the original calling thread - which is very handy if you're initiating your async operations from a GUI thread for example.
It also has another very handy method: Fork lets you spin off multiple worker threads and causes the calling thread to block until all the sub-threads are either complete or errored.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Concurrency;
namespace MyProject
{
public static class ASync
{
public static void ThrowAway(Action todo)
{
ThrowAway(todo, null);
}
public static void ThrowAway(Action todo, Action<Exception> onException)
{
if (todo == null)
return;
Run<bool>(() =>
{
todo();
return true;
}, null, onException);
}
public static bool Fork(Action<Exception> onError, params Action[] toDo)
{
bool errors = false;
var fork = Observable.ForkJoin(toDo.Select(t => Observable.Start(t).Materialize()));
foreach (var x in fork.First())
if (x.Kind == NotificationKind.OnError)
{
if(onError != null)
onError(x.Exception);
errors = true;
}
return !errors;
}
public static bool Fork<T>(Action<Exception> onError, IEnumerable<T> args, Action<T> perArg)
{
bool errors = false;
var fork = Observable.ForkJoin(args.Select(arg => Observable.Start(() => { perArg(arg); }).Materialize()));
foreach (var x in fork.First())
if (x.Kind == NotificationKind.OnError)
{
if (onError != null)
onError(x.Exception);
errors = true;
}
return !errors;
}
public static void Run<TResult>(Func<TResult> todo, Action<TResult> continuation, Action<Exception> onException)
{
bool errored = false;
IDisposable subscription = null;
var toCall = Observable.ToAsync<TResult>(todo);
var observable =
Observable.CreateWithDisposable<TResult>(o => toCall().Subscribe(o)).ObserveOn(Scheduler.Dispatcher).Catch(
(Exception err) =>
{
errored = true;
if (onException != null)
onException(err);
return Observable.Never<TResult>();
}).Finally(
() =>
{
if (subscription != null)
subscription.Dispose();
});
subscription = observable.Subscribe((TResult result) =>
{
if (!errored && continuation != null)
{
try
{
continuation(result);
}
catch (Exception e)
{
if (onException != null)
onException(e);
}
}
});
}
}
}