Async queries, filters, sort

6 posts, 0 answers
  1. Jerome
    Jerome avatar
    87 posts
    Member since:
    Aug 2010

    Posted 26 Mar 2013 Link to this post

    I'm trying to figure out the best way to implement this with the RadGridView. I have a WCF service with a client callback. I invoke the service to begin a query, and it invokes the callback multiple times as results are known. It has to be this way, because the results come streaming in, must begin to appear quickly, and I have no idea how many results there will be up front.

    Right now I'm implementing this completely in a ViewModel: exposing the set of results as an ObservableCollection which is being added to as results arrive. I also have various mechanisms in place to cancel outstanding queries, and ensure results from different instances of the query don't overlap.

    This makes it difficult to use the built in Filter and Sort controls of the RadGridView. All of my sort and filter controls are currently custom, and outside of the RadGridView, so that they can be directly bound to properties of the ViewModel. When those properties change, the ViewModel cancels and clears the last query/results, and begins a new one.

    I'd like a way to use the build in Filter/Sort controls. They all seem to require, to be used properly, that the bound collection be a CollectionView of some fashion. I'm fine with that. I can alter my ViewModel to expose one of those instead. The problem is I cannot figure out how to deal with the async delivery into this CollectionView. How do I do this? If I implement my logic in the Refresh method, how do I deliver the results asynchronously?

    QueryableCollectionView seems to require an IEnumerable as a source. I don't have one. That needs to be built as filters/sorts are changed.

    VirtualQueryableCollectionView seems the closest: but it seems to require knowing the total item count up front, which I do not and cannot know.

    My only apparent option at this point, as far as I can tell, is to implement my own ICollectionView. Is this my remaining option?

  2. Rossen Hristov
    Admin
    Rossen Hristov avatar
    2478 posts

    Posted 27 Mar 2013 Link to this post

    Hi,

    RadGridView does the filtering and sorting via LINQ, so if the ItemsSource of the grid implements the IQueryable interface, then the filtering will append a Where on it and the sorting will append an OrderBy on it. 

    If we detect that the ItemsSource is an ICollectionView, then we will translate our SortDescriptors to the ICollectionView's SortDescriptions and sort it, but we cannot do anything for filtering, since the ICollectionView uses a predicate for filtering which we cannot translate to. Our grid uses FilterDescriptors collection to describe the filtering information and they cannot be translated to such a predicate.

    We have a control called RadDataServiceDataSource which is to be used when working with WCF Data Services (OData), but I am not sure whether your services are such. If you use WCF Data Services then sorting and filtering will happen on the server out-of-the-box because the DataServiceQuery<T> is IQueryable and can understand what the grid is "telling" it.

    If you are using plain WCF Services, then your best shot would be to implement a class that implements IQueryable (for filtering and sorting to be directly applied on) and INotifyCollectionChanged. You will keep an in-memory ObservableCollection wrapped in this "collection view" and when data arrive to the client, you will clear and repopulate the observable collection and raise CollectionChanged with Reset. When the grid asks your collection view for its Enumerator, you will return the wrapped in ObservableCollection's enumerator. As for the IQueryable, you would have to somehow parse the LINQ expression that the grid has appended to this IQueryable (i.e. Where for filtering and OrderBy, ThenBy for sorting) and based on this information somehow tell the server what to return. I don't think that this is very trivial for implementation though, but it can be possible. Implementing such a collection view is beyond my expertise so I am afraid that I cannot be of much help when it comes to this.

    I am afraid that this is all that our controls can offer.

    All the best,
    Rossen Hristov
    the Telerik team

    Explore the entire Telerik portfolio by downloading Telerik DevCraft Ultimate.

  3. Jerome
    Jerome avatar
    87 posts
    Member since:
    Aug 2010

    Posted 27 Mar 2013 Link to this post

    Ahh. Well, that's a bummer. I could implement an IQueryProvider and tear off the Where and OrderBy expressions: but that leaves me with no way to stream the results back, as I'm sure you obtain the results by enumerating the IQueryable.

    Is there anyway to deliver streamed results into an IQueryableCollectionView, without knowing the total item count? That seems like my only remaining option: to completely implement an ICollectionView/IQueryableCollectionView class.
  4. Rossen Hristov
    Admin
    Rossen Hristov avatar
    2478 posts

    Posted 28 Mar 2013 Link to this post

    Hello,

    From RadGridView's point of view, all that matters is the INotifyCollectionChanged events that it receives from its ItemsSource. You mentioned that your results come to the client in batches from time to time and you want those result to appear in the grid as soon as they arrive. The only way I can think of about doing this would be to bind the grid to something that implements INotifyCollectionChanged and when results arrive to the client raise the CollectionChanged event with the appropriate event arguments.

    As for filtering and sorting, you might try the following.

    1) External Filtering. I have attached a sample project that demonstrates one possible approach for external filtering. Basically, you keep the filtering UI intact but when the moment comes (i.e. when the user clicks ApplyFilter) it is your turn to somehow perform external filtering. You totally dump the built-in RadGridView filtering and you do the filtering on the outside. RadGridView will never ever know what's going on. When the user clicks the button, you will have to read what he has entered in the UI by reading the ColumnFilterDescriptor of the respective column and decide what to tell your WCF Service. You will have to keep track of all columns that are currently filtered of course.

    2) Custom sorting. It is possible to perform external sorting as well. This help article describes how to do custom sorting. So basically when the user sorts you will have to tell your WCF Service what to return.

    So what you can do is the following. Have an in-memory ObservableCollection<T> on the client and bind the grid to this collection. When the user filters or sorts, tell your WCF Service what to return and wait. You can even turn on a BusyIndicator if you want while the client is waiting. Once a chunk of data arrives on the client, append it or clear and add it to the ObsevableCollection and the ObservableCollection will tell the grid what has happended and the grid will refresh accordingly. Your observable collection will the client view of your server data -- filtered and sorted as needed.

    This is the only possible approach I can think of that does not involve implementing a custom ICollectionView on the client which is quite complex in my opinion.

    If you have the source code of our controls, you can see that we have done something very similar in our QueryableDataServiceCollectionView<T>. This is the client collection view that we use for WCF Data Services. Basically, when you bind the grid to such a collection, the grid will apply its filter and sort criteria to this collection, the collection will take them and translate them on the DataServiceQuery<T> it wraps, which is an IQueryable, and then it will call the server with this query and start waiting for data. Once the data arrives to the client, this collection will take the results and place them in an ObservableCollection that is wrapped inside it. Then it will notify the grid that it needs to enumerate it because data has changed.

    So you get the idea. You will need to do something similar. It does not have to be wrapped in a standalone collection view class, but this is an architectural decision.

    All the best,
    Rossen Hristov
    the Telerik team

    Explore the entire Telerik portfolio by downloading Telerik DevCraft Ultimate.

  5. Jerome
    Jerome avatar
    87 posts
    Member since:
    Aug 2010

    Posted 28 Mar 2013 Link to this post

    Thanks. This is exactly what I needed. I have all of that stuff already working, but in a UI external to the RadGridView. Which is a bit annoying, but this should solve it.

    I think I'm going to start by attempting to implement the ICollectionView much in the way you guys have done. I have a few different screens where a good base implementation for this should come in handy.
  6. Rossen Hristov
    Admin
    Rossen Hristov avatar
    2478 posts

    Posted 28 Mar 2013 Link to this post

    Hi,

    Since you have all of the stuff working outside of the grid, why don't you wire the "external filtering" and "custom sorting" features that I mentioned to call the code that you already have. In other words, when the user filters a column, read what he has done and call your filtering code. Same with sorting. I have already tried to explain this in my previous post.

    You can of course implement an IQueryable/INotifyCollectionChanged class, which will be cleaner and reusable solution. IQueryable is the key here, since ICollectionView cannot solve the filtering problem, i.e. the grid cannot directly filter an ICollectionView because it does not have FilterDescriptions like it has SortDescriptions.

    In the end, it is your call.

    Kind regards,
    Rossen Hristov
    the Telerik team

    Explore the entire Telerik portfolio by downloading Telerik DevCraft Ultimate.

Back to Top