I know this should be simple, so I am obviously missing something.
I am writing a very simple Xamarin.Forms application with this as the MainPage.xaml.cs of the Shared project.
public partial class MainPage : ContentPage
{
public MyClass myClassInstance { get; set; }
public MainPage()
{
InitializeComponent();
DownloadDataAsync();
}
private async void DownloadDataAsync()
{
string page = #"http://www.blaw.com:80/foo/bar";
try
{
HttpClient client = new HttpClient();
var uri = new Uri(string.Format(page, string.Empty));
var response = await client.GetAsync(uri); <--- BONK!
if (response.IsSuccessStatusCode)
{
var responseString = await response.Content.ReadAsStringAsync();
XmlSerializer ser = new XmlSerializer(typeof(MyClass));
using (TextReader tr = new StringReader(responseString))
{
myClassInstance = (MyClass)ser.Deserialize(tr);
}
}
else
System.Diagnostics.Debug.WriteLine(response.StatusCode.ToString());
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
}
}
When I run this on my Android device, it never gets past the GetAsync line (marked with <-- BONK!). It doesn't throw an exception, and in never makes it to the if statement, it just returns from the method without doing anything at all.
I have enabled the ACCESS_NETWORK_STATE and INTERNET permissions in the Android Manifest.
What am I missing?
Calling asynchronous method from constructor is not a good idea. This will work in console applications but not on UI applications. Because this will block the UI thread, and control never returns to the code due to deadlock.
If you can call your async method in OnAppearing, it should work fine.
Related
I have the following method in Xamarin that makes an API call that returns a list of Items. When I start Visual Studio and run it returns a string of JSON with the information I expect it to. However when I run it several times and more often than not it hits the string returnedJson = await client.GetStringAsync line then just breaks out of the method, returning nothing.
I dont think its related to restarting Visual Studio, because sometimes after it not working several times it will work once again then stop again.
public async Task<IEnumerable<ItemModel>> GetItemsAsync(bool forceRefresh = false)
{
var baseAddr = new Uri("https://www.domain.co.uk");
var client = new HttpClient
{
BaseAddress = baseAddr
};
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", Settings.ApiToken);
// NOW PROTECTED
try
{
string returnedJson = await client.GetStringAsync("/api/Items/Index");
var convert = JsonConvert.DeserializeObject<List<ItemModel>>(returnedJson);
return convert;
}
catch (Exception ex)
{
return null;
}
return null;
}
The WebAPI method -
[Authorize]
[HttpGet("[action]")]
public async Task<List<ItemModel>> Index()
{
return await itemService.Get();
}
I'm relatively new to mobile and async data access and I'm trying to build a line-of-business app from Xamarin starter "cross platform" template in VS2017. It seems that when I do database operations too frequently I get 'database is locked' (most questions deal with roll-your-own sqlite implementations). I had added pretty verbose logging (I have to support non-technical end mobile users).
I changed to (as suggested in other answers) a singleton model for database access which is producing non-traceable (meaning no exceptions are caught and no xamarin log entries) exceptions when calling table.ReadAsync (see below).
As a secondary question, having spent so much time on this and running into so many different roadblocks (no doubt of my own making) I'm wondering whether I'm not following some unspoken rule for mobile development such as "only one async object read per page and design UI for 100% async". Am I trying to do too much? Here is my current "singleton" data access class:
public static class MainDataStore
{
private static ReaderWriterLockSlim ReadLock = new ReaderWriterLockSlim();
public static bool IsInitialized { get; set; }
public static MobileServiceClient MobileService { get; set; }
public static bool UseAuthentication = true;
public static IMobileServiceSyncTable<User> UserTable;
public static IMobileServiceSyncTable<Showroom> ShowroomTable;
public static IEnumerable<User> Users { get; set; } //= new ObservableRangeCollection<User>();
public static IEnumerable<Showroom> Showrooms { get; set; }
public static void InitializeAsync()
{
try
{
if (IsInitialized)
return;
Logging.D("Starting to initialize main store.");
AuthenticationHandler handler = null;
handler = new AuthenticationHandler();
MobileService = new MobileServiceClient(App.AzureMobileAppUrl, handler)
{
SerializerSettings = new MobileServiceJsonSerializerSettings
{
CamelCasePropertyNames = true
}
};
var store = new MobileServiceSQLiteStore(Settings.DatabaseName);
store.DefineTable<User>();
store.DefineTable<Showroom>();
MobileService.SyncContext.InitializeAsync(store, new MobileServiceSyncHandler());
UserTable = MobileService.GetSyncTable<User>();
ShowroomTable = MobileService.GetSyncTable<Showroom>();
Logging.D("Finished initializing main store.");
IsInitialized = true;
}
catch (Exception ex)
{
Logging.E(ex); // Debug.WriteLine("EXCEPTION: " + ex.Message + ". Stack: " + ex.StackTrace);
}
}
public static async void Load(ECarnavalObjectType type)
{
Logging.D("Reading lock entering. Read count: " + ReadLock.CurrentReadCount.ToString());
// ReadLock.EnterReadLock();
switch (type)
{
case ECarnavalObjectType.Users:
await GetUsersAsync();
Users = await UserTable.ToEnumerableAsync();
break;
case ECarnavalObjectType.Showrooms:
await GetShowroomsAsync();
Showrooms = await ShowroomTable.ToEnumerableAsync();
break;
}
// ReadLock.ExitReadLock();
}
public static async Task GetUsersAsync()
{
if (CrossConnectivity.Current.IsConnected)
{
try
{
// await UserTable.ReadAsync<User>(UserTable.CreateQuery());
await UserTable.PullAsync($"all{typeof(User).Name}", UserTable.CreateQuery());
}
catch (Exception ex)
{
}
}
}
public static async Task GetShowroomsAsync()
{
await ShowroomTable.ReadAsync<Showroom>(ShowroomTable.CreateQuery());
}
}
In your code, you are not awaiting the InitializeAsync(), which means it is likely that the database is still locked and being set up when you go to synchronize it.
Arrange your code in a singleton, then have every single method (read/list/etc.) call await InitializeAsync() to initialize the database. Do an early return on the InitializeAsync() method if the database is already created (you've got some good code there for that).
For more info, see my book: https://adrianhall.github.io/develop-mobile-apps-with-csharp-and-azure/chapter3/client/
I am really stuck on this as I have searched far and wide for a solution for an asyncpost using Web API but couldnt find anything. Essentially, its got to make a POST call using HttpClient to the relevant controller class AddMenuItem using Web API but it just doesn't work. It simply throws an error of a 404 Error and cannot see the controller method. Any reasons why and solution for this would be very helpful!
// Async Post Call
public static async void asyncPost()
{
using (var client = new HttpClient())
{
try
{
var values = new System.Collections.Generic.Dictionary<string, string>();
values.Add("ItemName", "Pepperoni Pizza");
var content = new FormUrlEncodedContent(values);
string baseAddress = "http://localhost:9000/";
HttpResponseMessage response3 = await client.PostAsync(baseAddress + "api/values/AddMenuItem", content);
if (response3.StatusCode == System.Net.HttpStatusCode.OK)
{
// Do something...
}
}
catch (OperationCanceledException) { }
}
}
// POST api/values
public void AddMenuItem([FromBody]string itemName)
{
//Should go in here when PostAync is called
}
Don't use async void; use async Task instead.
public static async Task PostAsync()
Then your controller can call it with await:
public async Task AddMenuItem([FromBody]string itemName)
{
await PostAsync(..);
}
Can any one tell me how to retrive data from OData service using Simple.Odata.Client with in xamarin forms ?
I try by following way :
In Portable Project
public App()
{
GetDocument();
}
public async void GetDocument()
{
String result = await ODataServiceAgent.GetDocuments(skipCount);
}
In OData Service Calls
public static async Task<string> GetDocuments(int skipCount)
{
string json = string.Empty;
try
{
var client1 = new ODataClient(new ODataClientSettings(ServiceConstant.sURL_Base, new System.Net.NetworkCredential(ServiceConstant.NCUserName, ServiceConstant.NCPassword))
{
IgnoreResourceNotFoundException = true,
//OnTrace = (x, y) => Console.WriteLine(string.Format(x, y)),
});
string commands = string.Format(ServiceConstant.sURL_WholeDataByPagging, ServiceConstant.servicePaggingTopCount, skipCount);
IEnumerable<IDictionary<string, object>> configs = client1.FindEntriesAsync(commands).Result;
List<IDictionary<string, object>> configList = ((List<IDictionary<string, object>>)configs.ToList());
json = JsonConvert.SerializeObject(configList);
}
catch (Exception ex)
{
string excepstionMessage = ex.Message;
}
return json;
}
while actual call is happen using "FindEntriesAsync" line its not responding
It in the call to Result. In general it's not a good idea to call Result or Wait on async methods: it may work in some environments (like desktop Windows) but it will deadlock in others, especially mobile ones. So just await it, don't do .Result or .Wait.
I have been experimenting with WP7 apps today and have hit a bit of a wall.
I like to have seperation between the UI and the main app code but Ive hit a wall.
I have succesfully implemented a webclient request and gotten a result, but because the call is async I dont know how to pass this backup to the UI level. I cannot seem to hack in a wait for response to complete or anything.
I must be doing something wrong.
(this is the xbox360Voice library that I have for download on my website: http://www.jamesstuddart.co.uk/Projects/ASP.Net/Xbox_Feeds/ which I am porting to WP7 as a test)
here is the backend code snippet:
internal const string BaseUrlFormat = "http://www.360voice.com/api/gamertag-profile.asp?tag={0}";
internal static string ResponseXml { get; set; }
internal static WebClient Client = new WebClient();
public static XboxGamer? GetGamer(string gamerTag)
{
var url = string.Format(BaseUrlFormat, gamerTag);
var response = GetResponse(url, null, null);
return SerializeResponse(response);
}
internal static XboxGamer? SerializeResponse(string response)
{
if (string.IsNullOrEmpty(response))
{
return null;
}
var tempGamer = new XboxGamer();
var gamer = (XboxGamer)SerializationMethods.Deserialize(tempGamer, response);
return gamer;
}
internal static string GetResponse(string url, string userName, string password)
{
if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(password))
{
Client.Credentials = new NetworkCredential(userName, password);
}
try
{
Client.DownloadStringCompleted += ClientDownloadStringCompleted;
Client.DownloadStringAsync(new Uri(url));
return ResponseXml;
}
catch (Exception ex)
{
return null;
}
}
internal static void ClientDownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error == null)
{
ResponseXml = e.Result;
}
}
and this is the front end code:
public void GetGamerDetails()
{
var xboxManager = XboxFactory.GetXboxManager("DarkV1p3r");
var xboxGamer = xboxManager.GetGamer();
if (xboxGamer.HasValue)
{
var profile = xboxGamer.Value.Profile[0];
imgAvatar.Source = new BitmapImage(new Uri(profile.ProfilePictureMiniUrl));
txtUserName.Text = profile.GamerTag;
txtGamerScore.Text = int.Parse(profile.GamerScore).ToString("G 0,000");
txtZone.Text = profile.PlayerZone;
}
else
{
txtUserName.Text = "Failed to load data";
}
}
Now I understand I need to place something in ClientDownloadStringCompleted but I am unsure what.
The problem you have is that as soon as an asynchronous operation is introduced in to the code path the entire code path needs to become asynchronous.
Because GetResponse calls DownloadStringAsync it must become asynchronous, it can't return a string, it can only do that on a callback
Because GetGamer calls GetResponse which is now asynchronous it can't return a XboxGamer, it can only do that on a callback
Because GetGamerDetails calls GetGamer which is now asynchronous it can't continue with its code following the call, it can only do that after it has received a call back from GetGamer.
Because GetGamerDetails is now asynchronous anything call it must also acknowledge this behaviour.
.... this continues all the way up to the top of the chain where a user event will have occured.
Here is some air code that knocks some asynchronicity in to the code.
public static void GetGamer(string gamerTag, Action<XboxGamer?> completed)
{
var url = string.Format(BaseUrlFormat, gamerTag);
var response = GetResponse(url, null, null, (response) =>
{
completed(SerializeResponse(response));
});
}
internal static string GetResponse(string url, string userName, string password, Action<string> completed)
{
WebClient client = new WebClient();
if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(password))
{
client.Credentials = new NetworkCredential(userName, password);
}
try
{
client.DownloadStringCompleted += (s, args) =>
{
// Messy error handling needed here, out of scope
completed(args.Result);
};
client.DownloadStringAsync(new Uri(url));
}
catch
{
completed(null);
}
}
public void GetGamerDetails()
{
var xboxManager = XboxFactory.GetXboxManager("DarkV1p3r");
xboxManager.GetGamer( (xboxGamer) =>
{
// Need to move to the main UI thread.
Dispatcher.BeginInvoke(new Action<XboxGamer?>(DisplayGamerDetails), xboxGamer);
});
}
void DisplayGamerDetails(XboxGamer? xboxGamer)
{
if (xboxGamer.HasValue)
{
var profile = xboxGamer.Value.Profile[0];
imgAvatar.Source = new BitmapImage(new Uri(profile.ProfilePictureMiniUrl));
txtUserName.Text = profile.GamerTag;
txtGamerScore.Text = int.Parse(profile.GamerScore).ToString("G 0,000");
txtZone.Text = profile.PlayerZone;
}
else
{
txtUserName.Text = "Failed to load data";
}
}
As you can see async programming can get realy messy.
You generally have 2 options. Either you expose your backend code as an async API as well, or you need to wait for the call to complete in GetResponse.
Doing it the async way would mean starting the process one place, then return, and have the UI update when data is available. This is generally the preferred way, since calling a blocking method on the UI thread will make your app seem unresponsive as long as the method is running.
I think the "Silverlight Way" would be to use databinding. Your XboxGamer object should implement the INotifyPropertyChanged interface. When you call GetGamer() it returns immediately with an "empty" XboxGamer object (maybe with GamerTag=="Loading..." or something). In your ClientDownloadStringCompleted handler you should deserialize the returned XML and then fire the INotifyPropertyChanged.PropertyChanged event.
If you look at the "Windows Phone Databound Application" project template in the SDK, the ItemViewModel class is implemented this way.
Here is how you can expose asynchronous features to any type on WP7.