Xamarin App does nothing after await the GetAsync method - xamarin.forms

I have created a class, in Xamarin Forms, that requests to a web API about the details of countries. Right now, I am testing only with a country.
However, once the line "HttpResponseMessage response = await client.GetAsync(uri);" is executed, the app does nothing additional.
In order to validate if the app executes other instructions after this line, I have included additional lines to write the sequence into the device log.
All the lines before the previous instruction are written into the log and none of them after this instruction.
On the other hand, the screen of the device remains blank, without insert the Label created by code.
Here is the code of the class and the method that consumes the API.
...
using Android.Util;
using System.Net.Http;
using System.Threading.Tasks;
class Country
{
public int iIdCountry { get; set; }
public string sCountryName { get; set; }
public string sCountryIsoCode { get; set; }
public string sCountryPhoneCode { get; set; }
public bool bCountryContainsPrefix { get; set; }
public bool bCountryActive { get; set; }
private static readonly HttpClient client = new HttpClient();
public static async Task<Country> GetCountryAsync(int id)
{
string baseUri = new BaseUri().baseUri;
string sufixUri = "/CountriesApi/GetItem/" + id;
var uri = baseUri + sufixUri;
string tag = "myapp";
Country country = null;
HttpResponseMessage response = await client.GetAsync(uri);
Log.Info(tag, "Response received");
if (response.IsSuccessStatusCode)
{
country = await response.Content.ReadAsAsync<Country>();
Log.Info(tag, "Country received");
}
Log.Info(tag, "Country returned");
return country;
}
}
...
This is the component that makes the call to the previous class:
...
using Android.Util;
using System;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace EnubexMobile.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class Start : ContentPage
{
public Start()
{
var stackLayout = new StackLayout();
string tag = "myapp";
InitializeComponent();
Log.Info(tag, "Started");
var country = Country.GetCountryAsync(1).GetAwaiter().GetResult();
Log.Info(tag, country.sCountryName);
var label = new Label() { Text = country.sCountryName };
Log.Info(tag, "Label created");
stackLayout.Children.Add(label);
Log.Info(tag, "Label added");
}
private void InitializeComponent()
{
throw new NotImplementedException();
}
}
}
...
The idea is to insert a label into the StackLayout with the name of the country received from the API.
Could someone help me to understand what I am missing here?
Thanks !
Regards,

Related

Auto Mapper Constructor initialization Mapping Issue

I have the following Mapping configurations:-
Initialized Data:-
private static IEnumerable<Source> InitializeData()
{
var source= new[]
{
new Source("John", "Doe", "1111111111"),
new Source("Jack", "Handsome", "2222222222"),
new Source("Joe", "Mackenze", "3333333333")
};
return source;
}
Source Model:
public class Source
{
private string First { get; set; }
private string Last { get; set; }
private string Phone { get; set; }
public Source(string first, string last, string phone)
{
First = first;
Last = last;
Phone = phone;
}
}
Destination Model
public class Destination
{
public string First { get; set; }
public string Last { get; set; }
public string Phone { get; set; }
}
Main
static void Main(string[] args)
{
var config = new MapperConfiguration(cfg =>
{
cfg.AllowNullCollections = true;
cfg.CreateMap<Source, Destination>().ReverseMap();
});
var mapper = new Mapper(config);
var source= InitializeData();
var people = mapper.Map<IEnumerable<Destination>>(source);
foreach (var p in people)
{
Console.WriteLine("Name: {0}-{1} Phone: {2}", p.First, p.Last, p.Phone);
}
Console.ReadLine();
}
Problem descriptions:
I have been struggled to understand the AutoMapper mapping between source and destination models.
My source model has a constructor to initialize or accept data from outside. It works fine when I removed the source constructor from the model that's mean flat mapping works fine but constructor initialization has the issue. When I debug in VS2019, it shows the number of records but all fields are empty/null.
What is wrong with the above mapping. I have gone through the AutoMapper reference docs but do not get a hold on this issue.
I highly appreciate your help!
Try calling AssertConfigurationIsValid. Check http://docs.automapper.org/en/latest/Configuration-validation.html.
Your Source properties are private. I assume you meant public.

System.Text.Json Deserialize Fails

With this DTO:
public class QuestionDTO {
public Guid Id { get; set; }
public string Prompt { get; set; }
public List<Answer> Choices { get; set; }
public QuestionDTO() {
}
public QuestionDTO(Question question) {
this.Id = question.Id;
this.Prompt = question.Prompt;
this.Choices = question.Choices;
}
}
I was getting an error about Unable to Parse without a parameterless constructor. I have since fixed that, but now my objects are de-serialized empty:
using System.Text.Json;
var results = JsonSerializer.Deserialize<List<QuestionDTO>>(jsonString);
The jsonString contains 3 items with the correct data, and the deserialized list contains 3 items, but all the properties are empty.
The new json library is case sensitive by default. You can change this by providing a settings option. Here is a sample:
private JsonSerializerOptions _options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }
private async Task SampleRequest()
{
var result = await HttpClient.GetStreamAsync(QueryHelpers.AddQueryString(queryString, queryParams));
_expenses = await JsonSerializer.DeserializeAsync<List<Common.Dtos.Expenses.Models.Querys.ExpensesItem>>(result, _options);
}

SignalR list of object undefined

I am trying to display on the console a list of object But it doesn't show the objects...
Here is the javascript I use to display the object received from the server :
connection.on("ReceiveLog", function (chatMessages) {
console.log(chatMessages);
for (var item in chatMessages) {
// work with key and value
var encodedMsg = item.User + " says " + item.Message;
var li = document.createElement("li");
li.textContent = encodedMsg;
document.getElementById("messagesList").appendChild(li);
} });
The server is sending a list of ChatMessage. Here is the ChatMessage class :
public class ChatMessage
{
string User { get; set; }
string Message { get; set; }
public ChatMessage(string user, string message)
{
this.User = user;
this.Message = message;
}
}
Why are my objects completely broken ? When I break the code on the server side, it really sends the list correctly. The problem seems to be from the javascript or maybe I need to serialize from the server side ?
I needed to set all the property of the object Public like so :
public class ChatMessage
{
public string User { get; set; }
public string Message { get; set; }
public ChatMessage(string user, string message)
{
this.User = user;
this.Message = message;
}
}
It works now.

Web API Post giving me error

I have a web API application. I'm supposed to do a post to an endpoint. When l tried my API controller in postman, l get the error message "
Requested resource does not support HTTP 'POST'
I'm new to Web API so any help and suggestions are welcomed.
This is my model class:
namespace Products.Models
{
public class Prouct
{
public string ProductID { get; set; }
public string ProductName { get; set; }
public string ProductPrice { get; set; }
public string VoucherID { get; set; }
}
}
Here is my controller class
[RoutePrefix("api/products")]
public class ProductsController : ApiController
{
static HttpClient client = new HttpClient();
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
public string Get(int id)
{
return "value";
}
[Route("products")]
public async Task PostAsync(string ProductID, string ProductName, string ProductPrice,
string VoucherID)
{
Products p = new Products();
p.ProductID = ProductID;
p.ProductName = ProductName;
p.ProductPrice = ProductPrice;
p.VoucherID = VoucherID;
var client = new HttpClient { BaseAddress = new
Uri("http://localhost:51613/") };
var response = await
client.PostAsJsonAsync("api/products",
p);
if (response.IsSuccessStatusCode)
{
}
public void Put(int id, [FromBody]string value)
{
}
public void Delete(int id)
{
}
You need to specify HttpPost on PostAsync method. by default, it is [HttpGet].
[HttpPost]
[Route("products")]
public async Task PostAsync(string ProductID, string ProductName, string ProductPrice, string VoucherID)
{
// implementation
}
Looks like you're stuck in a loop. Why does the PostAsync method call itself after having been invoked? This will result in an endless request loop.
var client = new HttpClient { BaseAddress = new Uri("http://localhost:51613/") };
This is not related to the fact that the [HttpPost] attribute is required however.
Please observe that you are supposed to use [FromBody] . Also inside Postman (image attached) you have to choose "Raw" data with the product json with type as JSON(application.json).
[HttpPost]
[Route("products")]
public async Task PostAsync([FromBody] Products p)
{
var client = new HttpClient
{
BaseAddress = new
Uri("http://localhost:51613/")
};
var response = await
client.PostAsJsonAsync("api/products",
p);
if (response.IsSuccessStatusCode)
{
}
}

NullReferenceException for DependencyService on Android

I am attempting to get the phone service information on a Xamarin project.
When I call var carrierHelper = DependencyService.Get<ICarrierHelper>();, I get a System.NullReferenceException: Object reference is not set to an instance of an object.
ICarrierHelper.cs
using System;
namespace MyProject
{
public interface ICarrierHelper
{
string GetPhoneCarrier();
}
}
MyProject.Droid/Helpers/CarrierHelper.cs
using MyProject.Droid;
using Android.Content;
using Android.Telephony;
using Xamarin.Forms;
[assembly: Dependency(typeof(CarrierHelper))]
namespace MyProject.Droid
{
public class CarrierHelper : ICarrierHelper
{
public string GetPhoneCarrier()
{
Console.WriteLine("****************************** MAIN ACTIVITY " + MainActivity.Current.ToString());
TelephonyManager mgr = MainActivity.Current.GetSystemService(Context.TelephonyService) as TelephonyManager;
Console.WriteLine("****************************** TELEPHONY MANAGER " + mgr.ToString());
return mgr.NetworkOperatorName;
//return mgr.SimOperatorName;
}
}
}
I have breakpoints in the Droid CarrierHelper.cs that never catch. So, I can only assume that the DependencyService can't find my file at all.
UPDATE (Adding more information and console logs.)
MyDevice.cs
using System;
using Plugin.DeviceInfo;
using Xamarin.Forms;
namespace MyProject
{
public class MyDevice
{
private static MyDevice current;
public static MyDevice Current
{
get
{
if (current == null)
current = new MyDevice();
return current;
}
}
public string platform { get; set; }
public string manufacturer { get; set; }
public string model { get; set; }
public string name { get; set; }
public string os_version { get; set; }
public string carrier { get; set; }
public string app_version { get; set; }
public string app_build { get; set; }
public MyDevice()
{
switch (Device.RuntimePlatform)
{
case Device.iOS:
platform = "iOS";
break;
case Device.Android:
platform = "Android";
break;
default:
platform = "UNKNOWN";
break;
}
manufacturer = CrossDeviceInfo.Current.Manufacturer;
model = CrossDeviceInfo.Current.Model;
name = CrossDeviceInfo.Current.DeviceName;
os_version = CrossDeviceInfo.Current.Version;
app_version = CrossDeviceInfo.Current.AppVersion;
app_build = CrossDeviceInfo.Current.AppBuild;
var carrierHelper = DependencyService.Get<ICarrierHelper>();
Console.WriteLine("----- CARRIER HELPER " + carrierHelper);
carrier = DependencyService.Get<ICarrierHelper>().GetPhoneCarrier();
Console.WriteLine("----- CARRIER: " + carrier);
}
}
}
The following Console logs print:
----- CARRIER HELPER MyProject.Droid.CarrierHelper
UPDATE (Adding stack trace and info.)
Here is the stack trace:
at MyProject.Droid.CarrierHelper.GetPhoneCarrier () [0x00001] in /MyProject/Helpers/CarrierHelper.cs:14
at MyProject.MyDevice..ctor () [0x000d2] in /MyProject.Core/Models/MyDevice.cs:57
at MyProject.MyDevice.get_Current () [0x0000d] in /MyProject.Core/Models/MyDevice.cs:15
at MyProject.UserDataStore+d__16.MoveNext () [0x00057] in /MyProject.Core/Services/UserDataStore.cs:197
UserDataStore.cs
public async Task UpdateLocation(double lat, double lng)
{
try
{
if (!CrossConnectivity.Current.IsConnected)
throw new Exception("Not currently connected to the internet.");
var model = new
{
access_token = $"{Settings.AuthToken}",
latitude = lat,
longitude = lng,
device = MyDevice.Current,
format = "json"
};
var json = JsonConvert.SerializeObject(model);
var location_content = new StringContent(json, System.Text.Encoding.UTF8, "application/json");
var response = await client.PostAsync(users_url + location_url, location_content);
if (!response.IsSuccessStatusCode)
throw new Exception(response.StatusCode.ToString() + " " + response.ReasonPhrase);
}
catch (Exception e)
{
Console.WriteLine("ERROR: " + e.Message); // this is where the stack trace is generated
await LocationDatabase.Connection.SaveLocationAsync(new Location(lat, lng));
await AppEventDatabase.Connection.SaveEventAsync(new AppEvent("location", e.Message, lat, lng));
}
}
This is the error message from the file above:
ERROR: Object reference not set to an instance of an object.
You have a catch-22 situation. GetPhoneCarrier() requires MyDevice.Current which is implemented via a lazy-loaded constructor for MyDevice. That constructor resolves ICarrierHelper and calls GetPhoneCarrier(), which requires MyDevice.Current...
I'm assuming that the resolution works out that it has already tried resolving that, and it hasn't completed, so it returns null.
I would suggest that you do less in the MyDevice constructor. Avoid resolving that dependency until you actually need it. That way, the constructor can complete, avoiding the catch-22.
Also beware that your approach to lazy loading isn't thread safe. Consider using Lazy<T> which nicely encapsulates double-checked thread-safe locking.

Resources