Introduction
This article will demonstrate how to build a Silverlight 4 application that uses (Entity Framework) Complex Types at the client level (through WCF services).
Background
Before Silverlight 4, it wasn't possible to use complex types on the client, but now, Silverlight 4 can use RIA services to access (Complex) types that the Entity Framework creates. In previous versions of Silverlight, it was possible to access simple entity types but not complex types.
Using the code
Setting up the sample database
If you do not have Microsoft's Northwind database installed on your database server, you can download the script here (download script - 238 KB). Log into your SQL Server Management Studio and open a new query analyzer window, and run the unzipped script to create the database (including data) that is used in the Data Access Layer (DAL) within the overall project.
Create a new (empty) project and add three solution folders, for the data access layer, WCF service, and finally the Silverlight client.
Creating the Data Access Layer (Entity Model) project
Create the data access layer DLL by adding a new Class Library to the 'data access layer' solution in the project.
Add an entity framework model to the DAL:
Select the Stored Procedure 'Employee Sales by Country' to the entity model. This Stored Procedure will be used as it will return a complex type (fields from multiple tables).
Bring up the 'Model View' browser so that we can then easily navigate to the entity model.
Create\import the C# function that will represent the method to call the entity method in the DAL.
Create the complex type (class) for the Stored Procedure.
Your DAL project structure should look like this:
Creating the WCF Service project
Add a new WCF service to the WCF solution folder in the project.
Add a reference to the data access layer project in your references:
That is your newly added DAL project in your WCF project.
Add the following code to the IOrdersService
class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using OrdersAccessLayer;
namespace WcfOrdersService
{
[ServiceContract]
public interface IOrdersService
{
[OperationContract]
List<Employee_Sales_by_Country_Result>
GetOrderDetailsData(DateTime start, DateTime end);
}
}
Add the following code to the OrdersService
class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using OrdersAccessLayer;
namespace WcfOrdersService
{
public class OrdersService : IOrdersService
{
private OrdersController orderCtrl;
public OrdersService()
{
orderCtrl = new OrdersController();
}
public List<Employee_Sales_by_Country_Result>
GetOrderDetailsData(DateTime start, DateTime end)
{
return this.orderCtrl.GetOrderDetails(start, end);
}
}
}
Compile the service before adding it to the Silverlight project below.
Creating the Silverlight 4 Client project
Create a new Silverlight project:
Enable the Silverlight project for RIA services and assign it the OrdersService service.
Add a reference to the WCF service:
The Silverlight project after adding the service reference:
Add the following code to the code-behind for the MainPage.cs. Notice that in the method SaveScheduleCompleted
, there is some LINQ performed on the collection of (entity class) 'Employee_Sales_By_Country_Result
'. Thus we are able to create a class in the Entity Framework and also be able to use it in the client.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Silverlight4Console.OrdersServiceReference;
using System.ServiceModel;
namespace Silverlight4Console
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
this.LoadOrderDetails();
}
private void LoadOrderDetails()
{
try
{
DateTime start = DateTime.Parse("1997-01-01");
DateTime end = DateTime.Parse("1997-01-31");
OrdersServiceReference.OrdersServiceClient ordersService =
new OrdersServiceReference.OrdersServiceClient();
ordersService.GetOrderDetailsDataAsync(start, end);
ordersService.GetOrderDetailsDataCompleted +=
new EventHandler<GetOrderDetailsDataCompletedEventArgs>(
SaveScheduleCompleted);
}
catch (FaultException fe)
{
Console.WriteLine(fe.Message);
Console.WriteLine("stack trace");
Console.WriteLine(fe.StackTrace);
Console.ReadLine();
}
catch (CommunicationException commProblem)
{
if (commProblem.InnerException is FaultException)
{
Console.WriteLine("An unknown exception was received. " +
commProblem.InnerException.Message);
Console.ReadLine();
return;
}
else
{
Console.WriteLine("There was a communication problem. " +
commProblem.Message);
Console.WriteLine(commProblem.StackTrace);
}
}
}
private void SaveScheduleCompleted(object sender,
GetOrderDetailsDataCompletedEventArgs e)
{
this.personDataGrid.ItemsSource = e.Result.Where(ord=>ord.SaleAmount>2000);
}
}
}
Add the following XAML code to the MainPage
(a simple grid to bind\display order results):
<UserControl x:Class="Silverlight4Console.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400"
xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">
<Grid x:Name="LayoutRoot" Background="White">
<sdk:DataGrid AutoGenerateColumns="true" VerticalAlignment="Center"
HorizontalAlignment="Center" Height="100"
Margin="57,66,0,0" Name="personDataGrid" Width="auto" />
</Grid>
</UserControl>
Here is the overall project structure:
Screen output
Points of Interest
Just as a side note, I found it easier to bind the Silverlight application to the WCF service (enabling RIA services), by designing the data access layer, WCF service, and then creating the Silverlight application - I found that creating the Silverlight application first with no RIA service to bind to (but still enabling RIA services) meant that it didn't pick up the services when I later enabled them.