Xamarin IMobileServiceTable Run Synchronously - c#

Using the IMobileServiceTable interface, how can I call result of a method synchronously. E.g. GetTask method below :
public class ItemsManager {
IMobileServiceTable<Item> itemTable;
private ItemViewModel _itemViewModel = new ItemViewModel ();
public ItemsManager()
{
this.itemTable = App.client.GetTable<Item>();
App.SetItemsManager(this);
}
//This seems to just hang
public Item GetTask(string id, string barcode)
{
var qry = itemTable.Where(a => a.ItemID == id || a.BarCodeID == id);
return qry.Query.FirstOrDefault();
}
}
I'm trying to get the result via scan method, and extract the item record to another form :
async void OnProductScanClicked (object sender, EventArgs e)
{
var result = await BarCodes.Instance.Read ();
if (!result.Success) {
await this.DisplayAlert ("Scan Failed", "Failed to get barcode", "OK");
} else {
var msg = String.Format ("Barcode : {0} - {1}", result.Format, result.Code);
App.SetItemsManager(new ItemsManager());
var item = App.ItemManager.GetTask(result.Code, result.Format.ToString());
if (item == null) {
await this.DisplayAlert ("Item Not Found!", msg, "OK");
}
else {
var myPage = new ItemsXaml();
myPage.BindingContext = item;
await Navigation.PushModalAsync (myPage);
}
}
}

I am just new to this stuff, and now have figure out on how to this. We just have to add the await method when calling any asynchronous function.

Related

Executing same function from several async methods simultenously causes errors

Here is a piece of code, where I try to execute different async methods, that need to be executed in specific order (the await, and Task.WhenAll() parts).
//Some other tasks before
Task<bool> taskIfcQuantityArea = Task.Run<bool>(() =>
{
return this.addGroupStringToDictionary("IfcQuantityArea");
});
Task<bool> taskIfcQuantityLength = Task.Run<bool>(() =>
{
return this.addGroupStringToDictionary("IfcQuantityLength");
});
Task<bool> taskIfcSiUnit = Task.Run<bool>(() =>
{
return addGroupStringToDictionary("IfcSiUnit");
});
Task<bool> taskIfcPropertySingleValue = Task.Run<bool>(() =>
{
return addGroupStringToDictionary("IfcPropertySingleValue");
});
//uses IfcPerson, IfcOrganization
Task<bool> taskIfcPersonAndOrganization = Task.Run<bool>(() =>
{
return addGroupStringToDictionary("IfcPersonAndOrganization");
});
//uses IfcOrganization
Task<bool> taskIfcApplication = Task.Run(async () =>
{
await taskIfcSiUnit;
return addGroupStringToDictionary("IfcApplication");
});
//uses IfcSiUnit
Task<bool> taskIfcMeasureWithUnit = Task.Run(async () =>
{
await taskIfcSiUnit;
return addGroupStringToDictionary("IfcMeasureWithUnit");
});
//some other tasks after.
When I do that job synchronously, all works fine, but when I do it in async, I have some random errors. At every test, the errors come randomly.
The only thing I see that could go wrong, is they all execute the same function addGroupStringToDictionary.
Here is the function :
private bool addGroupStringToDictionary(string typeName)
{
//int processCount = await Task.Run<int>(() =>
//{
GroupedListStrings groupElt = this.listGrouppedStrings.FirstOrDefault(x => x.Type == typeName.ToUpper());
if (groupElt != null)
{
List<string> listStringInGroup = groupElt.ListStrings;
foreach (string line in listStringInGroup)
{
try
{
if(typeName== "IfcLocalPlacement($")
{
typeName = "IfcLocalPlacement";
}
var type = Type.GetType("Ifc."+typeName);
if (typeName == "IfcPropertySingleValue" || typeName == "IfcDirection" || typeName == "IfcSiUnit" || typeName == "IfcQuantityLength" || typeName == "IfcQuantityArea" || typeName == "IfcQuantityVolume" || typeName == "IfcQuantityWeight")
{
try
{
object instance = Activator.CreateInstance(type, line);
this.addToListDictionary((IfcElement)instance);
}
catch
{
}
}
else if (typeName == "IfcOpeningElement")
{
try
{
object instance = Activator.CreateInstance(type, line, this.listDictionaries, this.DictionaryBolts);
this.addToListDictionary((IfcElement)instance);
}
catch
{
}
}
else
{
try
{
object instance = Activator.CreateInstance(type, line, this.listDictionaries);
this.addToListDictionary((IfcElement)instance);
}
catch
{
}
}
}
catch
{
this.addError(line);
}
}
this.listGrouppedStrings.Remove(groupElt);
this.reportProgressImport();
}
//return 100;
//});
this.reportProgressImport();
return true;
}
The catch got 1-2 times over a bit more than 1 million lines.
At each test the errors come randomly.
Is it possible that running the function simultaneously from several async methods, this is what causes the problem?
Here is the addToListDictionary function :
private void addToListDictionary(IfcElement elt)
{
if(elt.ErrorFound)
{
this.listReadButError.Add(elt);
return;
}
string type = elt.GetType().ToString();
if (elt is IfcRepere)
{
type = "Ifc.IfcRepere";
}
else if (elt is IfcRepereType)
{
type = "Ifc.IfcRepereType";
}
else if (elt is IfcPhysicalSimpleQuantity)
{
type = "Ifc.IfcPhysicalSimpleQuantity";
}
else if (elt is IfcProfileDef)
{
type = "Ifc.IfcProfileDef";
}
else if (elt is IfcGeometricRepresentationContext)
{
type = "Ifc.IfcGeometricRepresentationContext";
}
GroupDictionary group = this.ListDictionaries.FirstOrDefault(x => x.Name == type);
if(group==null)
{
group = new GroupDictionary { Name = type };
this.ListDictionaries.Add(group);
}
group.ListElements[elt.ID] = elt;
if (elt is IfcMechanicalFastener)
{
IfcMechanicalFastener bolt = (IfcMechanicalFastener)elt;
this.DictionaryBolts[bolt.Tag] = bolt;
}
else if(elt is IfcProject)
{
this.listProjects.Add((IfcProject)elt);
}
else if(elt is IfcElementAssembly ifcAss)
{
this.DictionaryIfcElementAssemblies[ifcAss.Key] = ifcAss;
}
}
Also some additive information about my ListDictionaries :
private List<GroupDictionary> listDictionaries = new List<GroupDictionary>();
public List<GroupDictionary> ListDictionaries { get { return this.listDictionaries; } set { this.listDictionaries = value; } }
And the class GroupDictionary
public class GroupDictionary
{
string name { get; set; }
public string Name { get { return this.name; } set { this.name = value; } }
public ConcurrentDictionary<int, IfcElement> ListElements = new ConcurrentDictionary<int, IfcElement>();
public GroupDictionary()
{
}
}
I made different GroupDictionary because as soon as I don't need one of them, I delete it to free space.
I have one dictionary with IfcPoint, I need it to gt IfcPolyLine (lines), but when I finish to treat all objects using IfcPoint, I clear remove the corresponding GroupDictionary in order to free some memory.
You have a obvious thread-safety issues here (i.e. you are trying to perform some operation which is not thread safe from multiple threads at a time). There are multiple ways you can try tackling it - using locks, or some synchronization primitives.
But in this case it seems that major source of issues is working with standard collections from multiple threads, which is not thread-safe (because thread-safety usually comes with performance price and is not always needed). You can start from switching to appropriate collections from System.Collections.Concurrent namespace.
To go down deeper I recommend free e-book by Joseph Albahari Threading in C#.

Update ObservableCollection when Firestore event occurs

I am trying to use Xamarin Forms to create a cross-platform application. I have decided to use Firestore as the database for my app.
I am trying to add chat functionality to my app using and I'm struggling with implementing the real-time listening functionality. I have already created a ViewModel class containing an ObservableCollection of Chats that is used by a ListView in the UI.
public class ChatsVM
{
public ObservableCollection<Chat> Chats { get; set; }
public ChatsVM()
{
Chats = new ObservableCollection<Chat>();
ReadMessages();
}
public async void ReadMessages()
{
User currentUser = await DependencyService.Get<IFirebaseAuthenticationService>().GetCurrentUserProfileAsync();
IList<Chat> chatList = await DependencyService.Get<IChatService>().GetChatsForUserAndListenAsync(currentUser.IsLandlord, currentUser.Id);
foreach (var chat in chatList)
{
Chats.Add(chat);
}
}
}
I have also created the services to fetch the data from Firestore. On the service side of things (example is showing Android service) I am using a standard List to hold Chat objects
List<Chat> Chats;
bool hasReadChats;
The method GetChatsForUserAndListenAsync adds a snapshot listener to my query and passes events to the OnEvent method.
public async Task<IList<Chat>> GetChatsForUserAndListenAsync(bool isLandlord, string userId)
{
string fieldToSearch;
if (isLandlord)
{
fieldToSearch = "landlordId";
}
else
{
fieldToSearch = "tenantId";
}
try
{
// Reset the hasReadChats value.
hasReadChats = false;
CollectionReference collectionReference = FirebaseFirestore.Instance.Collection(Constants.Chats);
// Get all documents in the collection and attach a OnCompleteListener to
// provide a callback function.
collectionReference.WhereEqualTo(fieldToSearch, userId).AddSnapshotListener(this);
// Wait until the callback has finished reading and formatting the returned
// documents.
for (int i = 0; i < 10; i++)
{
await System.Threading.Tasks.Task.Delay(100);
// If the callback has finished, continue rest of the execution.
if (hasReadChats)
{
break;
}
}
return Chats;
}
catch (FirebaseFirestoreException ex)
{
throw new Exception(ex.Message);
}
catch (Exception)
{
throw new Exception("An unknown error occurred. Please try again.");
}
}
public void OnEvent(Java.Lang.Object value, FirebaseFirestoreException error)
{
var snapshot = (QuerySnapshot) value;
if (!snapshot.IsEmpty)
{
var documents = snapshot.Documents;
Chats.Clear();
foreach (var document in documents)
{
Chat chat = new Chat
{
Id = document.Id,
LandlordId = document.Get("landlordId") != null ? document.Get("landlordId").ToString() : "",
TenantId = document.Get("tenantId") != null ? document.Get("tenantId").ToString() : ""
};
//JavaList messageList = (JavaList) document.Get("messages");
//List<Message> messages = new List<Message>();
// chat.Messages = messages;
Chats.Add(chat);
}
hasReadChats = true;
}
}
How would I propagate any changes to this list that are made by the event handler to the ObservableCollection in my VM class?
Credit to Jason for suggesting this answer.
So I modified the event handler to look for document changes in Firestore. Based on these changes, the service would send different messages using MessagingCenter.
public async void OnEvent(Java.Lang.Object value, FirebaseFirestoreException error)
{
var snapshot = (QuerySnapshot) value;
if (!snapshot.IsEmpty)
{
var documentChanges = snapshot.DocumentChanges;
IFirebaseAuthenticationService firebaseAuthenticationService = DependencyService.Get<IFirebaseAuthenticationService>();
Chats.Clear();
foreach (var documentChange in documentChanges)
{
var document = documentChange.Document;
string memberOne = document.Get(Constants.MemberOne) != null ? document.Get(Constants.MemberOne).ToString() : "";
string memberTwo = document.Get(Constants.MemberTwo) != null ? document.Get(Constants.MemberTwo).ToString() : "";
Chat chat = new Chat
{
Id = document.Id,
MemberOne = memberOne,
MemberTwo = memberTwo,
};
var documentType = documentChange.GetType().ToString();
switch (documentType)
{
case Constants.Added:
MessagingCenter.Send<IChatService, Chat>(this, Constants.Added, chat);
break;
case Constants.Modified:
MessagingCenter.Send<IChatService, Chat>(this, Constants.Modified, chat);
break;
case Constants.Removed:
MessagingCenter.Send<IChatService, Chat>(this, Constants.Removed, chat);
break;
default:
break;
}
Chats.Add(chat);
}
hasReadChats = true;
}
}
In the constructor for the VM, I subscribed as a listener for these messages and updated the ObservableCollection accordingly.
public class ChatsVM
{
public ObservableCollection<Chat> Chats { get; set; }
public ChatsVM()
{
Chats = new ObservableCollection<Chat>();
ReadChats();
}
public async void ReadChats()
{
IFirebaseAuthenticationService firebaseAuthenticationService = DependencyService.Get<IFirebaseAuthenticationService>();
MessagingCenter.Subscribe<IChatService, Chat>(this, Constants.Added, (sender, args) =>
{
Chat chat = args;
Chats.Add(chat);
});
MessagingCenter.Subscribe<IChatService, Chat>(this, Constants.Modified, (sender, args) =>
{
Chat updatedChat = args;
Chats.Any(chat =>
{
if (chat.Id.Equals(updatedChat.Id))
{
int oldIndex = Chats.IndexOf(chat);
Chats.Move(oldIndex, 0);
return true;
}
return false;
});
});
MessagingCenter.Subscribe<IChatService, Chat>(this, Constants.Removed, (sender, args) =>
{
Chat removedChat = args;
Chats.Any(chat =>
{
if (chat.Id.Equals(removedChat.Id))
{
Chats.Remove(chat);
return true;
}
return false;
});
});
User currentUser = await firebaseAuthenticationService.GetCurrentUserProfileAsync();
await DependencyService.Get<IChatService>().GetChatsForUserAndListenAsync(currentUser.Id);
}
}

WPF singelton intance gives null exception, before singelton value is set

I have created a Singelton
static readonly License_plateRequests _instance = new License_plateRequests();
private License_plateRequests()
{
}
public static License_plateRequests instance
{
get { return _instance; }
}
public License_plate license_plateFirst { get; set; }
I run a clickevent in a page, to run some code where i set the singelton value. Its my first application in WPF so, i dont know if the patteren is right.
private async void enterButton_Click(object sender, RoutedEventArgs e)
{
if (ImageStatus.Source.ToString() == "pack://application:,,,/ParkeringsApp;component/Countries/Denmark-icon.png")
{
nationality = "DK";
}
if (ImageStatus.Source.ToString() == "pack://application:,,,/ParkeringsApp;component/Countries/Germany-icon.png")
{
nationality = "GER";
}
if (ImageStatus.Source.ToString() == "pack://application:,,,/ParkeringsApp;component/Countries/Norway-icon.png")
{
nationality = "NOR";
}
if (ImageStatus.Source.ToString() == "pack://application:,,,/ParkeringsApp;component/Countries/Sweden-icon.png")
{
nationality = "SWE";
}
if (ImageStatus.Source.ToString() == "pack://application:,,,/ParkeringsApp;component/Countries/United-Kingdom-flat-icon.png")
{
nationality = "GB";
}
var data = await loginRequest.LoginAsync();
var token = await loginRequest.ParkingToken(data.jwt);
var licenseplate = await licensePlate.LicensePlate(token, nationality, numberplateInput.Content.ToString());
var parkings = await licensePlate.getParkingById(token, licenseplate.id);
try
{
foreach (var parking in parkings)
{
if (parking == null)
{
parkingRequests.ParkCar(token, "aca0cd99-e392-4069-847f-8953ca86d7e6", licenseplate.id);
NavigationService.Navigate(new RegistredPage());
}
else if (parking.time_end == 0)
{
NavigationService.Navigate(paymentPage, licenseplate);
licensePlate.license_plateFirst.country.alpha2 = nationality;
}
else
{
parkingRequests.ParkCar(token, "aca0cd99-e392-4069-847f-8953ca86d7e6", licenseplate.id);
NavigationService.Navigate(new RegistredPage());
}
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex);
}
}
Then i navigate by NavigationService to the next page PaymentPage.
I get a nullpointer Exception in PaymentPage.
public partial class PaymentPage : Page
{
License_plateRequests licensePlate = License_plateRequests.instance;
public PaymentPage()
{
InitializeComponent();
System.Diagnostics.Debug.WriteLine(licensePlate.license_plateFirst.country.alpha2);
}
}
I get the null pointer when i try to run the application. I havent set the value yet, so offcourse
licensePlate.license_plateFirst.country.alpha2
gives me null. But i havent loaded that page yet, and i havent set the value.
How can i handle this, so i can get the value when the page is first loaded?

Replace sync method with async one

How can I make this method asynchronous?
public List<ItemsList> GetAllItems()
{
List<ItemsList> items = new List<ItemsList>();
string query= String.Format("SELECT MFPARTN, MFDSC, sum(QHAVL) as Total FROM IPMFF");
newConnection.OpenConnection();
command = new Command(Query, newConnection.ConnectionInstance);
dataReader = command.ExecuteReader();
ItemsList itemsList = null;
while (dataReader.Read())
{
itemsList = new ItemsList();
if (System.DBNull.Value != dataReader["MFPARTN"])
{
itemsList.ItemNumber = (dataReader["MFPARTN"].ToString());
}
if (System.DBNull.Value != dataReader["MFDSC"])
{
itemsList.ItemDescription = (dataReader["MFDSC"].ToString());
}
if (System.DBNull.Value != dataReader["Total"])
{
itemsList.ItemQuantity = (Convert.ToInt32(dataReader["Total"]));
}
items.Add(itemsList);
}
return items;
}
Merely putting async and await does not seem to be working. Also, how can I make it a Task method? I am new to async methods, so.
Update
I have modified the method like this.
public Task<List<ItemsList>> GetAllItems()
{
List<ItemsList> items = new List<ItemsList>();
string query= String.Format("SELECT MFPARTN, MFDSC, sum(QHAVL) as Total FROM IPMFF");
newConnection.OpenConnection();
command = new Command(Query, newConnection.ConnectionInstance);
dataReader = command.ExecuteReader();
ItemsList itemsList = null;
while (await dataReader.ReadAsync())
{
itemsList = new ItemsList();
if (System.DBNull.Value != dataReader["MFPARTN"])
{
itemsList.ItemNumber = (dataReader["MFPARTN"].ToString());
}
if (System.DBNull.Value != dataReader["MFDSC"])
{
itemsList.ItemDescription = (dataReader["MFDSC"].ToString());
}
if (System.DBNull.Value != dataReader["Total"])
{
itemsList.ItemQuantity = (Convert.ToInt32(dataReader["Total"]));
}
items.Add(itemsList);
}
return items;
}
But it is returning this error when I execute this piece of code.
The model item passed into the dictionary is of type
'System.Threading.Tasks.TaskSystem.Collections.Generic.List, but
this dictionary requires a model item of type
'System.Collections.Generic.IEnumerable`
You can define a simple Task<T> to handle your GetAllItems function like so.
public Task<List<ItemsList>> GetListOfItems(){
//Simply pass in your GetAllItems Method
return Task.Factory.StartNew(()=>GetAllItems());
}
You can now create a function to handle the asynchronous operation like under:
public async void GetListOfItemsAsync(){
List<ItemsList> myItems=await GetListOfItems();
//Do something with your list...
}
There are other ways to do this. I would suggest you read up Microsoft's documentation on Asynchronous programming with async and await (C#).

Infinite loop or infinite recursion error in UWP using autosuggestbox

// The class for the search query
public class SearchQueries
{
List<data> list = new List<data>();
string response;
// The method that return the list after it is set
public List<data> GetData()
{
return list;
}
// The method that do the searching from the google API service
public async void SetData()
{
// The problem starts here, when i instantiate the search class in this class in other to get the value of the text in the autosuggestbox for my query, it crashes whenever i try to launch the page. it works fine whenever i give the address default data e.g string address = "London", the page open when i launch it and give me London related result whenever i type in the autosuggestbox.
Search search = new Search();
string address = search.Address;
list.Clear();
// Note the tutorial i used was getting the data from a local folder, but i'm trying to get mine from Google API
string dataUri = "https://maps.googleapis.com/maps/api/place/autocomplete/json?key=AIzaSyDBazIiBn2tTmqcSpkH65Xq5doTSuOo22A&input=" + address;
string Api = System.Uri.EscapeDataString(dataUri);
HttpClient client = new HttpClient();
client.Timeout = TimeSpan.FromMilliseconds(1000);
try
{
response = await client.GetStringAsync(Api);
for (uint i = 0; i < jsonarray.Count; i++)
{
string json_string_object = jsonarray.GetObjectAt(i)["description"].ToString();
list.Add(new data() { name = json_string_object });
}
}
catch (TimeoutException e)
{
ContentDialog myDlg = new ContentDialog()
{
PrimaryButtonText = "OK"
};
myDlg.Content = e.ToString();
}
}
// Method to get matched data
public IEnumerable<data> getmatchingCustomer(string query)
{
return list.Where(c => c.name.IndexOf(query, StringComparison.CurrentCultureIgnoreCase) > -1).OrderByDescending(c => c.name.StartsWith(query, StringComparison.CurrentCultureIgnoreCase));
}
// constructor for returning the SetData() method
public SearchQueries()
{
// It points to this method whenever the application crash, with the notification of infinite loop or infinite recursion
SetData();
}
}
// The Main Class of the page
public sealed partial class Search : Page
{
public string theaddress { get; set; }
SearchQueriess queries = new SearchQueriess();
public Search()
{
this.InitializeComponent();
myMap.Loaded += MyMap_Loaded;
theaddress = locationAddress.Text;
}
// The text change method of the autosuggest box.
private async void AutoSuggestBox_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
{
if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput)
{
if (sender.Text.Length > 1)
{
var marchingData = queries.getmatchingCustomer(sender.Text);
sender.ItemsSource = marchingData.ToList();
}
else
{
sender.ItemsSource = new string[] { "No suggestion...." };
}
}
}
}

Categories