Using ClientView in Code
Studio for Entity Framework (SEF) supports both visual and "all code" style of programming:
• Use the C1DataSource control if you want point-and-click, RAD-style application development.
• Use the ClientViewSource class if you want to separate your code from GUI but keep the ease of use of the C1DataSource. The ClientViewSource class is independent of GUI; it can be used in code with any of the GUI platforms (WPF, Silverlight, WinForms). It can be used completely separately from GUI, including in the view model layer of an MVVM application. At the same time, the ClientViewSource is the same object C1DataSource uses, so you can keep the ease of use characteristic of C1DataSource (but code-only, without visual designers). In fact, C1DataSource is just a collection of ClientViewSource objects plus visual designer support for them.
• For the most complete control over your code, you can use the third, lowest level of the SEF object mode: the ClientView class. If you prefer pure code, especially (but not only) using the MVVM pattern, this is the recommended level. It is still easy to program with, it is mostly based on LINQ which promotes a functional style of programming, intuitive and expressive.
The rest of this section is devoted to programming using the ClientView class.
Client views are smarter than simple queries
ClientView objects represent queries that can be executed on the server. In that regard, they provide the same functionality as queries of Entity Framework and RIA Services. When you start by creating the first ClientView in a newly created context, that's exactly what you get: an EF (or RIA) query that is executed on the server; the resulting entities are fetched to the client and made available for data binding and/or programmatic access on the client. When you continue working on the client, that is, modify some entities on the client and query for more entities, your client views start exhibiting richer behavior that you would not get from mere EF (or RIA) queries:
• Client views are live. When you modify (or add, or delete) entities on the client, client views are automatically updated to reflect changed data.
• Client views make use of the SEF client-side data cache. When you create a new client view or change an existing one, querying it does not necessarily require a round-trip to the server. SEF looks in the cache first and uses the cache if the query can be satisfied without going to the server. That happens when a query is repeated, but not only in that case. SEF is smart enough to detect various cases where a query can be satisfied without going to the server.
The starting point for creating client views are the GetItems methods of EntityClientScope (RiaClientScope):
ClientView<Product> products = _scope.GetItems<Product>();
or, in RIA Services (Silverlight):
ClientView<Product> products = _scope.GetItems<Product>("GetProducts");
(in RIA Services you need to specify the name of a query method in your domain service).
Having thus started with a base query, you can then apply filtering and paging to the view. Filtering and paging are operations that are performed on the server (if the data is not found in the cache), so applying them to a ClientView results in another ClientView. You can also apply other LINQ operators to a client view, such as grouping, sorting, and so on. That results in a View, which is the base class of ClientView. It is also a live view but it does not need ClientView functionality because those operators don't need the server; they can be performed entirely on the client.
Server-side filtering views
To apply filtering to a client view, use the AsFiltered method returning FilteredView, a class derived from ClientView:
FilteredView<Product> productsByCategory =
products.AsFiltered(p => p.CategoryID == categoryID);
Here categoryID is a variable or parameter that is assigned a value somewhere else in the code. When you create this view, with a certain value of categoryID, say 1, it will retrieve products with p.CategoryID = 1. It will do it using the cache, without a trip to the server, if possible; if not, it will fetch those entities from the server (that's the cache-awareness feature of client views). Unlike a standard EF (or RIA) query, it will also take into account entities that are added on the client and don't yet exist on the server, and entities modified on the client so they now satisfy the filter condition (that's the live feature of client views).
Unlike a standard EF (or RIA) query, this view will remain up to date (live) after it was first used. If you modify data on the client and you now have more (or less) entities satisfying the condition, the view will be automatically updated to reflect the changed data. And if you have GUI controls bound to it, those will be updated too.
If you use the AsFiltered method with key selector function
FilteredView<Product> productsByCategory =
products.AsFilteredBound(p => p.CategoryID);
you will be able to set/change the filter condition without creating a new view every time:
productsByCategory.FilterKey = categoryID;
There is also a convenience method, BindFilterKey you can use to bind the filter key of a view to a property (or property path) of an object:
FilteredView<Product> productsByCategory =
products.AsFilteredBound(p => p.CategoryID)
.BindFilterKey(comboBox1, "SelectedValue")
Server-side paging views
The other server-side operation, paging, is specified with the Paging method returning PagingView, a class derived from ClientView:
PagingView<Product> productsPaged = products.Paging(p => p.ProductName, 20);
A paging view contains a segment (page) of data at any given time. To create a paging view, you need to specify the page size (20 in the example above) and a key selector by which to sort, because paging can only be done over sorted data.
Since a paging view is a ClientView , it supports the two fundamental features of client views: it is cache-aware and live. The performance-enhancing cache-awareness of a paging view allows it to avoid round-trips to the server when a page is requested repeatedly. And since a paging view is live (as any ClientView ), it reflects changes that you make to the data on the client. For example, if you delete an entity that is currently in a paging view, it will automatically adjust itself to the changed data, including, if needed, fetching some entities from the server to occupy vacant places on the page.
Other client view operators
Progressive loading
If you have a client view that is too big to load all entities at once without a delay, you can make it load incrementally, progressively, using the ProgressiveLoading method:
ProgressiveView<Product> moreResponsiveView =
productsByCategory.ProgressiveLoading(p => p.ProductName, 100);
Here we intentionally used productsCategory (a FilteredView) instead of products, to show that any client view can be made progressive (except paging view where it does not make sense).
A progressive view loads data in portions, batches making data available on the client immediately after each portion has been loaded.
To create a progressive view, you need to specify the load size (100 in the example above) and a key selector by which to sort, because data must be sorted on the server to perform this kind of loading.
Include
Working with Entity Framework, you sometimes need to specify that related entities must be fetched together with your entity query. For example, querying for orders, you may want to get customer information for the orders to the client in the same query that fetches orders, to avoid fetching them one-by-one later (which is considerably slower than fetching all in one query). You can do this in Entity Framework by using the Include method of ObjectQuery. The same functionality is available in SEF ClientView.Include:
ClientView<Order> ordersWithCustomerInfo = orders.Include("Customer");
Live views
Client views are live; they are kept automatically up-to-date with changing data. But live views functionality is more general, live views (objects of class C1.LiveLinq.LiveViews.View from which ClientView is derived) can be defined on data of any kind, including but not limited to entities, and they support more LINQ query operators, such as grouping, sorting, joins and more (see ComponentOne LiveLinq).
All such operations (of which the most popular are grouping and sorting) can be applied to client views (because ClientView is derived from View). For example:
View productsByCategory = products
.OrderBy(p => p.ProductName).GroupBy(p => p.CategoryID);
or in LINQ query syntax:
View productsByCategory =
from p in products
orderby p.ProductName
group p by p.CategoryID into g
select new { Category = g.Key, Products = g };
The resulting views are live, but they are not client views. This is not a defect of some sort; it is simply because such views don’t need ClientView functionality. Sorting and grouping can be performed entirely on the client without resorting to the server. However, developers must be aware of this fact to avoid confusion. Once you applied a LINQ query operation to your view, it becomes a View, not a ClientView (however, it remains live, automatically reflects all changes you make to the data on the client). So, for example, if you need server-side filtering, use the AsFilter method, not the LINQ Where method. Use the LINQ Where method when you want filtering on the client.