Like most .NET Developers, I do a lot of Line of Business application programming, and I’m very excited about .NET RIA Services. In this (very long) blog post, I’ll update my Beer DB Demo using .NET RIA Services and the Silverlight Business Application Template. In the past, I’ve used the Beer DB for several talks and demos, including a Silverlight 1.1 Alpha Demo named Andy’s Fridge, and a Silverlight and LINQ to SQL demo.
While there is a ton of great information out there on .NET RIA Services, I found many details hard to find and I hope this walkthrough will be helpful to people encountering the same issues I did. Note that this was created using the .NET RIA Services July 2009 Preview – so if you’re using this with a later release there may be breaking changes.
Here is a look at what the completed app looks like – a simple Master/Detail interface with validation and Insert/Update/Delete operations:

Contents
Getting Started
1. Ensure you have all of the required setup components (available from http://silverlight.net/getstarted/
a. Microsoft Visual Studio 2008
b. Microsoft Expression Blend 3
c. Silverlight 3 Tools for Visual Studio
d. .NET RIA Services (this walkthrough created with July 2009 Preview)
e. A copy of the Beers Database and Images. These are available in the ZIP download, under \BeerDB.Web\ClientBin\images and \BeerDB.Web\App_Data.
2. Create a new “Silverlight Business Application” named BeerDB.

3. 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 template 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.

4. In Solution Explorer, right-click the BeerDB (Silverlight) project and select properties. Note the .NET RIA Services link, which is pointing the Silverlight project to the Web project in the solution. This link is required because, as we’ll see, automatic code and proxy generation is performed when the solution is compiled. For example, when we add a new server-side domain method to retrieve data, a proxy is created to call that method.

Adding a New Page
1. 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”


2. In Solution Explorer, find MainPage.xaml in the Silverlight project. Right-click it and select “Open in Expression Blend”
3. In the Objects and Timeline panel (or on the artboard), find and select the Divider1 and Link2 elements. Then Right-click and Copy, then Right-click and Paste.

4. Change the text for this newly copied link to “beers” – and in the Properties Panel, change the NavigateUri to “/BeerMaintenance”


5. Run the project. Note that the new “beers” link is available and you can navigate between all of the Views.
Adding the Database and Image Assets
1. 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).
2. 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.
3. Your BeerDB.Web project should now look something like this:

Creating the Entity Data Model
1. 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

2. Select Generate from Database and click Next

3. On this step, select Database.mdf for the data connection, and Save entity connection settings in Web.Config. Change the Connection settings to “BeerEntities” and click Next.

4. Select the Beers and Brewers Tables in this step and enter “BeerModel” for the Model Namespace. Then Click Finish.

5. Note our simple database schema. Close the BeerModel.edmx and select Ctrl+Shift+B to build the solution.
Creating the Domain Service Class
1. Right-click the BeerDB.Web project and select Add/New Item. Select Domain Service Class and name the class BeerService.

2. Be sure that the BeerEntities Entity Framework model is selected (if it is not available, make sure you did a rebuild!) Check the Beers and Brewers entities, Enable Editing, and check the “Generate associated classed for metadata” checkbox.

Querying using DomainDataSource
1. Back in the BeerDB (Silverlight) project, open the BeerMaintenance.xaml page.
2. At the top of the page, add in a new xmlns for the current project. This will allow access to the generated proxy classes from the web project.
xmlns:BeerDB.Web="clr-namespace:BeerDB.Web"
xmlns:BeerDB.Web="clr-namespace:BeerDB.Web"
3. Add in a Resources section to the navigation page, and add an instance of the beer Domain Context. Note: we need to create a single instance of the beer domain context to avoid issues with nested listbound elements such as ComboBox and ListBox.
<navigation:Page.Resources> <BeerDB:BeerContext x:Key="beerDomainContext" />
</navigation:Page.Resources>
<navigation:Page.Resources> <BeerDB:BeerContext x:Key="beerDomainContext" />
</navigation:Page.Resources>
4. Add in a DomainDataSource control from the toolbox, and tweak it like so –
a. Add in a DomainContext that points to our static resource BeerContext
b. Set AutoLoad to True
c. Set the QueryName to Beers
d. Handle the LoadError and LoadedData events
<riaControls:DomainDataSource x:Name="ddsBeers"
DomainContext="{StaticResource beerDomainContext}"
AutoLoad="True"
QueryName="GetBeers"
LoadError="ddsBeers_LoadError"
LoadedData="ddsBeers_LoadedData">
</riaControls:DomainDataSource>
<riaControls:DomainDataSource x:Name="ddsBeers"
DomainContext="{StaticResource beerDomainContext}"
AutoLoad="True"
QueryName="GetBeers"
LoadError="ddsBeers_LoadError"
LoadedData="ddsBeers_LoadedData">
</riaControls:DomainDataSource>
5. Drag in a DataGrid from the toolbox and tweak it so that it binds to the ddsBeers Domain Data Source.
<data:DataGrid x:Name="grdBeers" ItemsSource="{Binding Data, ElementName=ddsBeers}">
</data:DataGrid>
<data:DataGrid x:Name="grdBeers" ItemsSource="{Binding Data, ElementName=ddsBeers}">
</data:DataGrid>
6. In the code-behind file, implement the ddsBeers_LoadError and ddsBeers_LoadedData methods so that they raise any exceptions that occur during processing (see the section Debugging and Error Handling for more info).
private void ddsBeers_LoadError(object sender, LoadErrorEventArgs e)
{
if (e.Exception != null)
throw e.Exception;
}
private void ddsBeers_LoadedData(object sender, LoadedDataEventArgs e)
{
if (e.Error != null)
throw e.Error;
}
private void ddsBeers_LoadError(object sender, LoadErrorEventArgs e)
{
if (e.Exception != null)
throw e.Exception;
}
private void ddsBeers_LoadedData(object sender, LoadedDataEventArgs e)
{
if (e.Error != null)
throw e.Error;
}
7. Run the project – you should see a DataGrid with Beers data.
Sorting and Filtering using DomainDataSource
1. To sort our Beers by BeerName, we can use a SortDescriptor on the DomainDataSource. First add in another xmlns namespace at the top of BeerMaintenance.xaml:
xmlns:riaData="clr-namespace:System.Windows.Data;assembly=System.Windows.Ria.Controls"
xmlns:riaData="clr-namespace:System.Windows.Data;assembly=System.Windows.Ria.Controls"
2. Now we can use the Sort Descriptor by adding it inside the DomainDataSource control. In this example, we are sorting by BeerName:
<riaControls:DomainDataSource x:Name="ddsBeers"
DomainContext="{StaticResource beerDomainContext}"
AutoLoad="True"
QueryName="GetBeers"
LoadError="ddsBeers_LoadError"
LoadedData="ddsBeers_LoadedData">
<riaControls:DomainDataSource.SortDescriptors> <riaData:SortDescriptor Direction="Ascending" PropertyPath="BeerName" /> </riaControls:DomainDataSource.SortDescriptors>
</riaControls:DomainDataSource>
<riaControls:DomainDataSource x:Name="ddsBeers"
DomainContext="{StaticResource beerDomainContext}"
AutoLoad="True"
QueryName="GetBeers"
LoadError="ddsBeers_LoadError"
LoadedData="ddsBeers_LoadedData">
<riaControls:DomainDataSource.SortDescriptors> <riaData:SortDescriptor Direction="Ascending" PropertyPath="BeerName" /> </riaControls:DomainDataSource.SortDescriptors>
</riaControls:DomainDataSource>
3. Note that we can also add filtering declaratively, by using a FilterDescriptor. Add in a TextBox named txtFilter, along with a label control and place these at the top of the DataGrid:
<StackPanel Orientation="Horizontal"> <TextBlock Text="Filter: "/> <TextBox x:Name="txtFilter" Width="100" />
</StackPanel>
<StackPanel Orientation="Horizontal"> <TextBlock Text="Filter: "/> <TextBox x:Name="txtFilter" Width="100" />
</StackPanel>
4. Now we can add a FilterDescriptor to our DomainDataSource and filter on BeerName. The value of the filter is bound from the txtFilter.Text value using a ControlParameter.
<riaControls:DomainDataSource x:Name="ddsBeers"
DomainContext="{StaticResource beerDomainContext}"
AutoLoad="True"
QueryName="GetBeers"
LoadError="ddsBeers_LoadError"
LoadedData="ddsBeers_LoadedData">
<riaControls:DomainDataSource.SortDescriptors> <riaData:SortDescriptor Direction="Ascending" PropertyPath="BeerName" /> </riaControls:DomainDataSource.SortDescriptors>
<riaControls:DomainDataSource.FilterDescriptors> <riaData:FilterDescriptorCollection x:Name="DataSourceFilters"> <riaData:FilterDescriptor PropertyPath="BeerName" Operator="StartsWith"> <riaData:ControlParameter ControlName="txtFilter" PropertyName="Text" RefreshEventName="TextChanged" /> </riaData:FilterDescriptor> </riaData:FilterDescriptorCollection> </riaControls:DomainDataSource.FilterDescriptors>
</riaControls:DomainDataSource>
<riaControls:DomainDataSource x:Name="ddsBeers"
DomainContext="{StaticResource beerDomainContext}"
AutoLoad="True"
QueryName="GetBeers"
LoadError="ddsBeers_LoadError"
LoadedData="ddsBeers_LoadedData">
<riaControls:DomainDataSource.SortDescriptors> <riaData:SortDescriptor Direction="Ascending" PropertyPath="BeerName" /> </riaControls:DomainDataSource.SortDescriptors>
<riaControls:DomainDataSource.FilterDescriptors> <riaData:FilterDescriptorCollection x:Name="DataSourceFilters"> <riaData:FilterDescriptor PropertyPath="BeerName" Operator="StartsWith"> <riaData:ControlParameter ControlName="txtFilter" PropertyName="Text" RefreshEventName="TextChanged" /> </riaData:FilterDescriptor> </riaData:FilterDescriptorCollection> </riaControls:DomainDataSource.FilterDescriptors>
</riaControls:DomainDataSource>
Adding a DataForm (Details View)
A DataForm can quickly add Update, Insert, and Delete functionality to an application.
1. Open BeerMaintenance.xaml in Blend. Place the grdBeers Grid in a StackPanel and add a DataForm into the StackPanel, so that the resulting XAML resembles the following:
<StackPanel Orientation="Vertical">
<data:DataGrid x:Name="grdBeers" ItemsSource="{Binding Data, ElementName=ddsBeers}"> </data:DataGrid>
<dataFormToolkit:DataForm> </dataFormToolkit:DataForm>
</StackPanel>
<StackPanel Orientation="Vertical">
<data:DataGrid x:Name="grdBeers" ItemsSource="{Binding Data, ElementName=ddsBeers}"> </data:DataGrid>
<dataFormToolkit:DataForm> </dataFormToolkit:DataForm>
</StackPanel>
2. Add the following attributes to the DataForm:
<dataFormToolkit:DataForm x:Name="dataForm1"
ItemsSource="{Binding Data, ElementName=ddsBeers}"
CommandButtonsVisibility="All" AutoEdit="True"
AutoCommit="True"
EditEnded="dataForm1_EditEnded"
DeletingItem="dataForm1_DeletingItem" > <d:DataContext> <BeerDB:Beers></BeerDB:Beers> </d:DataContext>
</dataFormToolkit:DataForm>
<dataFormToolkit:DataForm x:Name="dataForm1"
ItemsSource="{Binding Data, ElementName=ddsBeers}"
CommandButtonsVisibility="All" AutoEdit="True"
AutoCommit="True"
EditEnded="dataForm1_EditEnded"
DeletingItem="dataForm1_DeletingItem" > <d:DataContext> <BeerDB:Beers></BeerDB:Beers> </d:DataContext>
</dataFormToolkit:DataForm>
a. x:Name=”dataForm1” - this is the ID of the DataForm in code.
b. ItemsSource=”{Binding Data, ElementName=ddsBeers}” - this is the runtime databinding source for the form to get data. Since this is the same data source as the DataGrid, they will stay in sync.
c. CommandButtonsVisibility=”All” - this shows all of the Edit and Paging buttons on the DataForm.
d. AutoEdit=”True” - this allows the user to edit the data in the form without explicitly selecting an “Edit” command button.
e. AutoCommit=”True” - this automatically commits changes to the DataForm when the user selects a different row.
f. EditEnded Event – handle this so that we can commit updates.
g. DeletingItem – handle this so that we confirm if the user wants to delete an item.
h. d:DataContext – this is what Blend uses at Design Time to determine what binding context is available. We set this to an instance of the Beers class so that we can bind fields for the Beer using Blend’s design tools.
3. In the code-behind file for this page, import the BeerDB.Web namespace so that we can access the proxy classes generated from the server-side context.
using BeerDB.Web;
using System.Windows.Ria.Data;
using BeerDB.Web;
using System.Windows.Ria.Data;
4. Add an instance of the BeerContext as a class-level variable.
BeerContext _beerContext;
BeerContext _beerContext;
5. In the OnNavigatedTo event handler, get a reference to the beerDomainContext page resource and wire up an event handler for the CollectionChanged event.
// Executes when the user navigates to this page.protected override void OnNavigatedTo(NavigationEventArgs e)
{
// Our main Data Context is in the Page's Resource section _beerContext = this.Resources["beerDomainContext"] as BeerContext;
// We need to handle CollectionChanged for Delete's performed on the DataForm ddsBeers.DataView.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(DataView_CollectionChanged);
}
// Executes when the user navigates to this page.protected override void OnNavigatedTo(NavigationEventArgs e)
{
// Our main Data Context is in the Page's Resource section _beerContext = this.Resources["beerDomainContext"] as BeerContext;
// We need to handle CollectionChanged for Delete's performed on the DataForm ddsBeers.DataView.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(DataView_CollectionChanged);
}
6. Implement the following event handlers to Update/Insert and Delete beer records from the DataForm. Note that we need to handle the CollectionChanged event in order to Delete records, and the EditEnded event handles both Insert and Update operations.
private void dataForm1_EditEnded(object sender, System.Windows.Controls.DataFormEditEndedEventArgs e)
{
dataForm1.CommitEdit();
// is this a NEW beer? If so we need to default the RowVersionId Beers beer = (dataForm1.CurrentItem as Beers);
if (beer.BeerId == 0)
{
beer.RowVersionId = new byte[1];
}
if (e.EditAction == DataFormEditAction.Commit)
_beerContext.SubmitChanges(SubmitChangesComplete, null);
}
void DataView_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
// We need to handle this event for Deletes, since EditEnded is not called on a delete. if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove
&& (e.OldItems[0] as Beers).BeerId > 0)
_beerContext.SubmitChanges(SubmitChangesComplete, null);
}
void SubmitChangesComplete(SubmitOperation op)
{
if (op.HasError)
throw op.Error;
}
private void dataForm1_EditEnded(object sender, System.Windows.Controls.DataFormEditEndedEventArgs e)
{
dataForm1.CommitEdit();
// is this a NEW beer? If so we need to default the RowVersionId Beers beer = (dataForm1.CurrentItem as Beers);
if (beer.BeerId == 0)
{
beer.RowVersionId = new byte[1];
}
if (e.EditAction == DataFormEditAction.Commit)
_beerContext.SubmitChanges(SubmitChangesComplete, null);
}
void DataView_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
// We need to handle this event for Deletes, since EditEnded is not called on a delete. if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove
&& (e.OldItems[0] as Beers).BeerId > 0)
_beerContext.SubmitChanges(SubmitChangesComplete, null);
}
void SubmitChangesComplete(SubmitOperation op)
{
if (op.HasError)
throw op.Error;
}
7. Run the project and test the Add, Update, and Delete methods of the DataForm.
Customizing the DataForm in Blend
1. Note that, when we added the DataForm, we included the following “d:DataContext” attribute:
<d:DataContext> <BeerDB:Beers></BeerDB:Beers> </d:DataContext> This informs Blend of the type of data that we will be binding to the DataForm at run-time, so that it can support design time data binding information. Anytime a property is prefixed with “d:”, it is defined in Blend’s namespace and is generally used to set design time properties.
<d:DataContext> <BeerDB:Beers></BeerDB:Beers> </d:DataContext> This informs Blend of the type of data that we will be binding to the DataForm at run-time, so that it can support design time data binding information. Anytime a property is prefixed with “d:”, it is defined in Blend’s namespace and is generally used to set design time properties.
2. Right-click BeerMaintenance.xaml and select “Open in Expression Blend”
3. In the Objects and Timeline Panel, find dataForm1. Right-click it and select Edit Additional Templates/Edit EditTemplate/Create Empty.

4. On the “Create DataTemplate Resource” dialog, accept the defaults and click OK.
5. Resize the Grid in the data template so that it is about 500 pixels wide and 400 pixels high.
6. Increase the number of rows in the Grid so that there are 6 rows. You can do this by clicking in the left-hand border of the Grid on the arboard:

7. Increase the number of columns in the Grid so that there are 2 columns. You can do this by clicking in the top border of the Grid on the arboard:

8. Add a TextBlock into the first row and column of the Grid. Ensure that its Margins are set to 0, set its HorizontalAlignment to Left and VerticalAlignment to Center. Change the text property to “Beer Name:”

9. Add a TextBox into the first row and second column of the Grid. Ensure that its Margins are set to 0, set its HorizontalAlignment to Left and VerticalAlignment to Center. Your Grid should now look something like this:

10. Next we’ll create the databinding for the TextBox. Select the Advanced Property Options control (the little white square) to the right of the Text property for the TextBox.

11. Select the Data Binding option from the popup menu.

12. On the Create Data Binding Dialog, select the BeerName field. Note that this is on the “Explicit Data Context” tab.

13. Repeat the steps above to add TextBlocks, TextBoxes, and Data Bindings for the Color, Alcohol, ImageUrl, and MoreInfo properties. Put a placeholder in for the Brewery Label (later on we’ll use a ComboBox for this field). The resulting XAML in the Data Template should look similar to the following:
<Grid x:Name="grdBeerDetails" Grid.Column="1"> <Grid.ColumnDefinitions> <ColumnDefinition Width="100"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="40" /> <RowDefinition Height="40" /> <RowDefinition Height="40"/> <RowDefinition Height="40"/> <RowDefinition Height="40" /> <RowDefinition Height="40" /> </Grid.RowDefinitions> <TextBlock x:Name="txtBeerNameLabel" Margin="0,0,4,0" VerticalAlignment="Top" Grid.Column="0" Grid.Row="0" Text="Name:" /> <TextBox x:Name="txtBeerName" VerticalAlignment="Top" Grid.Column="1" Grid.Row="0" Height="24" Text="{Binding BeerName, Mode=TwoWay}" />
<TextBlock x:Name="txtColorLabel" Margin="0,0,4,0" VerticalAlignment="Top" Grid.Column="0" Grid.Row="1" Text="Color:" /> <TextBox x:Name="txtColor" VerticalAlignment="Top" Grid.Column="1" Grid.Row="1" Height="24" Text="{Binding Color, Mode=TwoWay}" />
<TextBlock x:Name="txtAlcoholLabel" Margin="0,0,4,0" VerticalAlignment="Top" Grid.Column="0" Grid.Row="2" Text="Alcohol:" /> <TextBox x:Name="txtAlcohol" VerticalAlignment="Top" Grid.Column="1" Grid.Row="2" Height="24" Text="{Binding AlcoholContent, Mode=TwoWay}" />
<TextBlock x:Name="txtBreweryLabel" Margin="0,0,4,0" VerticalAlignment="Top" Grid.Column="0" Grid.Row="3" Text="Brewery:" />
<TextBlock x:Name="txtImageUrlLabel" Margin="0,0,4,0" VerticalAlignment="Top" Grid.Column="0" Grid.Row="4" Text="Image Url:" /> <TextBox x:Name="txtImageUrl" VerticalAlignment="Top" Grid.Column="1" Grid.Row="4" Height="24" Text="{Binding ImageUrl, Mode=TwoWay}" />
<TextBlock x:Name="txtMoreInfoLabel" Margin="0,0,4,0" VerticalAlignment="Top" Grid.Column="0" Grid.Row="5" Text="More Info Url:" /> <TextBox x:Name="txtMoreInfo" VerticalAlignment="Top" Grid.Column="1" Grid.Row="5" Height="24" Text="{Binding MoreInfoUrl, Mode=TwoWay}" />
</Grid>
<Grid x:Name="grdBeerDetails" Grid.Column="1"> <Grid.ColumnDefinitions> <ColumnDefinition Width="100"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="40" /> <RowDefinition Height="40" /> <RowDefinition Height="40"/> <RowDefinition Height="40"/> <RowDefinition Height="40" /> <RowDefinition Height="40" /> </Grid.RowDefinitions> <TextBlock x:Name="txtBeerNameLabel" Margin="0,0,4,0" VerticalAlignment="Top" Grid.Column="0" Grid.Row="0" Text="Name:" /> <TextBox x:Name="txtBeerName" VerticalAlignment="Top" Grid.Column="1" Grid.Row="0" Height="24" Text="{Binding BeerName, Mode=TwoWay}" />
<TextBlock x:Name="txtColorLabel" Margin="0,0,4,0" VerticalAlignment="Top" Grid.Column="0" Grid.Row="1" Text="Color:" /> <TextBox x:Name="txtColor" VerticalAlignment="Top" Grid.Column="1" Grid.Row="1" Height="24" Text="{Binding Color, Mode=TwoWay}" />
<TextBlock x:Name="txtAlcoholLabel" Margin="0,0,4,0" VerticalAlignment="Top" Grid.Column="0" Grid.Row="2" Text="Alcohol:" /> <TextBox x:Name="txtAlcohol" VerticalAlignment="Top" Grid.Column="1" Grid.Row="2" Height="24" Text="{Binding AlcoholContent, Mode=TwoWay}" />
<TextBlock x:Name="txtBreweryLabel" Margin="0,0,4,0" VerticalAlignment="Top" Grid.Column="0" Grid.Row="3" Text="Brewery:" />
<TextBlock x:Name="txtImageUrlLabel" Margin="0,0,4,0" VerticalAlignment="Top" Grid.Column="0" Grid.Row="4" Text="Image Url:" /> <TextBox x:Name="txtImageUrl" VerticalAlignment="Top" Grid.Column="1" Grid.Row="4" Height="24" Text="{Binding ImageUrl, Mode=TwoWay}" />
<TextBlock x:Name="txtMoreInfoLabel" Margin="0,0,4,0" VerticalAlignment="Top" Grid.Column="0" Grid.Row="5" Text="More Info Url:" /> <TextBox x:Name="txtMoreInfo" VerticalAlignment="Top" Grid.Column="1" Grid.Row="5" Height="24" Text="{Binding MoreInfoUrl, Mode=TwoWay}" />
</Grid>
14. Next we’ll add a ComboBox into the DataForm for the BreweryName selection. Note that we need to query a list of Brewery Names to fill this ComboBox. To query these, add in a new DomainDataSource control just inside the Grid for the Data Template.
<riaControls:DomainDataSource x:Name="ddsBrewers"
AutoLoad="True"
QueryName="GetBrewers"
LoadError="ddsBrewers_LoadError"
DomainContext="{StaticResource beerDomainContext}"
LoadedData="ddsBrewers_LoadedData"/>
<riaControls:DomainDataSource x:Name="ddsBrewers"
AutoLoad="True"
QueryName="GetBrewers"
LoadError="ddsBrewers_LoadError"
DomainContext="{StaticResource beerDomainContext}"
LoadedData="ddsBrewers_LoadedData"/>
15. Now we can add in a ComboBox into the DataTemplate which uses the ddsBrewers Data Source above. Note the ItemsSource binding which uses the Data Source, and the SelectedItem binding, which uses the Brewers property of the Beer that the record is bound to.
<ComboBox x:Name="cboBrewery"
Grid.Column="1" Grid.Row="3" Height="24" ItemsSource="{Binding Data, ElementName=ddsBrewers}" SelectedItem="{Binding Brewers, Mode=TwoWay}" DisplayMemberPath="BreweryName"/>
<ComboBox x:Name="cboBrewery"
Grid.Column="1" Grid.Row="3" Height="24" ItemsSource="{Binding Data, ElementName=ddsBrewers}" SelectedItem="{Binding Brewers, Mode=TwoWay}" DisplayMemberPath="BreweryName"/>
16. Add in the event handlers for the LoadError and LoadedData events for the Brewers query. Note that any errors from the DataContext are not automatically raised, so we need these event handlers to do so.
private void ddsBrewers_LoadError(object sender, LoadErrorEventArgs e)
{
if (e.Exception != null)
throw e.Exception;
}
private void ddsBrewers_LoadedData(object sender, LoadedDataEventArgs e)
{
if (e.Error != null)
throw e.Error;
}
private void ddsBrewers_LoadError(object sender, LoadErrorEventArgs e)
{
if (e.Exception != null)
throw e.Exception;
}
private void ddsBrewers_LoadedData(object sender, LoadedDataEventArgs e)
{
if (e.Error != null)
throw e.Error;
}
17. Run the project, and test the new DataForm template.
Adding Validation
1. Run the application and try setting the Alcohol Content field to a string value. Since the underlying field is a decimal, you get a validation warning. Validation on Data Types is built in automatically to the Data Form.

2. To add in validation other than data type checking, go to the BeerDB.Web project and open the BeerService.metadata.cs file. Find the BeersMetadata class and add the following attributes to enforce string lengths, required fields, and format. You can also optionally add in the error message to display when the validation errors occur.
internal sealed class BeersMetadata{
// Metadata classes are not meant to be instantiated. private BeersMetadata()
{
}
public Nullable<Decimal> AlcoholContent;
public int BeerId;
[Required]
[StringLength(100, ErrorMessage="You must enter a Beer Name.")]
public string BeerName;
public Brewers Brewers;
[StringLength(100)]
public string Color;
public EntityState EntityState;
[StringLength(200, ErrorMessage = "Image Url must be less than 200 chars.")]
public string ImageUrl;
public Nullable<DateTime> 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;
public byte[] RowVersionId;
}
internal sealed class BeersMetadata{
// Metadata classes are not meant to be instantiated. private BeersMetadata()
{
}
public Nullable<Decimal> AlcoholContent;
public int BeerId;
[Required]
[StringLength(100, ErrorMessage="You must enter a Beer Name.")]
public string BeerName;
public Brewers Brewers;
[StringLength(100)]
public string Color;
public EntityState EntityState;
[StringLength(200, ErrorMessage = "Image Url must be less than 200 chars.")]
public string ImageUrl;
public Nullable<DateTime> 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;
public byte[] RowVersionId;
}
3. Run the application and note the new validation for the data form.

Custom Validation and Shared Classes
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.
1. Add a new Class named CustomValideColors.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.

2. 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.");
}
}
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.");
}
}
3. 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;
[StringLength(100)]
[CustomValidation(typeof(CustomValidateColors), "IsValidBeerColor")]
public string Color;
4. Run the project and note the validation message for Color.
Styling the Application
The Silverlight Business Application Template has a single Styles.xaml resource file which defines the overall look and feel of the application. We can replace this file with a different set of style information, and can even use one of many free styles available from the community.
1. In the BeerDB (Silverlight) project, expand the Assets folder and note the Styles.xaml file. This contains the style information for our application.

2. There are many free alternate style templates for RIA Services available on the Expression Community Site. Simply download the ZIP and extract the contents into your Assets subfolder, overwriting the existing Styles.xaml. Below is the application with the Seeing Sound style applied.

Displaying the Product Image
Our database contains links to beer labels, so let’s use these to overlay a beer bottle and create a copy of the “product.” This will give the Details view a bit more spice!
1. Open BeerMaintenance.xaml in Expression Blend.
2. Edit the DataForm’s Data Template by right-clicking dataForm1 and selecting Edit Additional Templates/Edit EditTemplate/Edit Current
3. Right-click grdBeerDetails and select Group Into/StackPanel. Set the StackPanel’s Orientation to Horizontal.
4. Add a Canvas into the StackPanel, and add two Image controls into the Canvas. Set the Source of the first image to /images/beerBottle.png, and bind the Source of the second image to the ImageUrl property. Align the Images so that the label overlays the bottle. The resulting XAML should look similar to the following:
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Width="600">
<Grid x:Name="grdBeerDetails" Width="300" Margin="0,40,40,0">
<!-- Grid details removed for brevity -->
</Grid> <Canvas Height="340" Width="200"> <Image x:Name="imgBeerBottle" Source="/images/beerBottle.png" Width="93" Height="311" Canvas.Left="0" Canvas.Top="0" /> <Image x:Name="imgBeerLabel" Source="{Binding ImageUrl}" Width="73" Height="95" Canvas.Left="10" Canvas.Top="160" Stretch="Fill" /> </Canvas>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Width="600">
<Grid x:Name="grdBeerDetails" Width="300" Margin="0,40,40,0">
<!-- Grid details removed for brevity -->
</Grid> <Canvas Height="340" Width="200"> <Image x:Name="imgBeerBottle" Source="/images/beerBottle.png" Width="93" Height="311" Canvas.Left="0" Canvas.Top="0" /> <Image x:Name="imgBeerLabel" Source="{Binding ImageUrl}" Width="73" Height="95" Canvas.Left="10" Canvas.Top="160" Stretch="Fill" /> </Canvas>
</StackPanel>
Debugging and Error Handling
Here are a couple of hints on Debugging and Error Handling with .NET RIA Services.
1. If an error occurs during a remote call, such as an authorization failure, the Silverlight client will not raise the exception by default. This is because exceptions are silent by default. So be sure to handle the LoadError and LoadedData Events…
<riaControls:DomainDataSource x:Name="ddsBeers"
DomainContext="{StaticResource beerDomainContext}"
AutoLoad="True"
QueryName="GetBeers"
LoadError="ddsBeers_LoadError"
LoadedData="ddsBeers_LoadedData">
</riaControls:DomainDataSource>
…and then implement these handlers to check for an error, and minimally raise the exception:
private void ddsBeers_LoadError(object sender, LoadErrorEventArgs e)
{
if (e.Exception != null)
throw e.Exception;
}
private void ddsBeers_LoadedData(object sender, LoadedDataEventArgs e)
{
if (e.Error != null)
throw e.Error;
}
<riaControls:DomainDataSource x:Name="ddsBeers"
DomainContext="{StaticResource beerDomainContext}"
AutoLoad="True"
QueryName="GetBeers"
LoadError="ddsBeers_LoadError"
LoadedData="ddsBeers_LoadedData">
</riaControls:DomainDataSource>
…and then implement these handlers to check for an error, and minimally raise the exception:
private void ddsBeers_LoadError(object sender, LoadErrorEventArgs e)
{
if (e.Exception != null)
throw e.Exception;
}
private void ddsBeers_LoadedData(object sender, LoadedDataEventArgs e)
{
if (e.Error != null)
throw e.Error;
}
2. In the current (July 2009) version of .NET RIA Services, some exceptions are not properly raised to the Silverlight client. When this happens you will generally get the following error:
Exception has been thrown by the target of an invocation.
To debug this issue, and see the exception on the server side, be sure to enable CLR Exceptions – in Visual Studio, select Debug/Exceptions, and check the Thrown checkbox for “Common Language Runtime Exceptions.”

Exception has been thrown by the target of an invocation.
To debug this issue, and see the exception on the server side, be sure to enable CLR Exceptions – in Visual Studio, select Debug/Exceptions, and check the Thrown checkbox for “Common Language Runtime Exceptions.”