Silverlight 4 Beta and Visual Studio 2010 add much in the line of Rapid Application Development. Besides adding support for the Cider XAML designer and properties window, we now have Data Sources Window support for RIA Services.
In this (quite long) blog post, I’ll walk through the creation a Line of Business-type application using Silverlight 4, RIA Services, and Visual Studio 2010.
Contents
The Ingredients. 1
Getting Started. 1
Adding a New Page. 2
Adding the Database and Image Assets. 3
Creating the Data Access Layer. 4
Creating the Domain Service Class. 6
Using the Data Sources Window for RAD Data Forms. 8
Adding a ComboBox to the Details View.. 10
Saving Changes. 15
Adding New Records. 16
Deleting Records. 17
Adding Validation. 17
Custom Validation + Shared Classes. 19
Ensure you have all of the required ingredients:
Visual Studio 2010 Beta 2
Silverlight 4 Tools Beta for Visual Studio 2010
The Sample Code for this Walkthrough
Create a new Silverlight Business Application Template named BeerDB.

The Business Application Template creates a solution with .NET RIA Services enabled, and includes a Silverlight Navigation application with separate “Views” and a MainPage. Run the application by hitting F5 and note the project that was generated. At the top-right of the screen are “home” and “about” links. When you click these, you are brought to the appropriate View in the project. Close the browser window to stop debugging.
Let’s add in a new View to display our Beer Maintenance page. Right-click the Views subfolder in the BeerDB (Silverlight) project and select Add/New Item. Select Silverlight Page and name this page “BeerMaintenance.xaml”

Now we need to add a link from the Main Page to our new View. In Solution Explorer, find MainPage.xaml in the Silverlight project and open it. In the Document Outline window (View/Other Windows/Document Outline), find and select the Link1 and Divider1 elements.

Copy and Paste this XAML to add in a new HyperlinkButton and Rectangle Divider, and set the NavigateUri property to our new BeerMaintenance page.
NavigateUri="/BeerMaintenance" TargetName="ContentFrame" Content="Beers"/>
Run the project. Note that the new “beers” link is available and you can navigate between all of the Views.
We’ll use a simple file-based SQL server database, placed in our App_Data folder of the Web project. The database includes Uri’s to images of Beer Labels, which we’ll also bring into the project.
In the BeerDB.Web Project (Web Application), right-click the App_Data folder and select Add/Existing Item. Browse to the “Database.mdf” file (from the download for this walkthrough).
In the BeerDB.Web Project (Web Application), right-click the ClientBin folder and select Add/New Folder. Name the new folder Images. Right-click the Images folder and select Add/Existing Item. Browse and select all of the beer label images (from the download for this walkthrough.
Your BeerDB.Web project should now look something like this:

We’ll use the Entity Framework for our Data Access Layer, but note that we have lots of options here (LINQ to SQL, POCO, etc).
In the BeerDB.Web Project (Web Application), right-click and select Add/New Item. Select ADO.NET Entity Data Model and name the model BeerModel.edmx

Select Generate from Database for the first Step

In the next step, note that our Database.mdf is automatically selected. Enter “BeerEntities” for the connectionstring settings.

In the next step, select the Beers and Brewers tables and name the Model Namespace “BeerModel”:

Click “Finish” to complete the Entity Data Model Wizard. Note our simple database schema. Close the BeerModel.edmx and then Build the Project.
Right-click the BeerDB.Web project and select Add/New Item. Select Domain Service Class and name the class BeerService.

NOTE: If the next dialog comes up empty, just close and Rebuild the Solution, then repeat the above step.
In this step, we want to be able to edit and add Validation metadata, so check Enable Editing for both tables and “Generate associated classes for Metadata.”

Build the application to ensure everything is sync’ed up (there is a good amount of code generation happening with RIA Services!)
Back in the Silverlight application, find the BeerMaintenance.xaml View and open it. Open the Data Sources Window (from the menu, select Data/Show Data Sources). Select the Beer Entity under the BeerContext:

… and drag the Beer entity to the design surface.
A DataGrid and DomainDataSource are added to the page XAML. Also, look at the codebehind file (BeerMaintenance.xaml.cs) and note that a LoadedData event handler has been added to check for and raise any errors.
Run the project and notice the Grid gets populated:

Next we’ll add a “Details” View. Click the Beer drop-down in the Data Sources Window and select “Details” view:

Drag the Beer entity from the Data Sources onto the design surface, just under the DataGrid. Note that a Details view is automatically generated for you:

NOTE: We could also the the DataForm control for this Details View. However, VS2010’s Cider Designer does not support editing of Templates – so we would instead need to use Expression Blend, or hand-edit the template in XAML.
Let’s add a Dropdown (ComboBox) for the Brewers. In the Data Sources Window, select the Brewer Dropdown and the select Customize:

We want to show a ComboBox for the Brewers selection, so select ComboBox and click OK.

Back in the Data Sources window, drop down Brewer again and select ComboBox:

Delete the existing “Brewer Id” TextBox and replace it with a ComboBox by dragging the Brewer entity from the Data Sources window into the Grid Cell.

IMPORTANT NOTE: The following step is a temporary work-around for the Silverlight 4 Beta – there is an issue where the DomainDataSource cannot expose its Type information to the Design Surface. Hopefully a future release will fix this issue! But in the meantime we need to add a d:DataContext attribute as follows:
We need to inform the Design Surface/Properties Window that our main Grid container will be bound to a Beer at runtime. We can do this using a design-time DataContext attribute to our container Grid:
d:DataContext="{d:DesignInstance Type=my:Beer}" DataContext="{Binding ElementName=beerDomainDataSource, Path=Data}" …
Now we can data bind our ComboBox. Select the new ComboBox and in the Properties Panel, select the Advanced Properties icon next to SelectedItem and then “Apply Data Binding”

We want to bind the SelectedValue of the ComboBox to the Brewer value for the Beer, so select the Brewer property by double-clicking on it:

Then, set the SelectedValuePath to BrewerId:

Next, we’d like to display the name of the Brewery in the ComboBox instead of the Id, so change the DisplayMemberPath property to BreweryName:

There is one other little thing to take care of for the ComboBox, and this is because of timing issues which I talked about in a previous blog post. In order to make sure that the ComboBox has its Items filled at the time its SelectedItem is bound, we need to make both the Main Beer DomainDataSource and the Brewers DomainDataSource point to the same Data Context.
To do this, add in a Resources section to the page and define a BeerContext…
<navigation:Page.Resources>
<my:BeerContext x:Key="beerDomainContext" />
</navigation:Page.Resources>
Then, find the beerDomainDataSource (you can use the Document Outline to find it) and in the Properties Window, set its DomainContext to the beerDomainContext static resource (remember that you can double-click on the final element you want to bind to once you navigate to it).

Then find the brewerDomainDataSource and again set the DomainContext to the beerDomainContext. Now, both DomainDataSource objects are pointing to a single DataContext.
Run the project, and note that the ComboBox is populated with Brewers, and shows the correct Brewer for each selected row.
So far we have a simple Master/Detail setup, but we haven’t actually submitted changed back through the Service layer and into the Database.
Add a Button to the UI named btnSave. Double-click this button to get to the event handler in the code-behind.
At the very top of the class file, add a using statement to import the BeerDB.Web namespace (this is the namespace for the RIA Services generated Context and Proxy classes):
using BeerDB.Web;
At the top of the class definition, add a variable to store the Domain Data Source instance for beers:
DomainDataSource _ddsBeers;
In the OnNavigatedTo event handler, get a reference to the beerDomainDataSource in code, and wire up the SubmittedChanges event handler:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
_ddsBeers = this.FindName("beerDomainDataSource") as DomainDataSource;
_ddsBeers.SubmittedChanges += new EventHandler(_ddsBeers_SubmittedChanges);
}
Handle the SubmittedChanges event so that it shows any exceptions, and shows a Submit Complete message on success:
void _ddsBeers_SubmittedChanges(object sender, SubmittedChangesEventArgs e)
{
if (e.HasError)
{
System.Windows.MessageBox.Show(e.Error.ToString(), "Submit Error", System.Windows.MessageBoxButton.OK);
e.MarkErrorAsHandled();
}
else
MessageBox.Show("Submit Complete.");
}
Finally, implement the btnSave.Clicked event so that it submits changes to the Beer Domain Data Source:
_ddsBeers.SubmitChanges();
Give the project a run and try Updating some records.
Add a Reference to the assembly System.Windows.Data.dll. NOTE that in the Silverlight Beta, you may need to manually Browse to the folder C:\Program Files\Microsoft SDKs\Silverlight\v4.0\Libraries\Client to add this assembly.
Add a new Button named btnAddNew and then add the following code in its Click event, which will add a new Record into the Grid. It does this by creating a new Beer object and adding it to the DomainDataSource’s DataView collection. DataView is meant for code-based programmatic access of Data, and you can learn more about that here - http://jeffhandley.com/archive/2009/11/16/domaindatasourceview-again.aspx
private void btnAddNew_Click(object sender, RoutedEventArgs e)
{
// only allow a new row insert on an existing saved row
if ((_ddsBeers.DataView.CurrentItem as Beer).BeerId > 0)
{
Beer newBeer = new Beer();
_ddsBeers.DataView.Add(newBeer);
// disable the Grid until user saves
beerDataGrid.IsEnabled = false;
}
else
MessageBox.Show("Save the current NEW Beer before adding a new one.");
}
We can delete a record by calling Remove on the DataView Collection of the DomainDataSource class. Add a new button named btnDelete, and add implement it’s Click event handler as follows:
private void btnDelete_Click(object sender, RoutedEventArgs e)
{
_ddsBeers.DataView.Remove(_ddsBeers.DataView.CurrentItem);
_ddsBeers.SubmitChanges();
beerDataGrid.IsEnabled = true;
}
We get some basic data type validation automatically, with no custom coding required. For example, if you run the application and try to enter a character value in the Alcohol Content field, you’ll see a validation error is displayed, because this field is a decimal type.

If we want to add other validation, we can do so in the BeerService.metadata.cs file, inside the Web project of the solution. When we add validation to this metadata file, the validation is automatically copied into the client-side Silverlight project – giving us validation at both client and server side. This is important for performance and to prevent attacks that spoof the client side.
Open BeerService.metadata.cs and add Validation attributes to the BeerMetadata class to validate string lengths, required fields, and formatting:
public int BeerId;
[Required(ErrorMessage = "You must enter a Beer Name.")]
[StringLength(100, ErrorMessage = "Beer Name must be less than 100 chars.")]
public string BeerName;
public Brewer Brewer;
public Nullable BrewerId;
[StringLength(100, ErrorMessage = "Color must be less than 100 chars.")]
public string Color;
[StringLength(200, ErrorMessage = "Image Url must be less than 200 chars.")]
public string ImageUrl;
public Nullable IntroductionDate;
[StringLength(200, ErrorMessage = "More Info Url must be less than 200 chars.")]
[RegularExpression(@"^http\://[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(/\S*)?$", ErrorMessage = "More Info Url must be a valid Url.")]
public string MoreInfoUrl;
Let’s also add in a ValidationSummary control so that we can see all validation errors in our Details form. Right-click the Toolbox in Visual Studio and select Choose Items. Select the ValidationSummary control:

Next, drag a ValidationSummary control from the Toolbox inside the Grid containing the Details form. You should add a new RowDefinition to the Grid so that the ValidationSummary can show at the bottom of the other fields.
Run the application and test the validation of the various fields:

Besides the “canned” validation shown above, we can also define Custom Validation based on any kind of logic we prefer. Since the validation must execute on both the Server and the Silverlight Client, we place this Custom Validation in a Shared class. Shared Classes are automatically compiled into the Silverlight client when they are created on the Server side web project.
In this example, we’ll add a validation method that only allows certain colors. Note that, in a real world example, we would probably define this in a “lookup table” instead of code.
Add a new Class named CustomValidateColors.shared.cs to the BeerDB.Web project. Note that simply by adding the “.shared.cs” suffix, this will cause RIA Services to automatically copy and compile the class to the Silverlight application.

Add a new static method to the class which validates that the color of a beer is within a given list. Note the signature of the method.
public static ValidationResult IsValidBeerColor(object value,
ValidationContext context)
{
string[] colors = { "Light", "Amber", "Dark" };
if (colors.Contains(value))
{
return ValidationResult.Success;
}
else
{
return new ValidationResult(context.DisplayName + " must be Light, Amber, or Dark.");
}
}
You can now use this new validation method on a property in the metadata class. Open BeerService.metadata.cs and add in a CustomValidation attribute which uses the IsValidBeerColor method to validate the beer color.
[StringLength(100)]
[CustomValidation(typeof(CustomValidateColors), "IsValidBeerColor")]
public string Color;
Run the project and note the validation message for Color.