Xamarin - Using httpClient in .Net Standard Library - c#

I've created Xamarin project, and I added several .Net standard class libraries to it (one for each layer: data access, service layer etc.). Then, In ServiceLayer project, I implemented method, which fetch data from my Web API (external ASP Net Core project). When it comes to httpClient.GetAsync(), android app crash. What's more, when I cut this piece of code, and paste it in default xamarin .Net standard library, everything works. Any ideas?
Code is here:
HttpClient httpClient = new HttpClient();
var responseMessage = await httpClient.GetStringAsync(uri);
UPDATE:
In Viewmodel:
constructor(IServiceLayerService service){
_ServiceLayerService = service;
GetTestCommand = new DelegateCommand(async () => await GetTests());
}
public async Task GetTests()
{
TestObservableCollection = new ObservableCollection<List<Test>>(await _ServiceLayerService.GetTestModels());
}
Update 2:
I've changed my async method call, in the way presented in first answer. Now, when I'm trying to execute code, app also crashes, but I'm receiving error messages:
07-05 14:39:04.518 F/ (25383): /Users/builder/jenkins/workspace/xamarin-android-d15-7/xamarin-android/external/mono/mono/mini/debugger-agent.c:4897: Could not execute the method because the containing type is not fully instantiated. assembly:<unknown assembly> type:<unknown type> member:(null) signature:<none>
07-05 14:39:04.518 F/libc (25383): Fatal signal 6 (SIGABRT), code -6 in tid 25383 (com.companyname), pid 25383 (com.companyname)
Maybe I'm doing something wrong with Unity dependency injection, so here is registration of service layer classes in App.xaml.cs
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<NavigationPage>();
containerRegistry.RegisterForNavigation<TestsListPage, TestsListViewModel>();
containerRegistry.Register<IServiceLayerService, ServiceLayerService>();
}

The problem is with the async command delegate
GetTestCommand = new DelegateCommand(async () => await GetTests());
The command delegate results in an async void, which wont allow exceptions to be caught as they are considered fire and forget methods
Reference Async/Await - Best Practices in Asynchronous Programming
Create an event and handler to manage the async call
private event EventHandler gettingTests = delegate { };
private async void OnGettingTests(object sender, EventArgs args) {
try {
await GetTests();
} catch (Exception ex) {
//...handle exception
}
}
Note that event handlers are the one exception to the rule that allows async void
Subscribe to the event in the constructor and raise the event in the command delegate
constructor (IServiceLayerService service) {
_ServiceLayerService = service;
this.gettingTests += OnGettingTests;
GetTestCommand = new DelegateCommand(() => gettingTests(this, EventArgs.Empty));
}
So now when the command is invoked, it will raise the event and the async handler can make async calls correctly.

Related

Unable to return a value from SignalR Client from a different method

I'm working on a Winforms app that executes SQL Procedures through a SignalR client. I'm relatively new to using SignalR and am still wrapping my head around it.
I start off by running my connection method to establish a connection with my SignalR service. I have two addresses configured ready for when I puslish but the DEV configuration leads to the SignalR service I am hosting locally.
Connection to SignalR (ConnectHub)
private async Task ConnectHub()
{
string hubAddress = "";
#if DEBUG
HubAddress = ConfigurationManager.AppSettings["HubAddress_DEV"];
#else
HubAddress = ConfigurationManager.AppSettings["HubAddress_PROD"];
#endif
if (string.IsNullOrEmpty(hubAddress))
{
MessageBox.Show("Hub Address is missing from configuration.");
}
ConnectionHandler.Client = new HubClient(hubAddress, "MyHub");
ConnectionHandler.Client.MyAlert += ConnectionHandler.ClientOnMyAlert;
ConnectionHandler.Client.ServerErrorEvent += ConnectionHandler.ClientOnServerErrorEvent;
await ConnectionHandler.Client.Connect(new List<string>() {
VehicleInfo.ThisVehicle.WarehouseCode,
VehicleInfo.ThisVehicle.VehicleName
});
}
My client is stored globally in my ConnectionHandler class where my event handlers are also kept. (I have breakpoints on these as I have not implemented them yet)
ConnectionHandler Class
public static class ConnectionHandler
{
public static HubClient Client { get; set; }
public static void ClientOnServerErrorEvent(string error)
{
throw new NotImplementedException(); //Currently not implemented
}
public static async Task ClientOnMyAlert(EnumMyAlertType alerttype, string message, Exception exception)
{
await Task.Yield(); //Currently not implemented
}
}
When I call the code to Invoke the procedure in my SignalR client, it returns a DataTable to me which is the intended result.
Call to SignalR
await ConnectHub();
DataTable dt = await ConnectionHandler.Client.Connection.InvokeCoreAsync<DataTable>(
"FetchStatuses",
new object[0]); //This call works as intended and returns a populated DataTable
StatusInfo = new CStatuses();
All the above code is currently done on the main form, however I wanted to move this call to SignalR into a constructor to try and tidy things up.
The problem comes when I try to move this call into another method, the program hangs as I don't think it has received the return value from SignalR, I have placed a breakpoint beneath it and it is not reached. A TryCatch reveals nothing as it hangs within the "Try" with no exception.
Calling from contructor
public CStatuses()
{
Statuses = new List<CStatus>();
var dataTable = ConnectionHandler.Client.Connection.InvokeCoreAsync<DataTable>("FetchStatuses",
new object[0])
.Result; //My program hangs on this line and proceeds no further
I am at a loss as to why it is doing this when I can get a value from the client from the form and when other members of my team have tried to do the same thing they can make a call to SignalR also from a different method.
Does anyone have any ideas as to how I can make this work?
I realize this has gotten quite long but if I can elaborate on things please let me know
FIXED CODE THANKS TO SOLUTION:
I have moved the code from my CStatuses constructor into a new async method within the same class and called it after initialization. This removes the need for .Result and appears to solve the problem for me.
public async Task PopulateStatuses()
{
var dataTable = await ConnectionHandler.Client.Connection.InvokeCoreAsync<DataTable>("FetchStatuses",
new object[0]);
Statuses = new List<CStatus>();
foreach (DataRow row in dataTable.Rows)
{
var status = new CStatus
{
StatusId = Common.Utility.GetInt16Value(row["StatusID"]),
StatusCode = Common.Utility.GetStringValue(row["StatusCode"]),
Description = Common.Utility.GetStringValue(row["Description"])
};
Statuses.Add(status);
}
}
You are running into a deadlock with the .Result call, I would suggest creating an async method in the CStatuses class and after you initialize your CStatuses class call the websocket for data.

How to implement delegates that are safe for async

I have a Subscriber.cs that accepts an action, listens to a RabbitMQ-queue, and performs said action on any message from that queue:
public class Subscriber {
public Subscriber(Action<T> consumeMessage)
{
_consumeMessage = consumeMessage;
}
....
void HandleMessage(T message) {
try
{
_consumeMessage(message);
}
catch (Exception e)
{
_logger.LogError("Some error message");
}
}
}
This worked fine, until I (accidentally) provided an async function:
var subscriber = new Subscriber<MyMessage>(
consumeMessage: message =>
{
messageHandler.HandleAsync(message);
});
This executes the action in a 'fire and forget' fashion, which still works when it works, but fails silently when it fails.
So I tried this:
var subscriber = new Subscriber<MyMessage>(
async consumeMessage: message =>
{
await messageHandler.HandleAsync(message);
});
This of course looks pretty, but somehow causes the exception to occur in this snippet (rather than inside Subscriber) causing the whole app to crash.
So I tried this:
var subscriber = new Subscriber<MyMessage>(
consumeMessage: message =>
{
messageHandler.HandleAsync(message).GetAwaiter().GetResult();
});
This works as intended, but might lead to a deadlock (if I am to believe the internet).
It also makes my Subscriber a very unfriendly and dangerous-to-use component as it the two first examples both compile even though they don't actually work.
How should I design my Subscriber component so that it can work with async delegates safely?

Nothing happens when calling method from within SignalR3 rc1 vnext application

In my current setup, i have an ASP.NET 5 vNext project running.
I have setup SignalR Server 3.0.0-rc1-final and i am able to connect to my hub through my webinterface:
var visitorHub = $.connection.visitorsHub;
visitorHub.client.visitorEvent = function (message) {
$("#visitorinfo").append("<li>" + message + "</li>");
};
$.connection.hub.start().done(function () {
visitorHub.invoke("listenToEvents", "TestID");
});
So we are listening on visitorEvent from the hub and visitorEvent is called when listenToEvents is invoked.
My challenge come now, that i'm trying to notify from within the ASP.NET application. Using the build in IoC an SqlDependency is used to listen to events in the SQL server. Again this is working as intended, but when i'm trying to invoke the hub through it's HubContext nothing happens.
I have injected the IConnectionManager and able to get hold of my HubContext using:
var context = this.manager.GetHubContext<VisitorsHub>();
but when i do the following:
context.Clients.All.visitorEvent("2");
nothing happens.
I'm not sure why nothing happens and how i'm going to debug this?
My VisitorHub code is:
public class VisitorsHub : Hub
{
public async Task ListenToEvents(string visitorId)
{
this.NotifyVisitorListeners();
}
public void NotifyVisitorListeners()
{
this.Clients.All.visitorEvent("Event");
}
}
You can enable client side logging via:
$.connection.hub.logging = true;
$.connection.hub.start();
or without generated proxy:
var connection = $.hubConnection();
connection.logging = true;
connection.start();
Otherwise when using invoke (method without a proxy), methods aren't renamed to camelCase, but remain as they are, in your case CamelCase.
To overcome this you can simply rename method name on either end, or add HubMethodName decorator in your backend:
[HubMethodName("listenToEvents")]
public async Task ListenToEvents(string visitorId)
{
this.NotifyVisitorListeners();
}

Xamarin.Android using async method on background thread causes screen flash

So I'm trying to create a loading/splash screen for an app that I'm creating. Basically, if the user isn't authenticated, then they shouldn't be able to access the other parts of the app. Additionally, I'd like the app to attempt to sync the necessary database objects before it loads up the main activity.
The problem is that when I call the Authenticate() method and the InitLocalStoreAsync() methods, the screen flashes (almost like an activity reload, or like the app is doing something that I don't understand that's hiding the activity) while the methods are executing. I'd like that not to happen.
I'm very new to Android App Dev and even newer to Xamarin.
I'm using modified code that comes from the Azure Mobile Services tutorial on authentication etc.
Should I be somehow executing these methods using RunOnUiThread? If so, how do I await in conjunction with RunOnUiThread? Or should I be doing this in a completely different way?
I'm very lost. I've tried to search and find tutorials to follow, but I can't seem to find the answer. Here's the code so far:
protected override async void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
SetContentView (Resource.Layout.Activity_Splash);
// Create your application here
try{
CurrentPlatform.Init ();
// Create the Mobile Service Client instance, using the provided
// Mobile Service URL and key
client = new MobileServiceClient (applicationURL, applicationKey);
statusText = FindViewById<TextView> (Resource.Id.SplashStatusText);
ThreadPool.QueueUserWorkItem(x => Initialize());
}catch(Java.Net.MalformedURLException){
CreateAndShowDialog (new Exception ("There was an error creating the Mobile Service. Verify the URL"), "Error");
}catch(Exception e) {
CreateAndShowDialog (e, "Error");
}
}
private async void Initialize()
{
RunOnUiThread(() => statusText.Text = "Authenticating...");
await Authenticate();
RunOnUiThread (() => statusText.Text = "Syncing...");
await InitLocalStoreAsync();
MoveToMainActivity();
}
private async Task Authenticate()
{
try
{
user = await client.LoginAsync(this, MobileServiceAuthenticationProvider.Google);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
private async Task InitLocalStoreAsync()
{
// new code to initialize the SQLite store
string path = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), localDbFilename);
if (!File.Exists(path))
{
File.Create(path).Dispose();
}
var store = new MobileServiceSQLiteStore(path);
store.DefineTable<ToDoItem>();
// Uses the default conflict handler, which fails on conflict
// To use a different conflict handler, pass a parameter to InitializeAsync. For more details, see http://go.microsoft.com/fwlink/?LinkId=521416
await client.SyncContext.InitializeAsync(store);
}
How do I restructure this so that I don't get any screen flashes?
If you want to run an asynchronous method you have to use the Task Factory:
RunOnUiThread(() => statusText.Text = "Loading.");
Task.Run(() => AsyncWork()).ContinueWith(result => RunOnUiThread(() => statusText.Text = "Done!"));
The screen flashes i think it could be 2 things, the app crashed and is trying to recover the last activity or your are trying to update elements on the UI thread and doing processing/work too, so it might be "stutter".

C# winform application with threaded WCF client

I'm building an application that uses a WCF client to retrieve data from my server.
I want my call to the service to be asynchronous because many of them need to change the UI and I don't want to lose responsiveness from my app.
I tried using *Completed and *Async:
ServiceUserClient client = new ServiceUserClient();
client.FindUserCompleted += delegate(object sender, FindUserCompletedEventArgs e)
{
// here e.Result always fails
};
client.FindUserAsync(text);
Inside the *Completed delegate I always get an error (Connection closed by remote host: I enabled every logging I could find but I still don't understand why I get these errors)
Synchronous calls always work.
I have a class that handles all the calls to the service.
Is there a way to have syncronous calls inside something like a threaded class?
Are you setting the client side bindings to match what the server accepts?
You should also try testing it with the WCF test client (normally under %Program Files%\Microsoft Visual Studio 10.0\Common7\IDE\WcfTestClient.exe). If the test client works then check the bindings.
Is your call even getting to the server? I've had similar errors happen when serializing the response from the server to the client, so you might want to check for that. If you get to your server then the bindings are not the problem but rather there is a serialization problem. Do you have "sets" on the data model properties that are trying to get deserialized on the server?
I know this is no answer but I haven't been here enough to be allowed comments...and I've been where you are, totally frustrating.
I ended up creating my own async methods using BackgroundWorker this way (probably not the best way but it works):
// this is the click event on my search button
private void FindUser_Click(object sender, EventArgs e)
{
this.UserListSearch.Enabled = false;
this.UserListSearch.Items.Clear();
Model.FindUser(FindText.Text.ToUpper(), userlist =>
{
foreach (User u in userlist)
{
ListViewItem item = new ListViewItem(u.UserName);
item.Name = u.UserName;
item.SubItems.Add(u.Description);
this.UserListSearch.Items.Add(item);
}
this.UserListSearch.Enabled = true;
});
}
// this is the function I call when I need async call
public void FindUser(string text, Action<User[]> callback)
{
CreateBackgroundWorker<User[]>(() =>
{
ServiceUsersClient client = new ServiceUsersClient();
var results = client.FindUser(text);
client.Close();
return results;
}, callback);
}
// this is my utility function to create a bgworker "on demand"
private void CreateBackgroundWorker<T>(Func<T> dowork, Action<T> callback)
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += (sender, args) =>
{
T result = dowork.Invoke();
(callback.Target as Form).Invoke(callback, result);
};
worker.RunWorkerAsync();
}

Categories

Resources