UserDialogs loading does not pop up - c#

I wonder what I am doing wrong in the following implementation.
I cannot able to see loading dialog, even to opening the ClassroomViewModel takes few seconds.
public IMvxCommand ClassroomSelectedCommand => new MvxAsyncCommand<ClassroomViewModel>(ClassroomSelected);
private async Task ClassroomSelected(Model obj)
{
using (UserDialogs.Instance.Loading("Loading..."))
{
try
{
ShowViewModel<ClassroomViewModel>(new { Id = obj.Id });
}
catch (Exception ex)
{
}
}
}

You are using async APIs, use an MvxAsynCommand
private IMvxAsynCommand _classroomSelectedCommand;
public IMvxAsynCommand ClassroomSelectedCommand => _classroomSelectedCommand ?? (_classroomSelectedCommand = new MvxAsyncCommand<ClassroomViewModel>(ClassroomSelectedAsync));
private async Task ClassroomSelectedAsync(Model obj)
{
using (UserDialogs.Instance.Loading("Loading..."))
{
await Task.Delay(300);
try
{
ShowViewModel<ClassroomViewModel>(new { Id = obj.Id });
}
catch (Exception ex)
{
}
}
}

Related

Unable to catch DbUpdateConcurrencyException and throw UserFriendlyException

I am using ASP.NET Boilerplate with Code-First Entity Framework and MVC 5.
I want to handle concurrency. In the Update method, I put Timestamp data annotation for RowVersion field in my entity.
In the manager of my entity and in Update operation, I am trying to catch DbUpdateConcurrencyException exception and throw UserFriendlyException but because UpdateAsync is asynchronous, I don't know where to handle the exception.
Where should I handle this exception to be user-friendly and not to see Internal Server Error?
public abstract class BaseFullAuditedEntity : FullAuditedEntity<Guid>
{
[Timestamp]
public byte[] RowVersion { get; set; }
}
public class Branch : BaseFullAuditedEntity
{
public string Name { get; set; }
}
Manager code:
public interface IBranchManager : IDomainService
{
Task<Branch> Update(Branch branch, byte[] RowVersion);
}
public class BranchManager : DomainService, IBranchManager
{
private IRepository<Branch, Guid> _branchRepository { get; }
public async Task<Branch> Update(Branch branch, byte[] RowVersion)
{
try
{
return await _branchRepository.UpdateAsync(branch);
}
catch (DbUpdateConcurrencyException ex)
{
throw new UserFriendlyException("Update Concurrency Happened");
}
}
}
UpdateAsync(branch) only adds branch to the context.
Inject IUnitOfWorkManager and await SaveChangesAsync():
try
{
await _branchRepository.UpdateAsync(branch);
await _unitOfWorkManager.Current.SaveChangesAsync(); // Add this
return branch;
}
catch (DbUpdateConcurrencyException ex)
{
throw new UserFriendlyException("Update Concurrency Happened");
}
Alternatively, override SaveChanges and SaveChangesAsync in your DbContext to catch for all entities:
public override int SaveChanges()
{
try
{
return base.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
throw new UserFriendlyException("Update Concurrency Happened");
}
}
public override async Task<int> SaveChangesAsync()
{
try
{
return await base.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException ex)
{
throw new UserFriendlyException("Update Concurrency Happened");
}
}
Try catching as an aggregate exception...
(Sometimes, the real exception is being wrapped.)
try
{
// code...
}
catch (Exception ex)
{
if (ex is AggregateException)
{
var exMsg = FlattenAggregate((AggregateException)ex);
throw new UserFriendlyException(exMsg );
}
throw;
}
public static string FlattenAggregate(AggregateException aggregateException)
{
var sbErr = new StringBuilder();
var exceptions = aggregateException.Flatten();
foreach (var exception in exceptions.InnerExceptions)
{
sbErr.AppendLine(exception.ToString());
}
return sbErr.ToString();
}

Throwing FormCancelledException from await instead of AggregateException in unit test

I'm currently trying to test the following code in an application that makes use of the Microsoft Bot Framework.
public async Task ResumeAfterCalculation_v2FormDialog(IDialogContext context, IAwaitable<Calculation_v2Form> result)
{
try
{
var extractedCalculationForm = await result;
//Removed additional code
}
catch (FormCanceledException ex)
{
var reply = "You have canceled the operation.";
await _chat.PostAsync(context, reply);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
finally
{
context.Done<object>(null);
}
}
When a user types 'quit' to the bot the 'await result' code throws a FormCanceledException and the code quits the form.
When creating a test I implemented a class to mock the IAwaitable:
public class TaskAwaiterHelper<T> : IAwaiter<T>, IAwaitable<T>
{
public Task<T> Task { get; }
public TaskAwaiterHelper(T obj)
{
this.Task = System.Threading.Tasks.Task.FromResult(obj);
}
public TaskAwaiterHelper(Task<T> task)
{
this.Task = task;
}
public bool IsCompleted { get { return Task.IsCompleted; } }
public void OnCompleted(Action action)
{
SynchronizationContext context = SynchronizationContext.Current;
TaskScheduler scheduler = context == null ? TaskScheduler.Current
: TaskScheduler.FromCurrentSynchronizationContext();
Task.ContinueWith(ignored => action(), scheduler);
}
public T GetResult()
{
return Task.Result;
}
public IAwaiter<T> GetAwaiter()
{
return this;
}
}
I then created the following test:
[Fact]
public async Task ResumeAfterCalculation_v2FormDialog_WasCancelled_ThenCallsDone()
{
//Arrange
var chat = new Mock<IChatHelper>();
var calculationApi = new Mock<ICalculationApi>();
var dialogContextMock = new Mock<IDialogContext>();
var rootLuisDialog = new RootLuisDialog(chat.Object, calculationApi.Object);
var taskAwaiter = new TaskAwaiterHelper<Calculation_v2Form>(new Task<Calculation_v2Form>(() =>
{
throw new FormCanceledException("Error created for test test", null);
}));
taskAwaiter.Task.Start();
//Act
await rootLuisDialog.ResumeAfterCalculation_v2FormDialog(dialogContextMock.Object, taskAwaiter);
//Assert
chat.Verify(c => c.PostAsync(dialogContextMock.Object, "You have canceled the operation."), Times.Once());
dialogContextMock.Verify(t => t.Done<object>(null), Times.Once());
}
Now whatever I try to do I the exception that's being thrown in the IAwaitable is being wrapped in an AggregateException, so we always end up in the catch (Exception ex) instead of the desired catch (FormCanceledException ex)
Is there a way to make a Task throw a specific Exception instead of an AggregateException (I mean there should be as the bot framework itself seems to be able to do it).
I just found the answer, I basically created a new class:
public class ExceptionThrower : IAwaitable<Calculation_v2Form>
{
public IAwaiter<Calculation_v2Form> GetAwaiter()
{
throw new FormCanceledException("Error created for test test", null);
}
}
And just provided this to the method:
var exceptionThrower = new ExceptionThrower();
await rootLuisDialog.ResumeAfterCalculation_v2FormDialog(dialogContextMock.Object, exceptionThrower);

UserDialogs Loading does not show up

I am trying to see Loading progress as follows, but it does not show up.
View.cs
ViewModel.SelectedCommand.Execute(null);
ViewModel.cs
public ICommand SelectedCommand
{
get
{
return new MvxAsyncCommand(async () =>
{
// the following does not show loading
using (UserDialogs.Instance.Loading("Loading..."))
{
var task = await _classroomService.GetClassRoomAsync(SelectedClassroom.Id);
ObservableCollection<ClassroomViewModel> class = new ObservableCollection<ClassroomViewModel>(task.ConvertAll(x => new ClassViewModel(x)));
}
});
}
}
Another example
public ICommand ReloadCommand
{
get
{
return new MvxAsyncCommand(async () =>
{
await RefreshList();
});
}
}
// the following also does not show loading
private async Task RefreshList()
{
using (UserDialogs.Instance.Loading("Loading..."))
{
var task = await _classService.GetClasses();
}
}
If you are using Acr.MvvmCross.Plugins.UserDialogs see that it's depreated and you should use directly Acr.UserDialogs.
Check if you have correctly initialized it as follows:
You have to register it in App.cs of your PCL project:
Mvx.RegisterSingleton<IUserDialogs>(() => UserDialogs.Instance);
And init from the android platform project in your main activity:
UserDialogs.Init(() => Mvx.Resolve<IMvxAndroidCurrentTopActivity>().Activity)
Another thing to take into account is that you should inject it in your constructor as an IUserDialogs (you can use the static Instance way but it adds more flexibility and it is more testable by injecting it):
private readonly IUserDialogs _dialogs;
public ProgressViewModel(IUserDialogs dialogs)
{
this._dialogs = dialogs;
}
and use it like
private async Task RefreshList()
{
using (this._dialogs.Loading("Loading..."))
{
try
{
var task = await this._classService.GetClasses();
}
catch(Exception exc)
{
// This is done only for debugging to check if here lies the problem
throw exc;
}
}
}
You can check if it is properly working by calling something like
public ICommand MyTestCommand
{
get
{
return new MvxAsyncCommand(async () =>
{
// the following should should Loading for 3 seconds
using (this._dialogs.Loading("Loading..."))
{
await Task.Delay(TimeSpan.FromSeconds(3));
}
});
}
}
HIH
I dont like this approuch but it works
Device.BeginInvokeOnMainThread(async () =>
{
try
{
using (UserDialogs.Instance.Loading(("Loading...")))
{
await Task.Delay(300);
await _syncController.SyncData();
//Your Service code
}
}
catch (Exception ex)
{
var val = ex.Message;
UserDialogs.Instance.Alert("Test", val.ToString(), "Ok");
}
});

Xamarin retry failed data request

Retry action Image
I am building a Xamarin iOS & android App, and I want to implement a retry function to all the failed webcall or in case of disconnection, I already use Polly in the BLL side, and I want to give the user the possibility to retry manually as shown on the above image.
protected List<Task> _taskList;
_taskList.Add(Task.Run(async () =>
{
try
{
**// Webservice Call**
Task<UtilisateurDTO> utilisateurTask = UserFactory.Login(username, pwd,
App.Hardware.GetDeviceId());
UtilisateurDTO utilisateur = await utilisateurTask;
if (utilisateur != null)
{
InvokeOnMainThread(() =>
{
**// Set result to ui component**
});
}
}
catch (Exception ex)
{
InvokeOnMainThread(() =>
{
// Add action button "Retry" to snackBar
_snackBar = new TTGSnackbar("ex.Message", TTGSnackbarDuration.Forever, "Retry", (obj) => {
// **Retry all tasks**
Parallel.ForEach(_taskList, task => task.Start());
});
_snackBar.Show();
});
}
}));
I know that it's not possible to retry completed tasks, and I can't call my web service outside a task (to not block the UI thread), so what's the alternative?
Update with a solution
If you want to handle exceptions and retry in one place, here is my solution (not the best cause it reload everything)
// BaseViewClass
public abstract class BaseViewController:UIViewController
{
// Function to override in child controllers
protected abstract void ReloadData(TTGSnackbar obj);
public void HandleExceptions(Exception e)
{
// On commence par cacher la ProgressBar
InvokeOnMainThread(HideLoadigProgressBar);
if (e is ConnectionLostException)
{
Console.WriteLine("ConnectionLostException: " + e.ToString());
InvokeOnMainThread(() =>
{
_snackBar = new TTGSnackbar("Connection lost !", TTGSnackbarDuration.Forever, "retry", ReloadData);
_snackBar.Show();
});
}
else if (e is TimeoutException)
{
Console.WriteLine("TimeoutException: "+ e.ToString());
InvokeOnMainThread(() =>
{
_snackBar = new TTGSnackbar("TimeoutException", TTGSnackbarDuration.Forever, "Retry", ReloadData);
_snackBar.Show();
});
}
.....................
}
// Other ViewController
public partial class HomeController : BaseViewController
{
public override void ViewDidLoad()
{
base.ViewDidLoad();
LoadData();
}
public void LoadData(){
Task.Run(async () =>
{
try
{
// Web calls
Task<UtilisateurDTO> getUserTask = AccueilFactory.GetUser();
UtilisateurDTO utilisateur = await getUserTask;
// Set UI result
}
catch(Exception ex) {
HandleExceptions(ex);
}
}
}
protected override void ReloadData(TTGSnackbar obj)
{
LoadData();
}
}
You have to call your function again on exception, not only rerun last task.
Your code will be like this:
private void Login()
{
try
{
LoginInner(); // here you call service and update UI
}
catch (Exception ex)
{
InvokeOnMainThread(() =>
{
// Add action button "Retry" to snackBar
_snackBar = new TTGSnackbar("ex.Message", TTGSnackbarDuration.Forever, "Retry", (obj) => {
// **Retry all tasks**
Parallel.ForEach(_taskList, LoginInner); // ** call again loginInner **
});
_snackBar.Show();
});
}
}

WebAPI Client -- handle my own exception rather then AggregateException

I have written a very simple WebApiClient extending HttpClient. The code is following. The main reason to do that was to throw MyOwnWebApiException when httpResponse.IsSuccessStatusCode is false.
public class WebApiClient : HttpClient
{
public WebApiClient(string apiBaseUrl)
{
this.BaseAddress = new Uri(apiBaseUrl);
this.DefaultRequestHeaders.Accept.Clear();
}
public void AddAcceptHeaders(MediaTypeWithQualityHeaderValue header)
{
this.DefaultRequestHeaders.Accept.Add(header);
}
public async Task<string> DoPost(string endPoint, Object dataToPost)
{
HttpResponseMessage httpResponse = await ((HttpClient)this).PostAsJsonAsync(endPoint, dataToPost);
if (httpResponse.IsSuccessStatusCode)
{
string rawResponse = await httpResponse.Content.ReadAsStringAsync();
return rawResponse;
}
else
{
string rawException = await httpResponse.Content.ReadAsStringAsync();
MyOwnWebApiErrorResponse exception =
JsonConvert.DeserializeObject<MyOwnApiErrorResponse>(rawException, GetJsonSerializerSettings());
throw new MyOwnWebApiException (exception.StatusCode,exception.Message,exception.DeveloperMessage,exception.HelpLink);
}
}
#region "Private Methods"
private static JsonSerializerSettings GetJsonSerializerSettings()
{
// Serializer Settings
var settings = new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.All,
ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor,
ObjectCreationHandling = ObjectCreationHandling.Auto
};
return settings;
}
#endregion
Following is the code of the class using WebApiClient.
class TestWebApiClient
{
private WebApiClient _client;
public ComputationProcessesWebApiClient()
{
_client = new WebApiClient("http://test.api/");
_client.AddAcceptHeaders(new MediaTypeWithQualityHeaderValue("application/json"));
}
public void GetData(string dataFor)
{
try
{
DataRequest request = new DataRequest();
request.dataFor = dataFor;
**// THIS LINE IS THROWING AGGREGATEEXCEPTION--- **I WANT MyOwnException ****
string response = _client.DoPost("GetData", request).Result; // Use the End Point here ....
}
catch (MyOwnWebApiException exception)
{
//Handle exception here
}
}
}
Question
In the TestWebApiClient class, i dont want to catch AggregateException, rather i want to keep it more elegent and catch MyOwnWebApiException, but the problem is the line ** _client.DoPost("GetData", request).Result** throws an AggregateException if something goes wrong from the WebApi. How to change the code so that from TestWebApiClient i only have to catch MyOwnException ??
This is as a result of synchronously waiting for your task. If you stay async and await your task instead, you'll find that your actual Exception is the one that is caught.
Compare the following below:
void Main()
{
TryCatch();
TryCatchAsync();
}
void TryCatch()
{
try
{
ThrowAnError().Wait();
}
catch(Exception ex)
{
//AggregateException
Console.WriteLine(ex);
}
}
async Task TryCatchAsync()
{
try
{
await ThrowAnError();
}
catch(Exception ex)
{
//MyException
Console.WriteLine(ex);
}
}
async Task ThrowAnError()
{
await Task.Yield();
throw new MyException();
}
public class MyException:Exception{};
Top hint for async/await? It's async/await all the way down. The moment you .Wait() or .Result on a Task, things start to get messy.

Categories

Resources