This is the first part of a two-part series on working with collections in WCF RIA Services.
Introduction
A lot of business applications today are built with WCF RIA Services (which should come as no surprise, as it’s a really powerful, extensible framework). However, the collection type support could have been better. Often, you’d simply resort to fetching your entities and adding them to an ObservableCollection<T> in the completed event of your load operation. This still works, but in the first Service Pack for WCF RIA Services quite a few enhancements to existing collection types have been made, and new ones have been added. These allow you to make it easier to work with WCF RIA Services in an MVVM context: we now have collections which automatically track your DomainContext, collections which allow you to add filters, sorting & even grouping, and there’s a server-side pageable DomainCollectionView. In this article, we’ll have a look at these enhancements & new types, and the scenarios in which they can prove useful.
This article will look into 4 ways of working with your data from WCF RIA Services: the EntitySet (enhanced), the EntityList (new), the CollectionView (enhanced) and last but not least, the DomainCollectionView (new). It is accompanied by an example project & full source code, which you can download here.
SP1 for WCF RIA Services is included in Visual Studio 2010 SP1, which can be found here.
EntitySet<T>
The EntitySet is a basic collection type you can use in your WCF RIA Services application, for binding from your ViewModel. It’s an unordered collection with few options for customizing what it returns, but therefore it’s a simple, quick and easy solution if you need to view all the entities of a specific type which have been loaded through your DomainContext.
In the example application, I’m binding a ListBox to a property, Books:
Have a look at the following code:
/// <summary> /// The Books property /// </summary> public EntitySet<Book> Books { get { return Context.Books; } }
As you can see, Books is an EntitySet of type Book, and simply refers to Context.Books, of which 10 are loaded in the constructor:
public EntitySetViewModel() { InstantiateCommands(); // load books Context.Load<Book>(Context.GetBooksQuery().Take(10)); }
When we load more data, as such:
LoadMoreBooks = new RelayCommand(() =>
{
Context.Load<Book>(Context.GetBooksQuery());
});
more entities of type Book are added to the correct EntitySet: Context.Books, resulting in more Books in our ListBox. In other words: when data of type Book is loaded by the DomainContext, it’s added to the Context.Books EntitySet.
EntitySet<T>: adding and removing data.
How do you add or remove data from an EntitySet? Have a look at the following code for adding a Book:
AddBook = new RelayCommand(() => { Context.Books.Add(new Book() { Author = "Kevin Dockx" , ASIN = "123456" , Title = "Dummy book" }); });
and the following code for removing a Book:
DeleteBook = new RelayCommand(() =>
{
Context.Books.Remove(Context.Books.FirstOrDefault());
});
This will result in Books being added or removed from the Books EntitySet, which is immediately visible in our ListBox. When you submit the changes to the server, this will result in the corresponding Insert / Delete operations being executed on our DomainService.
EnityList<T>
The next collection type we’re looking into is a new one: the EntityList<T>. It can be found in the WCF RIA Services Toolkit, in Microsoft.Windows.Data.DomainServices (assembly is included in the example code, you don’t need to download the toolkit if you don’t want to). In essence, it is an observable collection which is backed by an Entity Set. The advantage of this, and reason why you might want to use it, is that this allows us to get a view of a subset of entities of a specific type, loaded through your DomainContext into the backing Entity Set. A typical EntityList<T> property definition looks as such:
private EntityList<Book> _books; public EntityList<Book> Books { get { if (this._books == null) { this._books = new EntityList<Book>( this.Context.Books); } return this._books; } }
An Entity List has a Source property, which defines the entities it should contain:
public EntityListViewModel() { InstantiateCommands(); // load books this.Books.Source = Context.Load<Book>(Context.GetBooksQuery().Take(10)).Entities; }
With these two pieces of code, your Entity List is initialized & ready to be used.
It gets interesting once you start loading more entities of type Book: these are NOT automatically reflected in our Entity List. This is by design: the same subset you initialized it with is used (which is exactly what allows us to get a view on a part of the books loaded in the Entity Set via the Domain Context instead of all of them). If you want to get your Entity List to track more books, you should set the Source property of the list again:
LoadMoreBooks = new RelayCommand(() => { this.Books.Source = Context.Load<Book>(Context.GetBooksQuery()).Entities; });
EntityList<T>: adding and removing data.
Adding a new entity of type Book is typically done in one of two ways: you can either add it to the Context, or add it to the Entity List itself. This results in different behavior, which you can view in the example application. When the following code is executed:
AddBook = new RelayCommand(() => { Context.Books.Add(new Book() { Author = "Kevin Dockx" , ASIN = "123456" , Title = "Dummy book" }); });
a new Book is added to the Context. But as the Entity List does not automatically track this (remember: the subset of items tracked is what’s set on the Source property, not every book loaded in the context), our ListBox still only shows the entities in the Entity Lists’ Source collection.
When the following code is executed:
AddBookToEntityList = new RelayCommand(() => { Books.Add(new Book() { Author = "Kevin Dockx" , ASIN = "123456" , Title = "Dummy book" }); });
A new book is added to the Entity List (Books) itself, and this is reflected in our ListBox. Behind the screens, this book is added to the backing Entity Set if it didn’t exist yet. If it was already available in the backing Entity Set, it isn’t added to the set, it’s just added to our Entity List. Submitting your changes to the server will result in the correct Insert method being executed.
Removing a Book behaves a bit different: when you remove the Book from the backing Entity Set on the Context, this is reflected in the Entity List:
DeleteBook = new RelayCommand(() =>
{
Context.Books.Remove(Context.Books.FirstOrDefault());
});
When you remove a Book from the Entity List, this Book is also removed from the backing Entity Set, meaning a submit operation will result in the correct Delete method being called.
DeleteBookFromEntityList = new RelayCommand(() =>
{
Books.Remove(Books.FirstOrDefault());
});
This concludes part one – stay tuned for part two, which will cover the more advanced collection types: the ICollectionView & DomainCollectionView.