getting a constructor to wait for a method to finish run before proceeding

I have a method that connects to a database
public EventsList() {
var response =AmazonUtils.DynamoDBClient.ScanAsync("Event", attributesToGet, new System.Threading.CancellationToken()).ContinueWith((Task<Amazon.DynamoDBv2.Model.ScanResponse> arg) =>
{

          var results =  arg.Result;

          foreach (Dictionary<string, AttributeValue> item in results.Items)
          {
           //the processitem method takes the attributename and value and just adds a new item to the array of items

           ProcessItem(item);

          }


         });

// events is an array of items which is global

this.Items = events

}

but the the issue is the constructor wont wait for the results
and items will be null for that page

since i cant have an async constructor im lost to what should i do. all the other examples show only to load one item which is not what i need
i need to load 4 - 6 items
but i cant do that cause the constructor will finish and the items wont load into my page cause of it
i am getting data cause the processitem spits the data out to the console but it loads after the page loads

im trying every thing and still coming up short i had it working before with a listview.itemsource property but i cant change the orientation cause my ui needs to go horizontal
i thought writing a custom render would allow me to change the orientation but that didnt work

Best Answer

Answers

  • ClintStLaurentClintStLaurent USUniversity ✭✭✭✭✭
    edited January 2017

    You're calling an async call. Of course it runs asynchronosly.

    You need to await you calling that method.

    if that's unfamiliar gibberish, google 'Xamarin await async'

  • thtcaribbeanguythtcaribbeanguy USMember ✭✭

    @ClintStLaurent said:
    You're calling an async call. Of course it runs asynchronosly.

    You need to await you calling that method.

    if that's unfamiliar gibberish, google 'Xamarin await async'

    yea i tried to have
    var response = await AmazonUtils.DynamoDBClient.ScanAysnc(..........);

    but it told me i cant use await consider making this method async
    which i wont lie im not strong in as yet so ill take a look at the info you gave me
    thank you

  • AdamMeaneyAdamMeaney USMember ✭✭✭✭✭

    If you need to await it when the page starts, trigger it from on appearing.

  • seanydaseanyda GBMember ✭✭✭✭✭

    @thtcaribbeanguy said:

    @ClintStLaurent said:
    You're calling an async call. Of course it runs asynchronosly.

    You need to await you calling that method.

    if that's unfamiliar gibberish, google 'Xamarin await async'

    yea i tried to have
    var response = await AmazonUtils.DynamoDBClient.ScanAysnc(..........);

    but it told me i cant use await consider making this method async
    which i wont lie im not strong in as yet so ill take a look at the info you gave me
    thank you

    put this code around it and you'll be able to run the await... but really you should make a new method to do it

    Device.BeginInvokeOnMainThread(async() =>
    {
    
    });
    
  • thtcaribbeanguythtcaribbeanguy USMember ✭✭

    @lpdavies said:

    @thtcaribbeanguy said:

    @ClintStLaurent said:
    You're calling an async call. Of course it runs asynchronosly.

    You need to await you calling that method.

    if that's unfamiliar gibberish, google 'Xamarin await async'

    yea i tried to have
    var response = await AmazonUtils.DynamoDBClient.ScanAysnc(..........);

    but it told me i cant use await consider making this method async
    which i wont lie im not strong in as yet so ill take a look at the info you gave me
    thank you

    put this code around it and you'll be able to run the await... but really you should make a new method to do it

    Device.BeginInvokeOnMainThread(async() =>
    {
    
    });
    

    yea i tried that and what it did was skip it and run
    this.items = eventshare

    then after it went back to device.begininvokeonmainthread

    what i need is for it to finish whats INSIDE FIRST
    then go to this.items = eventshare

  • thtcaribbeanguythtcaribbeanguy USMember ✭✭
    edited January 2017

    @AdamMeaney said:
    If you need to await it when the page starts, trigger it from on appearing.

    this is perfect for me ill save this for when i need it
    but my constructor isnt loading a page its loading A ViewModelBase

    so i want it to get the info from the database then assign it to Items

    the page's bindingcontext is set to an instance of a viewmodelbase

    PartyEvent.BindingContext = new EventSampleModel();
    
  • AdamMeaneyAdamMeaney USMember ✭✭✭✭✭

    I guess I am more confused as to what you are trying to do. Could you explain again?

    Just say what you want to happen, not what is happening. Like:

    "I am trying to load db items in the constructor of my viewmodel after navigating to a new page to set the values of a list to certain values." or something like that.

  • JohnHardmanJohnHardman GBUniversity mod

    @thtcaribbeanguy - as others have said, you cannot make a constructor async, but you can make OnAppearing async

        protected override async void OnAppearing()
        {
            try
            {
                base.OnAppearing();
    
                await CreateContentViewAndApplyToPageAsync(); // TODO - replace this with your async stuff
            }
            catch (Exception ex)
            {
                // TODO - report any exceptions
            }
        }
    

    Note that you probably only want to repopulate the page content when the page is pushed onto the navigation stack, rather than when another page subsequently pushed is then popped. If this is the case, you will need to add a check for whether OnAppearing is being called as the result of a push or a pop.

  • thtcaribbeanguythtcaribbeanguy USMember ✭✭

    @AdamMeaney said:
    I guess I am more confused as to what you are trying to do. Could you explain again?

    Just say what you want to happen, not what is happening. Like:

    "I am trying to load db items in the constructor of my viewmodel after navigating to a new page to set the values of a list to certain values." or something like that.

    i would like when i call the constructor (for my viewmodelbase class ) for it to go to the database and get me the info i need and use that information to create an array of items which i can this set to the instance of items

    this.items = events

  • thtcaribbeanguythtcaribbeanguy USMember ✭✭
    edited January 2017

    the original code had an example like this

    this.Items = new Item[] {
            //  new Item{Image = "websitelink", Name="john doe" },
    

    };

    but thats not the way i want it to work i want it to get the info from a database then make an array of items

    but now im getting a new error " i cant convert a Task to a Systems.Collection.Ienumerable"

    cause i got the async to work and when i try to cast it

                 this.Items = (Systems.Collection.IEnumerable)GetEvents();
    

    cast invalid

    //async method

                public async Task<Item[]> GetEvents()
        {
            List<String> attributesToGet = new List<String>();
            attributesToGet.Add("eventPicture");
    
    
            await AmazonUtils.DynamoDBClient.ScanAsync("Event", attributesToGet, new System.Threading.CancellationToken()).ContinueWith( (Task<Amazon.DynamoDBv2.Model.ScanResponse> arg) =>
             {
    
    
                // work is a global list of items 
                work.Add(new Item { Image = arg.Result.Items.ToString(), Name = " hahaha" });
                 System.Diagnostics.Debug.WriteLine(arg.Result.Items.ToString());
    
    
    
             });
            return work.ToArray();
    
    
        }
    
  • AdamMeaneyAdamMeaney USMember ✭✭✭✭✭
    edited January 2017

    You either do not call this from the constructor and use another method at another time, or at the end of GetEvents you should assign the results to your items list.

    To go in place of the return line if option 2:

    var items = work.ToArray();
    foreach (var item in items)
    {
        var resultingThing = DoParsing(item);
        this.Items.Add(resultingThing);
    }
    
  • thtcaribbeanguythtcaribbeanguy USMember ✭✭

    // i might be so wrong with this implementation but here goes
    // i listened to you guys advice and tried again
    // this line calls an async method to get an array of items
    //this.Items is in the constructor and on appearing wont work due to fact its not a page and the
    //control attached will not load the information unless its in the constructor
    this.Items = await GETIT();

    //this is the method
    // task that runs it async
    public async Task<Item[]> GETIT()
    {
    List attributesToGet = new List();
    attributesToGet.Add("eventPicture");

            await AmazonUtils.DynamoDBClient.ScanAsync("Event", attributesToGet, new System.Threading.CancellationToken()).ContinueWith( (Task<Amazon.DynamoDBv2.Model.ScanResponse> arg) =>
             {
    
    
                //work is a global list of items
                work.Add(new Item { Image = arg.Result.Items.ToString(), Name = " hahaha" });
                 System.Diagnostics.Debug.WriteLine(arg.Result.Items.ToString());
    
    
    
             });
            return work.ToArray();
    
    
        }
    

    after this it tells me

    The 'await' operator can only be used within async method> @AdamMeaney said:

    You either do not call this from the constructor and use another method at another time, or at the end of GetEvents you should assign the results to your items list.

    To go in place of the return line if option 2:

    var items = work.ToArray();
    foreach (var item in items)
    {
        var resultingThing = DoParsing(item);
        this.Items.Add(resultingThing);
    }
    

    what is this DoParsing method?
    is it a method that turns a list of a attributes into objects
    is it like a term?

  • AdamMeaneyAdamMeaney USMember ✭✭✭✭✭

    Ok let me try and put this a different way.

    Do not call GetIT in the constructor. Stop doing it. There are ways around the limitation of not being able to call async in the constructor, but your design is wrong.

    Call GetIT the first time your Page using the VM appears, or somewhere else that makes sense lifecycle wise for you.

  • thtcaribbeanguythtcaribbeanguy USMember ✭✭

    @AdamMeaney said:
    Ok let me try and put this a different way.

    Do not call GetIT in the constructor. Stop doing it. There are ways around the limitation of not being able to call async in the constructor, but your design is wrong.

    Call GetIT the first time your Page using the VM appears, or somewhere else that makes sense lifecycle wise for you.

    a class that inherits viewmodelbase has an Onappearing method? ( thought that was only pages )

    i think i found out why i get the ienumerable error

    item and items arnt the same thing ( DUH )
    items is ienumerable which takes a list of items

               public IEnumerable Items
               public class Item
    

    i think i can get getit() to work
    but it returns an array of items how would you cast it properly

    how would you make something like a ienumberable

    cause the original code has this

            this.Items = new Item[] {
            new Item{Image = "wedsitelink", Name="great" }
    
            }
    

    which works but my

            this.Items = GETIT()
    

    wont this confused me cause GETIT returns an array of Item

Sign In or Register to comment.