Part 2 – Getting Data into the Grid
In Part 1, we setup the projects necessary for a standard business application using IdeaBlade DevForce for the Model, MVVM Light for the ModelView and a Telerik RadGridView in the View.
In Part 3, we will follow through the binding process to see some of the challenges posed when making the grid editable.
In Part 2, we will make the changes necessary to make the grid editable.
Requirements
For this part of the sample, the requirements are as follows:
- Show Orders in the Rows of the Grid.
- Make the Grid Editable.
- Save changes to the Orders with a Save Changes button.
Showing the Orders in the Grid
This will require adding an Orders property to the MainViewModel; populating that property and then binding the Orders property to the grid.
- Make sure there is a reference in the Web project to the server-side Model project.
- Open the MainViewModel.cs file.
- Remove the Welcome property.
- Open the ViewModel\MainViewModel.cs file.
- Use the mvvminpc Snippet to add an Orders property.
/// <summary> /// The <see cref="Orders" /> property's name. /// </summary> public const string OrdersPropertyName = "Orders"; private ObservableCollection<order> _orders; /// <summary> /// Gets the Orders property. /// TODO Update documentation: /// Changes to that property's value raise the PropertyChanged event. /// This property's value is broadcasted by the Messenger's default instance when it changes. /// </summary> public ObservableCollection<order> Orders { get { return _orders; } set { if (_orders == value) { return; } //var oldValue = _orders; _orders = value; // Remove one of the two calls below //throw new NotImplementedException(); // Update bindings, no broadcast RaisePropertyChanged(OrdersPropertyName); // Update bindings and broadcast change using GalaSoft.MvvmLight.Messenging //RaisePropertyChanged(OrdersPropertyName, oldValue, value, true); } } - Next, add a method to fill the collection.
private void GetOrdersAsync() { var em = new NorthwindIBEntities(); var qry = em.Orders; em.ExecuteQueryAsync(qry, args => this.Orders = new ObservableCollection<order>(args.Results)); } - Next, add a call to the new method in MainViewModel constructor:
public MainViewModel() { GetOrdersAsync(); - Finally, add a binding to the XAML to show the Orders in the Grid. (Set the ItemsSource of the RadGridView to Orders.)
<telerik:RadGridView Name="radGridView1" ItemsSource="{Binding Path=Orders}" Height="775" Width="1000" /> - Build and Run. After a moment or two, you should see the grid, fully populated with Orders.

Making the Grid Editable
For once, the default behavior is desirable. We get this for free because the RadGridView is editable by default. Due to the magic of Binding, the Order objects are getting updated whenever a cell in the grid is edited.
Saving the Changes
Saving those changes will require us to have a button bound to an ICommand that will tell the EntityManager to save all the changes.
- In MainPage.xaml, add a button to the StackPanel above (or below) the RadGridView.
<Button Content="Save" Height="23" Name="SaveAllButton" Width="75" HorizontalAlignment="Left" />
- In MainViewModel.cs, add a new RelayCommand named SaveAllCommand.
public RelayCommand SaveAllCommand { get; private set; } - In the MainViewModel constructor, add a line to instantiate the command:
SaveAllCommand = new RelayCommand(this.SaveAllOrders);
- Now add the SaveAllOrders method and the SaveAllButtonEnabled property:
private void SaveAllOrders() { this.SaveAllButtonEnabled = false; _entityManager.SaveChangesAsync(args => this.SaveAllButtonEnabled = true); } public bool SaveAllButtonEnabled { get { return _saveAllButtonEnabled; } set { _saveAllButtonEnabled = value; RaisePropertyChanged("SaveAllButtonEnabled"); } } - Now you can go back to the button to set the bindings:
<Button Content="Save" Height="23" Name="SaveAllButton" Width="75" HorizontalAlignment="Left" IsEnabled="{Binding Path=SaveAllButtonEnabled}" Command="{Binding Path=SaveAllCommand}" /> - Build and Run. After a moment or two, you should see the grid, fully populated with Orders. Try editing some of the orders and hit Save All. Refresh the page to see that the changes were saved.
What’s next?
So now you have an editable grid. So far, we have not needed the RowViewModel. If you only want SaveAll functionality, the default behavior of the grid will suffice. The grid lets you edit any cell all the time and the bindings keep the model up to date. A simple call to entity manager to save the changes and all is well.
However, if you want some entity specific functionality, you will find it quite cumbersome to try to deal with this within the context of the grid’s ViewModel as it is really only concerned with the grid and entire grid related actions (such as Save All).
In Part 3, we will add some entity specific requirements so we can see how to use the RowViewModel to make working with the entities in the grid much simpler.




Purpose and Principles of the Data Layer
March 17, 2010 — Simon KingabyAt its core, the Data Layer’s main purpose is:
Data Layer’s Main Purpose:
To abstract all interactions with the database so that business objects can be written to deal with business rules, not with database interaction.
For example, when promoting a standard Deal, the business logic comprises:
In addition to these items, there are interactions with the Deal Pricing, Deal Costing, Deal Transaction Pricing and a variety of other lookup tables. Records need to be saved, retrieved, updated and deleted to facilitate the persistence of the Deal’s new “Promoted” state. The Data Layer will encapsulate the code that interacts with the database, so the Deal Promoter class only needs to be concerned with the business rules of promoting a deal, and not with the mechanics of persisting those changes to the database.
ORM Principles
In order to effectively serve as an Object-Relational Mapping (ORM) tool, the Data Layer needs to implement the following principles.
Principle 1: The Data Layer should generate the code necessary to deal with different tables and should provide a common API for working with the resulting data objects.
A typical database interaction involves the following steps:
The only thing that changes from table to table is the names of the table and its columns. All of the database code is identical.
Principle 2: The Data Layer should be able to work with the entire Object Graph by saving and retrieving related objects as a set.
Some objects are more complex than others, for example, a Deal has Pricing, Charges and Transactions that are an integral part of it. It also has Confirmations that are related to it. When saving a Deal, the pieces and parts of the Deal should get saved too.
Principle 3: The Data Layer should handle failures within the context of a transaction and roll back the changes to a consistent, stable state.
Sometimes, when saving a complex object, an error may occur in one of the pieces. In this case, the Data Layer should gracefully handle the error and leave the object and the database in a stable, consistent state.
Principle 4: The Data Layer should intelligently map database tables to appropriate Business Objects.
In several cases, a business object will represent a concept differently than the database might. For example, the database table EMPLOYEE contains all the records for the Employees, Managers and Direct Reports business classes.
Principle 5: The Data Layer should handle concurrency properly.
When objects are saved to the database, concurrency problems arise because the data being saved is about to overwrite data that has already changed since it was last retrieved. Concurrency resolutions include: Overwrite, Merge and Discard. The Data Layer needs to support these options and allow developers to choose which resolution to employ.
Query Support
Principle 6: The Data Layer should support LINQ.
In order to provide data sources for drop downs and grids the Data Layer needs to be able to support querying, including sorting, grouping and summarizing. There are three choices to do this:
1) Oracle native SQL queries
Implementing this technique often pushes Business and UI logic all the way back into the database. It makes ORM a challenge as the query objects are not like table-based as they are not updateable and do not usually have the necessary key fields.
2) Custom querying support in the Data Layer
Implementing this technique is complex, non-standard and may have performance issues.
3) LINQ (The .Net framework’s built in query language)
Implementing LINQ provides powerful, sophisticated, query capabilities. LINQ to Entities also raises performance by taking advantage of the Entity Framework’s knowledge of the database objects.
Principle 7: The Data Layer should support asynchronous communication.
One of the worst aspects of application performance is the perceived lag while waiting for data to be retrieved from a database, transferred over the network and rendered in the UI. Asynchronous communication is the recommended way to prevent this lag by allowing the UI to be responsive while the data is retrieved, transferred, and even rendered, asynchronously. In Silverlight, all network communication is asynchronous, so the Data Layer must support asynchronous communication.
Oracle Support
Principle 8: The Data Layer should support Oracle specific features, such as Sequences, Packages and Oracle Data Types.
Most Oracle tables have a key field that is a numeric tied to a Sequence value. Much of the legacy code is embedded in the Database in Oracle Packages. There are also some Oracle specific Data Types (particularly LOB’s) that need to be translated to/from their .Net equivalents. The Data Layer needs to handle all three of these situations properly.
Trouble Shooting Support
Principle 9: The Data Layer should support granular logging for debugging and troubleshooting.
When debugging and troubleshooting, a detailed log of what is happening can be a very useful tool. Especially in asynchronous or Inversion of Control situations where the code cannot be easily stepped through, a log is critical to the discovery and elimination of bugs.
Performance Enhancement
Principle 10: The Data Layer should support server-side and client-side caching to improve performance.
Data Caching on the Server-side allows redundant calls for data from different clients to be served in a single database request. Data Caching on the Client-side allows redundant calls for data on the client to be served on the client without any network traffic at all.
Principle 11: The Data Layer should support validation at the client and at the server to improve performance.
Eliminating round trips by providing client-side validation will improve performance. Providing server-side validation will ensure data integrity at the server.