Address: http://www.silverlightshow.net/items/Working-with-collections-in-WCF-RIA-Services-part-two.aspx
This is the second part of this article.
In the first part, we discuss two relatively simple set types: entityset and entitylist. In this article, we will learn more about the other two more advanced types: icollectionview and domaincollectionview.
Icollectionview
Icollectionview is not a new interface. It has been implemented by a large number of Silverlight controls, such as DataGrid. Now we can use it directly in viewmodel. To allow controls to be bound to an icollectionview implementation (for example, the familiar collectionviewsource and pagedcollectionview), we can do this:
Private Icollectionview Createview ( Ienumerable Source ){ Collectionviewsource CVS = New Collectionviewsource (); Cvs. Source = source; Return Cvs. view ;} Private Icollectionview _ Books; Public Icollectionview Books { Get { If ( This . _ Books = Null ){ This . _ Books = createview ( This . Context. Books ); This . _ Books. Filter = New Predicate < Object > (Bookcorrespondstofilter );} Return this . _ Books ;}}
When the book data is loaded, it is automatically reflected in the View:
PublicCollectionviewviewmodel () {instantiatecommands ();// Load booksContext. Load <Book> (Context. getbooksquery (). Take (10));}
Icollectionview:Add and remove data
You can directly add and remove entities from the context. changes to these entitysets will be tracked by collectionviewsource.
So what does this mean? So far, we have not seen the difference between this method and the direct use of entityset, right? In fact, what really special about icollectionview is that you can add filtering, sorting, and grouping rules.
IcollectionviewFilter
Icollectionview defines a filter attribute of the predicate <Object> type. Let's add and modify ourCode:
Private Icollectionview _ Books; Public Icollectionview Books { Get { If ( This . _ Books = Null ){ This . _ Books = createview ( This . Context. Books ); This . _ Books. Filter = New Predicate < Object > (Bookcorrespondstofilter );} Return this . _ Books ;}} Public bool Bookcorrespondstofilter ( Object OBJ ){ Book Book = OBJ As Book ; If (Filteractive ){ Return Book. Title. Contains ( "Silverlight" );} Return true ;}
When the bookcorrespondstofilter method is executed, it checks whether the title attribute of each book contains the word "Silverlight". If it does not contain the word, it is not displayed in the view.
The function provided by the current code is only used when you explicitly know the filtering time. However, most applicationsProgramIf you need to determine the filtering time, let's make some changes: add the filteractive attribute, which is set to true when you click Add filter.
Public boolBookcorrespondstofilter (ObjectOBJ ){BookBook = OBJAsBook;If(Filteractive ){ReturnBook. Title. Contains ("Silverlight");}Return true;}
When we click the button, we will find that the interface has not changed. Why?
When we change the filtering condition or the book object changes (such as changing its name), the icollectionview will not automatically perform the filter again: the filter method is executed only when the object is added to entityset. This means you have to explicitly notify it to use the new filter condition to re-check the loaded object. We can achieve this by calling the refresh () method of icollection:
Refresh =NewRelaycommand() =>{ Books. Refresh ();});
Now, the view is re-created, and all book objects are re-filtered. Of course, this is only necessary when we change the filter condition or the entityset changes.
IcollectionviewSorting and grouping
Icollectionview has two interesting attributes: sortdescriptions and groupdescription. You can use them to define the sorting and grouping rules for entityset.
The sorting operation can be performed by clicking the DataGrid column header bound to the icollectionview, however, when we use other controls such as ListBox that have no headers, we need to change their sorting rules by code:
Addsort =NewRelaycommand() => {Books. sortdescriptions. Add (NewSortdescription("Title",Listsortdirection. Ascending ));});
Grouping A set is similar:
Addgrouping =NewRelaycommand() => {Books. groupdescriptions. Add (NewPropertygroupdescription("Author"));});
Effect
One thing to note is that once a set is grouped, UI virtualization will be disabled-so be cautious when operating a large amount of data. When grouping a large amount of data, it is generally necessary to work with paging.
Icollection is a good choice for sorting, filtering, and grouping. However, it can only act on data in the memory, which means that all data must be loaded to the client. This is suitable for most application scenarios. In other cases, we can solve the problem through domaincollectionview.
Domaincollectionview
In many enterprise applications, tens of thousands or even millions of data records are sorted, filtered, and grouped. Icollectionview is no longer applicable to such scenarios. The reason is described above. We need a set that allows the server to sort, filter, group, and more important paging operations.
This is the responsibility of domaincollectionview. You can find it in the Microsoft. Windows. Data. domainservices program in the WCF Ria services Toolkit (this Assembly is already included in the sample code ). Using domaincollectionview requires more settings than other sets, but once you have mastered these settings, you will find that they are still very simple. The source and loader (collectionviewloader by default) attributes are required during domaincollection initialization.
PublicDomaincollectionview<Book> Books {Get{Return this. View ;}}
Source defines the source entity used for view (any set that implements ienumerable). A typical example is the set that implements inotifycollectionchanged.
This. Source =NewEntitylist<Book> (Context. Books );
Loader focuses on data loading. When we use the default collectionviewloader, our colleagues need to input two callbacks: onload and onloadcompleted, they define the events that occur when the data must be loaded and loaded. (You can also use a simple loadoperation instead of collectionviewloader if you want ).
This. Loader =NewDomaincollectionviewloader<Book> (This. Onloadbooks,This. Onloadbookscompleted );
Private Loadoperation < Book > Onloadbooks (){ Return this . Context. Load ( This . Query. sortpageandcount ( This . View ));} Private void Onloadbookscompleted ( Loadoperation < Book > OP ){ If (Op. haserror) {op. markerrorashandled ();} Else if (! Op. iscanceled ){ This . Source. Source = op. entities; If (Op. totalentitycount! =- 1 ){ This . Books. settotalitemcount (op. totalentitycount );}}}
As you can see, in onloadebooks, we confirm that the request execution contains sortdescription and paging (only the data required for the current page is loaded) and the total number of data (required by datapager ).
When books is loaded, the source set is set to the loaded book object, and the total number of objects is assigned to the totalitemcount attribute through totalentitycount.
This. View =NewDomaincollectionview<Book> (Loader, source );
The rest is used for initialization of loading (for example, setting the page size to 5 ):
Using(This. View. deferrefresh ()){This. View. pagesize =5;This. View. movetofirstpage ();}
(If deferrefresh () is used, the refresh data event of the view can be postponed until all the code segments included in the using are executed)
In fact, when we perform paging, sorting (and other operations that may start to refresh the view), the loader is executed and loaded with data. Once the load operation is complete, the source attribute will be updated and the view will be notified of the update and respond to changes through the notification mechanism.
(Note: In the release L version of the WCF Ria services toolkit, sortpageandcount has been changed to sortandpageby)
Domaincollectionview: Add and remove data
The Code is as follows:
Addbook = New Relaycommand () => { // You can add books like this, But DCV is server side oriented: to get correct // behaviour, you shocould add it to the context and submit the changes, after which // The next query will fetch the book you just added. Book Book = books. addnew () As Book ; Book. Author = "Kevin dockx" ; Book. Asin = "123456" ; Book. Title = "Silverlight for Dummies" ;}); Deletebook = New Relaycommand () => { // Deleting an item can be done like this, But shocould be done directly on the context // & submitted to the server Books. removeat ( 0 );});
Then, this operation will lead to non-synchronization of view filtering, sorting, and other rules. After all, domaincollectionview is designed to work on the server.
The correct way to add and remove objects is to perform corresponding operations on the server at the same time, such as adding an object to the context (or removing it from the context ), call submitchanges to submit it to the server and refresh your domaincollectionview.
Domaincollectionview: Data filtering, sorting, and grouping
Next, let's take a look at how to filter data. Simply add the corresponding where condition after entityquery, for example:
Addfilter =NewRelaycommand() => {// Filters in DCV shocould be done by adding a where clause to the query, as DCV is mainly used for // server side logicThis. Query = context. getorderedbooksquery (). Where (B => B. Title. Contains ("Silverlight"));This. View. movetofirstpage ();});
Next is sorting and grouping. When we click the column header, sortdesparts will be added to the book collection to determine the next data read sorting policy and automatically retrieve data again.
Some applications require that the List jump to the first page after the sorting rule changes. For such a requirement, we need to write an event handler like this:
InotifycollectionchangedPolicyingsortdescriptions = (Inotifycollectionchanged)This. Books. sortdescriptions; policyingsortdescriptions. collectionchanged + = (sender, e) => {This. View. movetofirstpage ();};
Like using icollectionview, we can also add custom sortdescription to domaincollectionview using code.
Addsort =NewRelaycommand() => {Books. sortdescriptions. Add (NewSortdescription("Title",Listsortdirection. Ascending); books. Refresh ();});
The grouping method is similar:
Addgrouping =NewRelaycommand() => {Books. groupdescriptions. Add (NewPropertygroupdescription("Author"); Books. Refresh ();});
After integrating the above content, we finally get a set of server paging, sorting, and grouping:
Summary
WCF Ria Services SP1 adds or enhances many Collection types. Both better binding options and server-side paging and sorting sets make it easier to interact with mvvm. If you are using WCF Ria services in combination with mvvm to develop industry software or commercial programs, it is necessary to understand these new collection types.