Friday, August 13, 2010

How to consume WCF service with tcpTransport in Silverlight4

Last month, I wrote a new Silverlight4 sample for All-in-One Code Framework project. The sample called CSSL4WCFNetTcp, which demonstared how to utilize the Silverlight4 new supported netTcpTransport to create Silverlight client and consume WCF service.
For convenience, I post the sample's readme file here, someone who have interest on this sample could download 1Code latest release.

Creation
To demonstrate silverlight accessing WCF, we may need a WCF service and a Silverlight WCF client. Here we separate the creation progress into three tasks:
  1. Creating Duplex WCF service with netTcpBinding
  2. Creating Silverlight WCF client
  3. Deploying cross domain policy file
Creating Duplex WCF service with netTcpBinding
1. Create a new console project "NetTcpWCFService", add a new WCF Service to project, named WeatherService.
2. Open IWeatherService.cs, define the service contract like this:
namespace NetTcpWCFService
{
    [ServiceContract(CallbackContract=typeof(IWeatherServiceCallback))]
    public interface IWeatherService
    {
        [OperationContract(IsOneWay = true)]
        void Subscribe();
            [OperationContract(IsOneWay = true)]
        void UnSubscribe();
    }
    public interface IWeatherServiceCallback
    {
        [OperationContract(IsOneWay=true)]
        void WeatherReport(string report);
    }
}
3. Open WeatherService.cs. We use static event approach to implement subscription service.
First, prefix the ServiceBehavior to the existing WeatherService class, set InstanceContext mode to PerSession. Then, replace the default contents of the WeatherService class with the following:
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
    public class WeatherService : IWeatherService
    {
        static event EventHandler WeatherReporting;
        IWeatherServiceCallback _callback;
        public void Subscribe()
        {
            _callback = OperationContext.Current.GetCallbackChannel();
            WeatherReporting += new EventHandler(WeatherService_WeatherReporting);
        }
        public void UnSubscribe()
        {
            WeatherReporting -= new EventHandler(WeatherService_WeatherReporting);
        }
        void WeatherService_WeatherReporting(object sender, WeatherEventArgs e)
        {
            // Remember check the callback channel's status before using it.
            if (((ICommunicationObject)_callback).State == CommunicationState.Opened)
                _callback.WeatherReport(e.WeatherReport);
            else
                UnSubscribe();
        }
    }
 
    class WeatherEventArgs:EventArgs
    {
        public string WeatherReport{set;get;}
    }

4. Create a separate thread to generate fake weather report periodically.
static WeatherService()
        {
            ThreadPool.QueueUserWorkItem(
                new WaitCallback(delegate
                {
                    string[] weatherArray = { "Sunny", "Windy", "Snow", "Rainy" };
                    Random rand = new Random();
 
                    while (true)
                    {
                        Thread.Sleep(1000);
                        if (WeatherReporting != null)
                            WeatherReporting(
                                null,
                                new WeatherEventArgs
                                {
                                    WeatherReport = weatherArray[rand.Next(weatherArray.Length)]
                                });
                    }
                }));
        }

5. Configure the app.config file to add netTcpbinding endpoint to WCF service.

Note that, only a few ports are allowed to be accessed by Silverlight, that is 4502-4534, and we need client access policy file to permit Silverlight access, please refer to Deploying cross domain policy file
6. Start the ServiceHost in Main method
static void Main(string[] args)
        {
            using (var host = new ServiceHost(typeof(WeatherService)))
            {
                host.Open();
                Console.WriteLine("Service is running...");
                Console.WriteLine("Service address: "+host.BaseAddresses[0]);
                Console.Read();
            }
        }

Creating Silverlight WCF client
Now, create Silverlight application to consume the WCF. We need one button to subscribe/unsubscribe servicie, and one listbox to display the weather report from service.
1. Create a new Silverlight project.
2. Open MainPage.xaml, Add Button, TextBlock and ListBox to MainPage and adjust layout.

3. Add Service Reference to our “weatherService” WCF service. To do this: First, set NetTcpWCFService project as startup project, press Ctrl+F5 to start the service. Then, right click Silverlight project, select “Add Service Reference”, in dialog, input the weather service address, and press OK. After done this, VS would generate wcf proxy code in Silverlight project.
4. Open MainPage.cs file, Initialize the WCF proxy in Loaded event
public partial class MainPage : UserControl,IWeatherServiceCallback
    {
        public MainPage()
        {
            InitializeComponent();
            Loaded += new RoutedEventHandler(MainPage_Loaded);
        }
 
        bool _subscribed;
        WeatherServiceClient _client;
        void MainPage_Loaded(object sender, RoutedEventArgs e)
        {
            _client = new WeatherServiceClient(
                new System.ServiceModel.InstanceContext(this));
            _client.SubscribeCompleted += _client_SubscribeCompleted;
            _client.UnSubscribeCompleted += _client_UnSubscribeCompleted;
        }
 
        void _client_UnSubscribeCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                _subscribed = false;
                btnSubscribe.Content = "Subscribe weather report";
                tbInfo.Text = "";
            }else
                tbInfo.Text = "Unable to connect to service.";
            btnSubscribe.IsEnabled = true;
        }
 
        void _client_SubscribeCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                _subscribed = true;
                btnSubscribe.Content = "UnSubscribe weather report";
                tbInfo.Text = "";
            }
            else
                tbInfo.Text="Unable to connect to service.";
            btnSubscribe.IsEnabled = true;
        }
 
        // Display report when callback channel get called.
        public void WeatherReport(string report)
        {
            lbWeather.Items.Insert(
                0,
                new ListBoxItem
                {
                    Content = string.Format("{0} {1}",DateTime.Now, report)
                });
        }
    }
5. Add event handler to handle button click event
private void Button_Click(object sender, RoutedEventArgs e)
        {
            if (!_subscribed)
            {
                _client.SubscribeAsync();
            }
            else
            {
                _client.UnSubscribeAsync();
            }
            btnSubscribe.IsEnabled = false;
        }
Now, the Silverlight WCF Client is completed. We have one last task to permit the Silverlight access.

Deploying cross domain policy file
1. Create a xml file, named "clientaccesspolicy.xml", set content as below
This file grant permissions to allow Silverlight clients from any domian to access server's 4502-4506 port.

2. Find out the server web site's root physical path (by default, C:\inetpub\wwwroot), place the policy file in that folder. To varify the deployment, browse http://localhost/clientaccesspolicy.xml, check if you could see the policy xml content.

Demo
To test the project
1. Open CSSL4WCFNetTcp solution, build.
2. Start the duplex WCF. Run NetTcpWCFService.exe, it's under CSSL4WCFNetTcp\NetTcpWCFService\bin\debug folder.
3. Right click "CSSL4WCFNetTcpTestPage.aspx" in VS, Select "View in Browser".
4. When Silverlight application loaded, click "subscribe" button, if all code and configuration is correct, you may find the listbox displaying a new record in each second.

6 comments:

Joseph said...

I get unable to access service. At first it was prompting for integrated credentials so I turned on anonymous on my wwwroot in IIS but not it says same thing but doesn't prompt for login.

Joseph said...

Figured it out. I had to run aspnet_regiis -i again under the v4.0 framework directory.

Unknown said...

Thank you for this great example. I have been searching for days for a working example, and this one works right out-of-box! thank you

Anonymous said...

After trying this I get the following error upon trying to add the configured service to the client:
"This operation is not supported for a relative URI"

Unknown said...

Perfect, I have been looking for a complete working example for the last few hours.

Arshdeep Singh Virdi said...

Check this for details and a working sample (silvelight 5):

http://arshdeep-virdi.blogspot.ca/2012/07/silverlight-5-and-wcf-nettcp-binding.html

http://forums.silverlight.net/p/254512.aspx/1?Need+help+with+Silverlight+5+and+WCF+net+tcp+binding

Post a Comment