VENTAS: 1-800-867-1389

 none
MobileServiceCollectionView Count Always Zero

    Pregunta

  • Hi,

    I have an issue with a simple Azure setup. I believe it is a deserialization problem but I am not sure. First off, the permissions on the table are wide open for testing (though authentication does work properly when enabled). Here is the issue - If I call the following, I get a valid result set:

    App.MobileService.GetTable("TableName")

    But, if I use the generic methods the result count is always zero and NO EXCEPTION is raised:

     IMobileServiceTable<MyTableName> myTable = App.MobileService.GetTable<MyTableName>();

    Where "MyTableName" is the name of the table as well as a class I have locally marked as following:

        public class MyTableName
        {
            public int id { get; set; }
    
            [DataMember(Name = "Name")]
            public string Name
            {
                get;
                set;
            }
        }

    I have spent WAY too long on this and I'm wondering what "simple" thing I am missing. Any help would be greatly appreciated.


    • Editado cyntacks miércoles, 06 de marzo de 2013 6:09 Updated code example
    miércoles, 06 de marzo de 2013 6:05

Respuestas

  • Hi C,

    It just struck me that you may be trying to read the Count directly in your code.  This is wrong.

    The MobileServiceCollectionView class is a wrapper that allows data to be fetched Asynchronously from a table.  It is used primarily for XAML controls and allows you to attach data to that control.  When you add a ref of this class to the control ItemSource it wires up this control and listens for changes to the collection to keep the control in sync with the data source.

    Is that how you are using this Class?

    If you want to work directly with the items in the collection (like get the count) you have to wait until the data has been fetched.  You would need to listen for the CollectionChanged event and react appropriately.

    Using our todoItems sample.  You wire the CollectionChanged event after assign the class

                items = todoTable
                    .Where(todoItem => todoItem.Complete == false)
                    .ToCollectionView();
    
    
                items.CollectionChanged += items_CollectionChanged;
    

    And in the implementation look for the Reset event (which tells you the data has been loaded - currently we do not support paging so all the data will be there).

      void items_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
            {
                if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Reset)
                {
                    int n = items.Count;
                }
            }
    

    The sender object is the collection as well.

    It is difficult to assist you without all the information as to what you want to do with this class and how you are using it, but I hope this helps.

    -Jeff


    Jeff Sanders (MSFT)

    jueves, 07 de marzo de 2013 16:05

Todas las respuestas

  • Hi C,

    Your syntax is correct.  It gets the table object.

    The next step is to read the data and that is what you are missing.

    For example

     private void RefreshTodoItems()
            {
                // This code refreshes the entries in the list view be querying the TodoItems table.
                // The query excludes completed TodoItems
                items = todoTable
                    .Where(todoItem => todoItem.Complete == false)
                    .ToCollectionView();
                ListItems.ItemsSource = items;
            }

    If this does not answer your question you need to provide enough information for me to duplicate your scenario.

    -Jeff


    Jeff Sanders (MSFT)

    miércoles, 06 de marzo de 2013 15:03
  • Hi Jeff,

    Thanks for the reply, yes, I am doing just what you wrote (sorry, should have been more clear on that). One of the important things I have not mentioned (and now believe may be the cause of this) is we originally entered the data through scripts within SQL Server Management Studio, by passing the "auto write" capabilities of Azure Mobile Services. I am going out on a limb here, but is it possible that due to the fact we inserted data originally via "hand" that the link between table and model did not get generated?

    Thanks again for your help


    • Editado cyntacks miércoles, 06 de marzo de 2013 15:38 clarity
    miércoles, 06 de marzo de 2013 15:37
  • I see,

    Use the Dynamic Schema properties in a test app to add similar fields, then check in the SQL Azure dashboard to ensure the databases are identical.

    Look at the schema information and the data types for the columns in the table and you should be able to spot what is different!

    -Jeff


    Jeff Sanders (MSFT)

    miércoles, 06 de marzo de 2013 15:58
  • Thanks Jeff,

    I will try that out, I wish there was better exception handling here. For example, if the deserialization is failing, it would be great to know why.

    I will try this out and post-back my findings.

    miércoles, 06 de marzo de 2013 16:44
  • More info Jeff, and this is a bit strange to me. A colleague figured out that if we call the Async method on the query utilizing the await keyword, everything works just fine. But of course the item returned is a List<T> and not the MobileServiceCollectionView<T>. So, this DOES NOT WORK:

    IMobileServiceTable<Mode> modeTable = App.MobileService.GetTable<Mode>();
    MobileServiceTableQuery<Mode> query = modeTable.Take(3);
    MobileServiceCollectionView<Mode> items = query.ToCollectionView();

    But this does work:

    IMobileServiceTable<Mode> modeTable = App.MobileService.GetTable<Mode>();
    MobileServiceTableQuery<Mode> query = modeTable.Take(3);
    IEnumerable<Mode>  modes = await query.ToListAsync();

    What am I missing? Well, a lot I am sure, but any ideas what could be wrong here?

    • Editado cyntacks miércoles, 06 de marzo de 2013 18:32
    miércoles, 06 de marzo de 2013 18:31
  • Hi C,

    Let's start by you telling my precisely what you are trying to do and query.

    Here are some sample queries from our sample app you can download from your dashboard:

    // Get some filtered, sorted, paged data as a CollectionView
                MobileServiceCollectionView<TodoItem> collectionView = todoTable
                    .Where(item => item.Id > 10)
                 .OrderBy(item => item.Text)
                 .Skip(5)
                 .Take(5)
                    .ToCollectionView();
    
    
      // Get some filtered data as a List using an extension method
                List<TodoItem> incompleteItemsList = await todoTable
                    .Where(item => item.Complete == false)
                    .ToListAsync();
    -Jeff
     

    -Jeff


    Jeff Sanders (MSFT)

    miércoles, 06 de marzo de 2013 19:02
  • Hi Jeff,

    Thanks a lot for the continued help, I really do appreciate it. All I am trying to do is create a simple test with my own project using the Azure Mobile Service. In AMS I have a table called "Mode" that has two records. The code you provided is nearly exact match to mine. The first example you have does not seem to work, but the second example does work. Below is my full test.

    Also I should note I am attempting this from Windows Phone 8 with no luck, yet other members of my team can successfully access this data from the Windows 8 Modern UI desktop portion of our project (we are building a Mobile and Modern app accessing the same AMS).

    As always, any help is appreciated.

     public async System.Threading.Tasks.Task<MobileServiceUser> Authenticate(MobileServiceAuthenticationProvider provider)
            {
                if (user == null)
                {
                    //   user = await App.MobileService.LoginAsync(provider);
                }
    
                MobileServiceCollectionView<Mode> firstAttempt =  TestToCollectionView();
                List<Mode>  secondAttempt = await TestToToListAsync();
    
                return user;
            }
    
            public MobileServiceCollectionView<Mode> TestToCollectionView()
            {
                IMobileServiceTable<Mode> modeTable = App.MobileService.GetTable<Mode>();
    
                MobileServiceCollectionView<Mode> collectionView = modeTable
                    .Where(mode => mode.Id > 0)
                    .OrderBy(mode => mode.Name)
                    .Skip(0)
                    .Take(5)
                    .ToCollectionView();
    
                return collectionView;
            }
    
            public async Task<List<Mode>> TestToToListAsync(){
                IMobileServiceTable<Mode> modeTable = App.MobileService.GetTable<Mode>();
    
                return await modeTable
                    .Where(mode => mode.Id > 0)
                    .OrderBy(mode => mode.Name)
                    .Skip(0)
                    .Take(5)
                    .ToListAsync();
            }

     

     

    miércoles, 06 de marzo de 2013 21:17
  • You must await the call for the collection view or this will not work.

    Jeff Sanders (MSFT)

    miércoles, 06 de marzo de 2013 21:19
  • Thanks Jeff,

    This may be my lack of experience with the TPL, Await, Asycn etc., but how would you await the ToCollectionView() when it is not an asnyc method?

    Again, thanks for staying on top of this all day.

    miércoles, 06 de marzo de 2013 21:58
  • Hi C,

    The collectionView is an asynchronous data source that wraps the query.

    How are you determining that your code does not work?  How are you trying to use the collectionView?

    -Jeff


    Jeff Sanders (MSFT)

    jueves, 07 de marzo de 2013 15:32
  • Hi Jeff,

    The code "works", there is no error. But the MobileServiceCollectionView.Count is 0 in the first example, and it is 5 in the second example. Shouldn't these both contain the same number of results?

    I know I must be doing something wrong as I believe my code (above) matches all of the examples online, but still I can't get results using the collection view.

    Thanks for the help, I do appreciate it. I wonder what I should do next? I wish I could post my project but since the data is in the cloud that's a bad idea.

    jueves, 07 de marzo de 2013 16:02
  • Hi C,

    It just struck me that you may be trying to read the Count directly in your code.  This is wrong.

    The MobileServiceCollectionView class is a wrapper that allows data to be fetched Asynchronously from a table.  It is used primarily for XAML controls and allows you to attach data to that control.  When you add a ref of this class to the control ItemSource it wires up this control and listens for changes to the collection to keep the control in sync with the data source.

    Is that how you are using this Class?

    If you want to work directly with the items in the collection (like get the count) you have to wait until the data has been fetched.  You would need to listen for the CollectionChanged event and react appropriately.

    Using our todoItems sample.  You wire the CollectionChanged event after assign the class

                items = todoTable
                    .Where(todoItem => todoItem.Complete == false)
                    .ToCollectionView();
    
    
                items.CollectionChanged += items_CollectionChanged;
    

    And in the implementation look for the Reset event (which tells you the data has been loaded - currently we do not support paging so all the data will be there).

      void items_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
            {
                if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Reset)
                {
                    int n = items.Count;
                }
            }
    

    The sender object is the collection as well.

    It is difficult to assist you without all the information as to what you want to do with this class and how you are using it, but I hope this helps.

    -Jeff


    Jeff Sanders (MSFT)

    jueves, 07 de marzo de 2013 16:05
  • You are a saint.... I wish I had read that in other places. This totally makes sense, and that is a great explanation.

    Jeff, thanks again for everything, I am going to send this to my team.

    jueves, 07 de marzo de 2013 16:53
  • Great!  Best of luck developing with Mobile Services!

    -Jeff


    Jeff Sanders (MSFT)

    jueves, 07 de marzo de 2013 17:39