How to consume a WCF Service in Silverlight?

SheoNarayan
Posted by in Silverlight category on for Intermediate level | Views : 41590 red flag
Rating: 1 out of 5  
 1 vote(s)

This article explains how to create WCF Service and consume WCF Services in Silverlight. To explain this, I have taken a simple example in which I will use Northwind database.

First create a Silverlight application that should have your test .aspx page (Please go through http://www.dotnetfunda.com/articles/article217.aspx to see some basics of how to get started with Silverlight)

Create a Class Library

Create a class library project named DataAccessLayar and add a class named Customer.cs with following public properties.

namespace DataAccessLayer

{

    [DataContract]

    public class Customer

    {

        [DataMember]

        public string ContactName { get; set; }

        [DataMember]

        public string CustomerID { get; set; }

        [DataMember]

        public string Address { get; set; }

        [DataMember]

        public string City { get; set; }

        [DataMember]

        public string Hiredate { get; set; }

        [DataMember]

        public string Country { get; set; }

    }

}

In the above code snippet, you can notice that Customer class definition has an attribute named DataContract and properties have DataMember. These attributes are necessary to work with Wcf Services. If we will not keep them, WCF service will not be able to transfer the object and its properties. In order to add these attribute you may need to add reference of System.Runtime.Serialization (Right click and Add Reference … from .NET Tab)  to your class library.

Add another class called DataAccessLayer.cs in this class library that has a method called GetCustomers. This method is responsible for connecting to the database and returning the results as Generic lists. Here is the code for this.

NOTE: To avoid complications in this tutorial, I have created Customer class and DataAccessLayer class in the same class library project. In real scenario, you should separate them out.

string NorthwindConnStr = ConfigurationSettings.AppSettings.Get("NorthwindConnStr").ToString();

 

        /// <summary>

        /// Get customers

        /// </summary>

        /// <returns></returns>

        public List<Customer> GetCustomers(string startsWith)

        {

            List<Customer> list = new List<Customer>();

 

            using (SqlConnection conn = new SqlConnection(NorthwindConnStr))

            {

                conn.Open();

                using (SqlCommand dCmd = new SqlCommand("vs_GetCustomersStartingWith", conn))

                {

                    SqlParameter prms = new SqlParameter("@startsWith", SqlDbType.VarChar, 5);

                    prms.Value = startsWith + "%";

                    dCmd.Parameters.Add(prms);

                    dCmd.CommandType = CommandType.StoredProcedure;

                    using (SqlDataReader reader = dCmd.ExecuteReader())

                    {

                        while (reader.Read())

                        {

                            list.Add(new Customer

                            {

                                CustomerID = reader["CustomerID"].ToString(),

                                Address = reader["Address"].ToString(),

                                City = reader["City"].ToString(),

                                Country = reader["Country"].ToString(),

                                ContactName = reader["ContactName"].ToString()

                            });

                        }

                    }

                }

                conn.Close(); // lets close explicitely

            }

            return list;

        }

In the above method, I have created a generic type variable that will accept Customer object, I have a stored procedure named vs_GetCustomersStartsWith that will take a string as a parameter and return all records whose name starts with parameter value.

Finally I am forming the Customer object inside while (reader.Read()) loop and adding it into the generic list collection.

Get solutions of the .NET problems with video explanations, .pdf and source code in .NET How to's.

Create a WCF Service

Let’s create the WCF Service now. Right click the solution and click Add > New Proejct … Select Visual C# > Web from the Project Type and WCF Service Application from Templates, name it as WcfService. This will by default create following files:

  1. IService1.cs
  2. Service1.svc
  3. Service1.svc.cs
  4. Web.config

Now, let’s Add Reference to our Class Library project into our WCFService, right click the WcfService project and click Add Reference … Select Projects tab and select the DataAccessLayer project.

In the IService1 interface remove all code and add a method definition named GetCustomers. Notice that the interface declaration must have an attribute ServiceContract and its method declaration must have an attribute OperationContract otherwise this class and its member will not be exposed as services. My sample IService1 interface code looks like this.

using System;

using System.Collections.Generic;

using System.Runtime.Serialization;

using System.ServiceModel;

using System.Text;

using System.Collections.Generic;

using DataAccessLayer;

 

namespace WcfService

{

    [ServiceContract]

    public interface IService1

    {

 

        [OperationContract]

        List<Customer> GetCustomers(string startsWith);

    }

}

Now let’s implement the above method in Service1.svc.cs file. If your project names are same as mine, your code for this file looks like below.

using System;

using System.Collections.Generic;

using System.Runtime.Serialization;

using System.ServiceModel;

using System.Text;

using DataAccessLayer;

 

namespace WcfService

{

    public class Service1 : IService1

    {

        public List<Customer> GetCustomers(string startsWith)

        {

            DataAccessLayer.DataAccessLayer dal = new DataAccessLayer.DataAccessLayer();

            return dal.GetCustomers(startsWith);

        }

    }

}

Now we need a little configuration change in web.config file. So open the config file of WcfService project and change following.
In the system.serviceModel tag

  1. For endpoint – change the value of binding to basicHttpBinding.
  2. For identity > dns change the value to “localhost:3637” //as we are going to fix the port in the project properties later on

Following is the code snippet related with endpoint and dns of my web.config file

<!-- Service Endpoints -->

<endpoint address="" binding="basicHttpBinding" contract="WcfService.IService1">

<!--

Upon deployment, the following identity element should be removed or replaced to reflect the

identity under which the deployed service runs. If removed, WCF will infer an appropriate identity

automatically.

-->

<identity>

<dns value="localhost:3637"/>

</identity>

</endpoint>

<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>

Now right click the project, click Properties and go to Web tab (left side) and select Specific port and enter 3637 in the textbox and save it. This is to avoid automatically generation of random ports while we are working on our sample application.

As Silverlight doesn’t support cross domain access by default so we need create a ClientAccessPolicy.xml file that is read when any request is sent to the server to access wcf service.  In order to work for cross domain, write following code in this file

<?xml version="1.0" encoding="utf-8"?>

<access-policy>

  <cross-domain-access>

    <policy>

      <allow-from http-request-headers="*">

        <domain uri="*"/>

      </allow-from>

      <grant-to>

        <resource path="/" include-subpaths="true"/>

      </grant-to>

    </policy>

  </cross-domain-access>

</access-policy>

This ensures that your wcf service will be consumed by Silverlight application even if both do not fall in the same domain domain.

Save this project and build it.

Now, right click the WcfService project and go to Debug and click start new Instance. This will open up a browser with http://localhost:3637/Service1.svc address. Click Stop button from Visual Studio and return to normal mode, you will notice that your localhost:3637 port is still active and is able to listen the request (See bottom-right of your screen and you will see that ASP.NET Development Server – Port 3637 is still active).  Keep it as it is.

Consume WCF Service in Silverlight

Right click the Silverlight application and click Add Service Reference... . Write the url of the wcf service you got above in the address box, click Go button. In the Namespace box, write WcfServiceReference and click OK. This should add a reference of the wcf service and also create ServiceReferences.ClientConfig file in the root of your Silverlight application.

Double click the xaml file and drag a TextBlock, TextBox, Button and DataGrid to your xaml file and ensure that they are looking similar to the below code snippet. This will ensure that your Search form is looking good.

<Grid x:Name="LayoutRoot" Background="White">

        <Canvas>

            <TextBlock Text="Search" Canvas.Left="10" Canvas.Top="12"></TextBlock>

            <TextBox x:Name="txtSearch" Width="75" Height="22" Canvas.Left="55" Canvas.Top="10"></TextBox>

            <Button Content="  Get Data from WCF Service  " Click="Button_Click" Canvas.Top="9" Canvas.Left="140"></Button>

            <TextBlock x:Name="lblWaiting" Canvas.Left="375" Canvas.Top="10" Text="Status: Idle"></TextBlock>

            <data:DataGrid x:Name="dataGrid1" Canvas.Left="10" Canvas.Top="35" Width="550" Height="300" IsReadOnly="True"

                           ItemsSource="{Binding Mode=OneWay}" AutoGenerateColumns="True" >

            </data:DataGrid>

        </Canvas>

    </Grid>

As you can see that we have specified Button_Click event handler that will fire when user clicks button after entering keyword in the textbox. Let us see the code for this event.

private void Button_Click(object sender, RoutedEventArgs e)

        {

            // call data web service

            var proxy = new WcfServiceReference.Service1Client("BasicHttpBinding_IService1");

            proxy.GetCustomersCompleted += new EventHandler<SilverlightApplication.WcfServiceReference.GetCustomersCompletedEventArgs>(proxy_GetCustomerCompleted);

            proxy.GetCustomersAsync(txtSearch.Text.Trim());

            // call simple web service

            lblWaiting.Text = "Status: Waiting ...";

        }

 

//-------------------------------------------------

 

        void proxy_GetCustomerCompleted(object sender, WcfServiceReference.GetCustomersCompletedEventArgs e)

        {

            System.Collections.ObjectModel.ObservableCollection<WcfServiceReference.Customer> list = e.Result;

 

            dataGrid1.ItemsSource = list;

            lblWaiting.Text = "Status: Done";

            if (list.Count.Equals(0))

                lblWaiting.Text = "No records found.";

            else

                lblWaiting.Text = list.Count + " records found.";

        }

You can see that in the Button_Click event I have specified a variable named proxy and instantiating it by passing the endpointConfigurationName as parameter. endpointConfigurationName is nothing but the bindingConfiguration value of the ServiceReferences.ClientConfig file.

Later on I have specified the event handler for GetCustomersCompleted and specified the parameter for GetCustomerAsync method as the keyword (textbox value).

When the GetCustomersCompleted will finish, it will raise an event named proxy_GetCustomerCompleted. In this event, write the code like above. Build your Silverlight project and ensure that it is built successfully.

Now, try to run your web application that is hosting Silverlight, you should see your browser something like above image. Try to enter some keyword and hit the button, you should expect the search results coming through Wcf service you just created and displaying in the DataGrid.

Hope this article was useful. Thanks for reading, please let me know if you have any question or suggestions.

Page copy protected against web site content infringement by Copyscape

About the Author

SheoNarayan
Full Name: Sheo Narayan
Member Level: HonoraryPlatinum
Member Status: Administrator
Member Since: 7/8/2008 6:32:14 PM
Country: India
Regards, Sheo Narayan http://www.dotnetfunda.com

Ex-Microsoft MVP, Author, Writer, Mentor & architecting applications since year 2001. Connect me on http://www.facebook.com/sheo.narayan | https://twitter.com/sheonarayan | http://www.linkedin.com/in/sheonarayan

Login to vote for this post.

Comments or Responses

Posted by: Cfaust on: 10/13/2009
Hello Sheo,

thanks for this article, it was quite helpful for me.
I come from traditional client server desktop architectures respectively java middleware stuff and some experience with asp.net....started now with silverlight and was looking for a way how to call a storedprocedure from a sl client...that was tougher than I expected ...tried first with ado.net service operations but those I didn't get called from SP client...but your proposal worked fine....thanks once more!

regards
Christian
Posted by: SheoNarayan on: 10/13/2009
Thank you Christian.

I am glad this article helped you.

Regards,
Sheo Narayan
Posted by: Rajesh.Gour on: 9/2/2010 | Points: 10
Hello Sheo,

This is first when a made a silver light application in which i want to use WCF.Your article help me a lot , i would appreciate your simple article that explain me a lot.



Posted by: Rukhiyas on: 12/14/2010 | Points: 25
Hi Sheo

I am learning silver light for the last 5 days. Actually my requirement is I need to build a warehouse report which can consists of lakh of records. Also I need to support multi level grouping and sub total for each grouping.

I would like to know whether SL is the proper technology for doing the same.

What I have found is SL do not provide report viewer control. Can I achieve this report using SL data grid?


Please reply at the earliest.


Thanks
Rukhiya

Login to post response

Comment using Facebook(Author doesn't get notification)