NullReferenceException for DependencyService on Android - xamarin.forms

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.

Related

Call a rest web api in asp.net and receive error

Hi I want to call a rest Web Api and I use asp.net MVC+Web Api.
I write a get Token Method like below :
public TokenViewModel GetToken()
{
//string Result = string.Empty;
TokenViewModel token = null;
string baseAddress = "http://$$$$$$$$$$/api/security/login";
using (HttpClient client = new HttpClient())
{
try
{
var url = new Uri(baseAddress);
MultipartFormDataContent form = new MultipartFormDataContent();
Dictionary<string, string> parameters = new Dictionary<string, string>();
parameters.Add("UserName", "###");
parameters.Add("Password", "$$$");
HttpContent DictionaryItems = new FormUrlEncodedContent(parameters);
form.Add(DictionaryItems, "model");
var response = client.PostAsync(url.ToString(), form, System.Threading.CancellationToken.None);
if (response.Result.StatusCode == System.Net.HttpStatusCode.OK)
{
//Get body
var bodyRes = response.Result.Content.ReadAsStringAsync().Result;
token = JsonConvert.DeserializeObject<TokenViewModel>(bodyRes);
//Get Header
// var headers = response.Result.Headers.GetValues("appToken");
}
else
{
var a = response.Result.Content.ReadAsStringAsync().Result;
}
}
catch (Exception ex)
{
}
return token;
}
}
And also webController:
namespace WebAPI.Controllers
{
public class WebApiController : ApiController
{
private readonly GetToken_BLL _tokenService;
public WebApiController(GetToken_BLL tokenService)
{
_tokenService = tokenService;
}
public object Verfiybll { get; private set; }
public class stcAPIMessage
{
public string Message { get; set; }
public HttpStatusCode StatusCode { get; set; }
}
[HttpPost]
[Route("api/Token")]
public IHttpActionResult Token()
{
stcAPIMessage message = new stcAPIMessage();
GetToken_BLL tokenbll = new GetToken_BLL();
var result = tokenbll.GetToken();
if (result == null)
{
message.Message = "error in recieveing token";
message.StatusCode = HttpStatusCode.BadRequest;
return Content(message.StatusCode, message.Message);
}
else if (string.IsNullOrEmpty(result.Token))
{
message.Message = "Error";
message.StatusCode = HttpStatusCode.BadRequest;
return Content(message.StatusCode, message.Message);
}
return Ok(result);
}
}
}
When I run the program it throw out error:
An error occurred when trying to create a controller of type 'Web ApiController'.
Make sure that the controller has a parameter less public constructor.
System. Invalid Operation Exception Type 'WebAPI.Controllers.
Web ApiController' does not have a default constructor
System.
The parameter less constructor error is common in ASP.NET web applications that use dependency injection.
I have noticed there is a constructor parameter being used:
GetToken_BLL _tokenService
Use a dependency injection resolver for the type GetToken_BLL so that the parameter _tokenService can be instantiated.

Xamarin App does nothing after await the GetAsync method

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,

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.

Error retrieving data from Api Controller

I'm working on an ASP.NET Core Api and Xamarin forms client using Visual Studio 2017.
I'm getting an error
System.Runtime.Serialization.SerializationException: Invalid JSON string
because response.Content is null, when retrieving data from API but when paste this Url in browser "https://localhost:44305/api/Agreement/GetAgreementText/1" it shows data in the browser. When I run using client it's not hit to api method debug point .
Here is my APi method
[HttpGet]
[Route("GetAgreementText/{id}")]
public DefaultApiResult GetAgreementText(long Id)
{
Company com = _companyRepository.Get(Id);
string st = com.AgreementText;
DefaultApiResult result = new DefaultApiResult
{
Data = st
};
return result;
}
Here is my client application Api invoking method
public string GetAgreementTextLoading(long idCompany)
{
string agreementText = "";
// var token = _tokenService.GetLastActivateToken().Hash;
var clientURL = "https://localhost:44305/";
var client = new RestClient(clientURL);
var request = new RestRequest("api/Agreement/GetAgreementText/{Id}", Method.GET);
request.AddUrlSegment("Id", idCompany.ToString());
IRestResponse response = client.Execute(request);
AppRestResponse apiResponse = SimpleJson.DeserializeObject<AppRestResponse>(response.Content);
var statusMessage = "";
if (apiResponse.Success)
{
statusMessage = "Success.";
if (!string.IsNullOrEmpty(response.Content))
{
agreementText = apiResponse.Data.ToString();
}
else
{
throw new Exception("Invalid response");
}
}
else
{
agreementText = "Error retrieving agreement text";
}
return agreementText;
}
public class AppRestResponse
{
public bool Success { get; set; }
public object Data { get; set; }
public IEnumerable<AppRestReponseError> ErrorMessages { get; set; }
}
public class DefaultApiResult
{
public bool Success
{
get
{
return ErrorMessages.Count == 0;
}
private set { }
}
public List<ErrorMessage> ErrorMessages { get; set; }
public object Data { get; set; }
public DefaultApiResult()
{
ErrorMessages = new List<ErrorMessage>();
}
public DefaultApiResult(string errorMessage)
:this()
{
ErrorMessages.Add(new ErrorMessage()
{
Message = errorMessage
});
}
public DefaultApiResult(string[] errorMessages)
:this()
{
foreach (var errorMessage in errorMessages)
{
ErrorMessages.Add(new ErrorMessage()
{
Message = errorMessage
});
}
}
}
I'm not sure about the SimpleJson and the rest client you are using .
However , assuming you're using the RestSharp , it seems that there's no need to use the SimpleJson to deserialize response here .
I just remove the following codes :
IRestResponse response = client.Execute(request);
AppRestResponse apiResponse = SimpleJson.DeserializeObject<AppRestResponse>(response.Content);
and add the following two lines:
IRestResponse<AppRestResponse> response = client.Execute<AppRestResponse>(request);
var apiResponse= response.Data;
It works as expected .

JSON SerializeObject fails to serialize class with abstract member on iOS hardware

When I try to run my Xamarin Forms application on an actual iPhone, it fails with a System.MissingMethodException: "Constructor on type 'System.ComponentModel.ReferenceConverter' not found."
It works fine on the iOS Simulators as well as on Android hardware and virtual devices.
I've been able to narrow it down to the following scenario: serializing an object with an interface referenced member:
public class Widget {
public ISprocket Sprocket { get; set; };
}
public interface ISprocket {
int SprocketId { get; set; }
}
Executing the lines:
var w = new Widget { Sprocket = new Sprocket { SprocketId = 1 } };
string result = JsonConvert.SerializeObject(w);
works everywhere (seemingly) except on my iPhone 8+, where it throws a System.MissingMethodException
I'm using the most current versions of XF (2.5.0.122203) and JSON.NET (10.0.3).
To recreate this problem, create a new Xamarin Forms solution with an iOS client called "TestBed". Add Newtonsoft.Json via nuget, then create a page
"TestBedPage" with the following content:
using System;
using Newtonsoft.Json;
using Xamarin.Forms;
namespace TestBed
{
public class TestBedPage : ContentPage
{
public TestBedPage()
{
try
{
var m = new Widget { Sprocket = new Sprocket { SprocketId = 1 } };
var st = JsonConvert.SerializeObject(m);
Content = new Label { Text = st };
} catch (Exception ex) {
Content = new Label { Text = ex.ToString() };
}
}
}
public class Widget
{
public string Title { get; set; } = string.Empty;
public ISprocket Sprocket { get; set; }
}
public interface ISprocket
{
int SprocketId { get; set; }
}
public class Sprocket : ISprocket
{
public int SprocketId { get; set; }
}
}
Deploy it to an iPhone and run. You should get a screenful of MissingMethodException stack trace.
Does anyone have any idea what's going on here?
The issue is that something is trying to instantiate via Activator class an instance of System.ComponentModel.ReferenceConverter via its System.Type ctor but since it isn't referenced directly in you application it gets removed by the managed linker.
You need to provide a custom linker configuration file ,add it to your Xamarin.iOS project.
Refer to here

Resources