Consuming WCF Services from Silverlight 4

There are several ways to perform Data Access with Silverlight 4. One of the best ways is to use a WCF Service. One of the biggest advantages of using WCF services is that the Business Objects [Data Contracts] can be mapped exactly with your Silverlight Applications’ View-Model. In this tutorial I will start with a Silverlight form and then create a service. In real-life senario, you’ll probably create a form based on a service. But for now, lets first start with the Silverlight 4 Form.

Create a Silverlight Form – First open Visual Studio 2010 and start a new Silverlight application in a new project. (Life’s much easier if you use some good naming convention. I’ll name my application SilverlightWcf). You will be asked if you want to create a web project along with this. I’d agree to that and let it create a SilverlightWcf.Web project. In the SilverlightWcf project, create a form that’s something like the one shown below. Just copy and paste the XAML in your MainPage.xaml.

<Grid x:Name="LayoutRoot" Background="White" Width="400" Height="300">
    <Border Margin="20" BorderBrush="#B8343434" CornerRadius="10" BorderThickness="1">
        <Border.Background>
            <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                <GradientStop Color="White" Offset="0" />
                <GradientStop Color="#FFDEDEDE" Offset="1" />
            </LinearGradientBrush>
        </Border.Background>
        <Grid Margin="20">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="3*" />
                <ColumnDefinition Width="5*" />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="25" />
                <RowDefinition Height="25" />
                <RowDefinition Height="25" />
                <RowDefinition Height="25" />
                <RowDefinition Height="25" />
                <RowDefinition Height="25" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <TextBlock Grid.Row="1" Grid.RowSpan="1" HorizontalAlignment="Left" Margin="0" Name="textBlock1" Text="Name" VerticalAlignment="Center" Width="120" />
            <TextBlock Grid.Row="2" Grid.RowSpan="1" HorizontalAlignment="Left" Margin="0" Name="textBlock2" Text="Location" VerticalAlignment="Center" Width="120" />
            <TextBlock Grid.Row="3" Grid.RowSpan="1" HorizontalAlignment="Left" Margin="0" Name="textBlock3" Text="Group" VerticalAlignment="Center" Width="120" />

            <TextBox x:Name="TxtName" Grid.Column="1" Grid.Row="1" Grid.RowSpan="1" HorizontalAlignment="Left" VerticalAlignment="Center" Width="120" />
            <ComboBox x:Name="CmbLocation" Grid.Column="1" Grid.Row="2" Grid.RowSpan="1" Height="23" HorizontalAlignment="Left" VerticalAlignment="Center" Width="120" >
                <ComboBox.Items>
                    <ComboBoxItem Content="Gondor" />
                    <ComboBoxItem Content="Rohan" />
                    <ComboBoxItem Content="Mordor" />
                    <ComboBoxItem Content="The Shire" />
                </ComboBox.Items>
            </ComboBox>
            <StackPanel Orientation="Horizontal" Grid.Column="1" Grid.Row="3" >
                <RadioButton x:Name="RdbMen" Content="Man" HorizontalAlignment="Left" VerticalAlignment="Center" GroupName="grpGroupType" Margin="0,0,5,0" />
                <RadioButton x:Name="RdbElf" Content="Elf" HorizontalAlignment="Left" VerticalAlignment="Center" GroupName="grpGroupType" Margin="0,0,5,0" />
                <RadioButton x:Name="RdbHobbit" Content="Hobbit" HorizontalAlignment="Left" VerticalAlignment="Center" GroupName="grpGroupType" Margin="0,0,5,0" />
            </StackPanel>

            <CheckBox x:Name="ChkOwnRing" Content="I own a ring of power" Grid.Column="1" Grid.Row="4" Height="16" HorizontalAlignment="Left" VerticalAlignment="Center" VerticalContentAlignment="Top" />
            <Button x:Name="BtnSubmit" Content="Submit" Grid.Column="1" Grid.Row="5" Grid.RowSpan="1" Height="23" HorizontalAlignment="Left" VerticalAlignment="Center" Width="75"
                Click="BtnSubmit_Click" />

            <TextBlock x:Name="TxtMessage" Text="[Message]" Grid.Row="7" FontWeight="Bold" Grid.ColumnSpan="2" HorizontalAlignment="Left" Width="318" TextWrapping="Wrap"></TextBlock>
        </Grid>
    </Border>
</Grid>

This is how the form is going to work. We will provide the values that are required and our service returns a message that we’ll display at the bottom. But remember, we’ll be transferring specific business objects through our service. Based on the form above, I hope you get an idea what kind of business objects are required. We’ll be creating those objects in the next step. At this point, you can run the application once to make sure things don’t break. We will be adding code in the code-behind (MainPage.xaml.cs) later.

Create the WCF Service – Now lets go to the web project and add a new item. Right click on your Web application, Select Select a “Silverlight enabled WCF application” (I named the service LotrService.svc). Once you add this item, you will find a ServiceContract (the class or sometimes an interface) stub generated for you. We’ll be adding our own DataContract (additional classes) and we’ll create an OperationContract (functions) to serve the data. At this point you should know what’s 3-tier architecture. Specifically, you should know what’s a Business Object. In our application we will create a class that represents the Business Object and decorate it with the [DataContract] tag and that will be our data contract. We’ll also decorate each of our properties that need to be exposed with the [DataMember] tag. Read more about data contracts on MSDN @ http://msdn.microsoft.com/en-us/library/ms733127.aspx

Why do we need to add this Decoration? – We need to do this to let the application know that this Business Object is serializable.

Why do we need to serialize this Object? – Because that’s the only way we can send data from one system to another. (Web Services… Duh!)

Now the code below shows contains the code that goes in our Service (LotrService.svc.cs). Quite simple actually. Just pay attention to the ServiceContract, OperationContract, DataContract and DataMembers. (Without these, you’ll not be able to run the application successfully).

[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class LotrService
{
    [OperationContract]
    public void DoWork()
    {
        // Add your operation implementation here
        return;
    }

    /// <summary>
    /// This service rewrites the LotrCharacter Object's name and returns the updated object
    /// </summary>
    /// <param name="lotrChar">An instance of the LotrCharacter</param>
    /// <returns>LotrCharacter</returns>
    [OperationContract]
    public LotrCharacter GetModifiedCharacter(LotrCharacter lotrChar)
    {
        try
        {
            lotrChar.Message = String.Format("Welcome, {0} - the {1} of {2}. ", lotrChar.Name, lotrChar.Group, lotrChar.Location);
            if (lotrChar.OwnsRing)
                lotrChar.Message += "Please go drop the ring off at Mordor. You can use my Metro Card!";
            else
                lotrChar.Message += "No Ring? Then lets be Merry with some Pipe Weed!";

            return lotrChar;
        }
        catch (Exception)
        {
            return null;
        }
    }
}

[DataContract]
public class LotrCharacter
{
    [DataMember]
    public string Name { get; set; }

    [DataMember]
    public string Location { get; set; }

    [DataMember]
    public string Group { get; set; }

    [DataMember]
    public bool OwnsRing { get; set; }

    [DataMember]
    public string Message { get; set; }
}

Add the WCF Service Reference– Now its time to consume this service. Go to your Silverlight project (In my case SilverlightWcf) and right click on the ‘References‘ system folder. Select ‘Add Service Reference‘. This opens up a new form. Here click the ‘Discover’ button and you’ll find the services in your solution. Expand them, look at what’s inside. It’ll show the list of services available. Look around, play around and before you add the reference, give it a proper name (I named mine LotrServiceReference).

Sometimes you might get this error: The type ‘SilverlightWCF.Web.LotrService’, provided as the Service attribute value in the ServiceHost directive could not be found.

Or sometimes the service reference may not list the available services properly. This usually occurs when your program has not been built due to which your assemblies have not been loade. One simple way to fix this is to run your Web application that contains your WCF service by setting an ASPX page as the startup page. Running fine? Now just type in the URL of your service in the browser (doesn’t matter if you’re running it in VS {Cassini} or IIS. In my case the URL is http://localhost:53768/LotrService.svc). Check if the url opens up and the page looks like the screenshot below. If it does, you’re fine. Just go ahead and add the Service Reference in your Silverlight project again (or update the reference if it was already added).

Consume the Service – Now that we have the Service reference added successfully, lets add some code to the Silverlight Project. We’ll go back to MainPage.xaml.cs and here we’ll add a using statement to reference the classes in our Service Reference. Each service reference is wrapped in its own namespace whose name is same as the one you provided while adding (in my case is LotrServiceReference).

using SilverlightWCF.LotrServiceReference;

On the button click event handler, we’ll create a new instance of LotrCharacter and assign the values from the form. LotrCharacter is now available as we have added a service reference and the data contracts are also referenced. We will then pass the LotrCharacter object to the service, which will make certain modifications to the object, and then we’ll asynchronously fetch the modified LotrCharacter. The code for that is shown below. If you have any doubts on this, leave a comment below. (Also I’ll add a new post on binding the LotrCharacter object to the from. Statements like myCharacter.Name = TxtName.Text; are frowned upon. Check back soon.)

public partial class MainPage : UserControl
{
    public MainPage()
    {
        InitializeComponent();
    }

    private void BtnSubmit_Click(object sender, RoutedEventArgs e)
    {
        // Store values from the form into our business object
        LotrCharacter myCharacter = new LotrCharacter();
        myCharacter.Name = TxtName.Text;
        myCharacter.Location = Convert.ToString(((ComboBoxItem)CmbLocation.SelectedItem).Content);

        if (RdbElf.IsChecked == true) myCharacter.Group = Convert.ToString(RdbElf.Content);
        else if (RdbHobbit.IsChecked == true) myCharacter.Group = Convert.ToString(RdbHobbit.Content);
        else if (RdbMen.IsChecked == true) myCharacter.Group = Convert.ToString(RdbMen.Content);

        myCharacter.OwnsRing = (ChkOwnRing.IsChecked == true);

        // This is the way we use the Client Object to call a function (Service) asynchronously
        // 1. Create a Client instance
        LotrServiceClient lotrSvc = new LotrServiceClient();
        // 2. Assign a ServiceCompleted Event Handler (As its Asynchronous)
        lotrSvc.GetModifiedCharacterCompleted +=
            new EventHandler<GetModifiedCharacterCompletedEventArgs>(LotrSvc_GetModifiedCharacterCompleted);
        // 3. Call the Service (Asynchronously)
        lotrSvc.GetModifiedCharacterAsync(myCharacter);
    }

    protected void LotrSvc_GetModifiedCharacterCompleted(object senderm, GetModifiedCharacterCompletedEventArgs e)
    {
        // Its a good practice to check for errors after receiving a response
        if (e.Error != null)
        {
            TxtMessage.Text = e.Error.Message;
            TxtMessage.Foreground = new SolidColorBrush(Colors.Red);
            return;
        }

        // e.Result will always have the return value of the service (In case of Async functions)
        LotrCharacter c = e.Result as LotrCharacter;
        TxtMessage.Text = c.Message;
    }
}

And that’s it! Run the application. The above code sample is with Silverlight 4 and Vs 2010. This code should be pretty much the same with Silverlight 3 and vs 2008. Let me know if you are having any trouble running this.

A little bit on asynchronous events – While you’re using WCF services, you’ll be creating event handlers all the time. In .net all event handlers must have 2 arguments to be valid. 1 – the object that’s sending the event and 2 – the event argument. When you add a service reference, for each service, VS creates 3 objects that you’ll be using –

  1. The Async Function (GetModifiedCharacterAsync in this case)
  2. The Event Delegate (GetModifiedCharacterCompleted in this case)
  3. The Event Completed Argument (GetModifiedCharacterCompletedEventArgs in this case)

Using these three you’ll be creating an event handler and assigning this handler to the event delegate using the += operator. To use async functions you should know “What should be done once you get the data”. Identify these steps and put those in the event handler. These statements are executed automatically (asynchronously) when ‘CompletedEvent’ occurs. For people coming from a background of normal ADO.NET like data retrieval, this is where you’ll find a big difference.

One Reply to “Consuming WCF Services from Silverlight 4”

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.