Silverlight Enabled WCF Services and Entity Framework 4 in a Silverlight Navigation Application

If you want to create a Silverlight application that consumes “Silverlight enabled WCF services” that work with EF4 to return data here’s what you have to do:

1. Create a Silverlight Navigation Application (for example)

image

image

2.Add a ADO.Net Entity Data Model to the Web project (the one that will provide data to be returned by the methods the WCF service):

image

image

image

Choose the tables you want the tool to include in your EF4 Conceptual model:

image

Regarding the ADO.Net Entity Data Model, you’re done.

3. The Silverlight enabled WCF service:

In the web project add a new Silverlight Enabled WCF service:

using System;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.Collections.Generic;
using UsingWCFServicesReloaded4.Web.Models;

namespace UsingWCFServicesReloaded4.Web.Services
{
    [ServiceContract(Namespace = "")]
    [SilverlightFaultBehavior]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class LendingService
    {
        [OperationContract]
        public List<Person> GetPeople()
        {
            LendsEntitiesObjectContext context = new LendsEntitiesObjectContext();            
            return context.People.ToList<Person>();
        }

        // Add more operations here and mark them with [OperationContract]
    }
}

Do not fall in the temptation of prematurely disposing of the ObjectContext. You frequently see EF4 examples where the ObjectContext for your entities is created inside a using block, something like this:

using System;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.Collections.Generic;
using UsingWCFServicesReloaded4.Web.Models;

namespace UsingWCFServicesReloaded4.Web.Services
{
    [ServiceContract(Namespace = "")]
    [SilverlightFaultBehavior]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class LendingService
    {
        [OperationContract]
        public List<person> GetPeople()
        {
            using (LendsEntitiesObjectContext context = new LendsEntitiesObjectContext())
            {
                return context.People.ToList<person>();
            }            
        }

        // Add more operations here and mark them with [OperationContract]
    }
}

This will bring you pain, I assure you. The reason for this to subtly not work is that (unless you are using POCOs with EF4, and without the virtual trick [ask me about it, if you’re interested]) the entities that were auto generated for you will contain references to the ObjectContext, in this case Person has references to LendsEntitiesObjectContext which at the time of the WCF black magic that occurs between this method (GetPeople) being invoked (which involves serializing the list of persons so it can be sent through the wire) and the results getting to the client invoking it, tragedy will happen, because that LendsEntitiesObjectContext will not exist at that time.

Now you have to add this service to your silverlight app:

image

Click discover and your service should be there:

image

So there you go, there’s not much more to it than this, just for kicks here’s the XAML and the source code of the Home.xaml file that was autogenerated by the Silverlight Navigation Application Visual Studio template that I’ve changed to display a datagrid with the results returned from the GetPeople method of the WCF service:

Views\Home.xaml’s XAML:

<navigation:Page x:Class="UsingWCFServicesReloaded4.Home" 
    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"
    xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
    mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480"
    Title="Home"
    Style="{StaticResource PageStyle}" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">

    <Grid x:Name="LayoutRoot">
        <ScrollViewer x:Name="PageScrollViewer" Style="{StaticResource PageScrollViewerStyle}">

            <StackPanel x:Name="ContentStackPanel">

                <TextBlock x:Name="HeaderText" Style="{StaticResource HeaderTextStyle}" 
                                   Text="Home"/>
                <TextBlock x:Name="ContentText" Style="{StaticResource ContentTextStyle}" 
                                   Text="Home page content"/>
                <sdk:DataGrid AutoGenerateColumns="True" 
                              Name="PeopleDataGrid"/>
                <Button x:Name="RefreshButton" Click="RefreshButton_Click">
                    Refreh
                </Button>
            </StackPanel>

        </ScrollViewer>
    </Grid>
</navigation:Page>

Code Behind:

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.Navigation;
using System.Windows.Shapes;

namespace UsingWCFServicesReloaded4
{
    public partial class Home : Page
    {
        LendingService.LendingServiceClient _proxy = new LendingService.LendingServiceClient();
        public Home()
        {
            InitializeComponent();
        }

        // Executes when the user navigates to this page.
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            _proxy.GetPeopleCompleted += new EventHandler<LendingService.GetPeopleCompletedEventArgs>(_proxy_GetPeopleCompleted);
        }

        void _proxy_GetPeopleCompleted(object sender, LendingService.GetPeopleCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                MessageBox.Show(e.Error.Message);
            }
            else
            {
                PeopleDataGrid.ItemsSource = e.Result;                
            }
        }

        private void RefreshButton_Click(object sender, RoutedEventArgs e)
        {
            _proxy.GetPeopleAsync();
        }
    }
}

Here’s a snapshot of what you should get when you hit refresh:

image

And just in case you want to try it with the simple database I’ve used here’s the DDL for it:


-- --------------------------------------------------
-- Entity Designer DDL Script for SQL Server 2005, 2008, and Azure
-- --------------------------------------------------
-- Date Created: 05/09/2011 16:03:32
-- Generated from EDMX file: ...Lends.edmx
-- --------------------------------------------------

SET QUOTED_IDENTIFIER OFF;
GO
USE [Lends];
GO
IF SCHEMA_ID(N'dbo') IS NULL EXECUTE(N'CREATE SCHEMA [dbo]');
GO

-- --------------------------------------------------
-- Dropping existing FOREIGN KEY constraints
-- --------------------------------------------------

IF OBJECT_ID(N'[dbo].[FK_Valuable_Person]', 'F') IS NOT NULL
    ALTER TABLE [dbo].[Valuables] DROP CONSTRAINT [FK_Valuable_Person];
GO

-- --------------------------------------------------
-- Dropping existing tables
-- --------------------------------------------------

IF OBJECT_ID(N'[dbo].[People]', 'U') IS NOT NULL
    DROP TABLE [dbo].[People];
GO
IF OBJECT_ID(N'[dbo].[Valuables]', 'U') IS NOT NULL
    DROP TABLE [dbo].[Valuables];
GO

-- --------------------------------------------------
-- Creating all tables
-- --------------------------------------------------

-- Creating table 'People'
CREATE TABLE [dbo].[People] (
    [ID] int IDENTITY(1,1) NOT NULL,
    [FirstName] nvarchar(50)  NOT NULL,
    [LastName] nvarchar(50)  NOT NULL
);
GO

-- Creating table 'Valuables'
CREATE TABLE [dbo].[Valuables] (
    [ID] int IDENTITY(1,1) NOT NULL,
    [Description] nvarchar(max)  NOT NULL,
    [HolderID] int  NULL
);
GO

-- --------------------------------------------------
-- Creating all PRIMARY KEY constraints
-- --------------------------------------------------

-- Creating primary key on [ID] in table 'People'
ALTER TABLE [dbo].[People]
ADD CONSTRAINT [PK_People]
    PRIMARY KEY CLUSTERED ([ID] ASC);
GO

-- Creating primary key on [ID] in table 'Valuables'
ALTER TABLE [dbo].[Valuables]
ADD CONSTRAINT [PK_Valuables]
    PRIMARY KEY CLUSTERED ([ID] ASC);
GO

-- --------------------------------------------------
-- Creating all FOREIGN KEY constraints
-- --------------------------------------------------

-- Creating foreign key on [HolderID] in table 'Valuables'
ALTER TABLE [dbo].[Valuables]
ADD CONSTRAINT [FK_Valuable_Person]
    FOREIGN KEY ([HolderID])
    REFERENCES [dbo].[People]
        ([ID])
    ON DELETE NO ACTION ON UPDATE NO ACTION;

-- Creating non-clustered index for FOREIGN KEY 'FK_Valuable_Person'
CREATE INDEX [IX_FK_Valuable_Person]
ON [dbo].[Valuables]
    ([HolderID]);
GO

-- --------------------------------------------------
-- Script has ended
-- --------------------------------------------------

6 Comments on “Silverlight Enabled WCF Services and Entity Framework 4 in a Silverlight Navigation Application”

  1. jibin mathew says:

    Hi, can you please suggest me some links for starting the concepts

    “using silverlight enabled wcf services and entity framework models”. I need a little more description on these steps.

    Thanks
    Jibin

  2. In retrospect, the part of not disposing of the objectcontext is probably not a good idea, the way to go might be to detach the entities from the object context and dispose of that object context when you’re done.
    I say this because if you end up having the same object context on different threads you’ll get a MappingException.

  3. Peddiraju says:

    Your service Might be wrong i am getting errors like person,SilverlightFaultBehavior


Leave a comment