Click here to Skip to main content
15,886,776 members
Articles / Desktop Programming / XAML

Silverlight Session Management, Error handling, and Unit Test of WCF Service

Rate me:
Please Sign up or sign in to vote.
4.25/5 (3 votes)
15 Sep 2010CPOL3 min read 35K   12   8
Silverlight Session Management, Error handling, and Unit Test of WCF service

Requirements

I had the following requirements:

  • Session management.
    In the second web service call, I wanted to know something from the first call.

  • Impersonate user
    I didn't want to enter my credentials if my service had to work as my user.

  • Error handling
    It’s not very cool to get a white website after an error occurs.

  • Unit tests of the WCF service
    It would be great to have tests for the stuff above.

Let's start. I'm going to implement each of these topics separately, and hopefully get a solution working before this blog entry is finished. You can download the attached ZIP file which contains the solutions for the above requirements.

Introduction

I just started with a new Silverlight solution in Visual Studio 2008. The only thing I changed from the original is to assign a static port to the built in web server. This makes the life with WCF much easier. Then add a new Silverlight-enabled WCF Service to the web project, create some operation contract, and add a service reference to the Silverlight project.

Using the Code

1. Session management (with aspNetCompatibilityEnabled)

For this, there are two possibilities:

  1. Do it yourself (write something like a session agent service)
  2. Use the ASP session management

I guess there are lots of reasons not to use aspNetCompatibility (The most obvious might be performance), but the simplicity of it was the reason for me to choose it anyway. I have to say that I did not invent this, I just copied it from an article of SpoonStomper from the Silverlight forum.

I want to achieve the following goal. I press a button on my Silverlight application which sends a string to my service. Then I want to press another button and get back this string again (sounds really easy btw.).

To enable the ASP session management, you first have to edit the web.config.

ASP.NET
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
//this should be default since Silverlight 3   

On your service class, you have to change the RequirementsMode from Allowed to Required:

C#
[AspNetCompatibilityRequirements(RequirementsMode = 
	AspNetCompatibilityRequirementsMode.Required)] 
public class MyCoolService{ 

With this modification, you're ready to go. Now you can use the HttpContext.Current.Session for handling session stuff.

C#
[OperationContract]
public void ServiceCallOne(string message)
{
    HttpContext.Current.Session["Test"] = message;
}

[OperationContract]
public string ServiceCallTwo()
{
    return (string)HttpContext.Current.Session["Test"];
} 

2. Impersonate User

The user impersonation is built on top of the previous topic. Since we are ASP.NET controlled, we can use the HttpContext to impersonate. Here you can find the description from Microsoft:

C#
[OperationContract]
public string WhoAmIAfterImpersonate()
{
  ((WindowsIdentity)HttpContext.Current.User.Identity).Impersonate();
  return HttpContext.Current.User.Identity.Name;
}    

After the Impersonate, the service stuff will be executed as the user who has called it.
BTW, as far as I understand, this requires NTLM authentication.

3. Error Handling

Since it is not possible to catch a regular exception from a WCF service in a Silverlight application, there is another way to do it.

I just explain it in a short story, and give the example. You may find the whole story from Microsoft here.

First: Create a FaultBehavior class in your web project

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.Web;

namespace SilverlightWCF.Web
{
    public class MyFaultBehavior : BehaviorExtensionElement, IEndpointBehavior
    {
        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, 
		EndpointDispatcher endpointDispatcher)
        {
            SilverlightFaultMessageInspector inspector = 
				new SilverlightFaultMessageInspector();
            endpointDispatcher.DispatchRuntime.MessageInspectors.Add(inspector);
        }
        public class SilverlightFaultMessageInspector : IDispatchMessageInspector
        {
            public void BeforeSendReply(ref Message reply, object correlationState)
            {
                if (reply.IsFault)
                {
                    HttpResponseMessageProperty property = 
				new HttpResponseMessageProperty();

                    // Here the response code is changed to 200.
                    property.StatusCode = System.Net.HttpStatusCode.OK;
                    reply.Properties[HttpResponseMessageProperty.Name] = property;
                }
            }
            public object AfterReceiveRequest(ref Message request, 
		IClientChannel channel, InstanceContext instanceContext)
            {
                // Do nothing to the incoming message.
                return null;
            }
        }
        // The following methods are stubs and not relevant.
        public void AddBindingParameters(ServiceEndpoint endpoint, 
			BindingParameterCollection bindingParameters)
        {
        }
        public void ApplyClientBehavior(ServiceEndpoint endpoint, 
					ClientRuntime clientRuntime)
        {
        }
        public void Validate(ServiceEndpoint endpoint)
        {
        }
        public override System.Type BehaviorType
        {
            get { return typeof(MyFaultBehavior); }
        }
        protected override object CreateBehavior()
        {
            return new MyFaultBehavior();
        }
    }
} 

Second: Edit the web.config

XML
<system.serviceModel>
  <!--Add a behavior extension within the service model-->
  <extensions>
    <behaviorExtensions>
      <add name="myFault" type="SilverlightWCF.Web.MyFaultBehavior, 
	SilverlightWCF.Web, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
    </behaviorExtensions>
  </extensions>

...

<behaviors>
  <!--Add a endpointBehavior below the behaviors-->
    <endpointBehaviors>
      <behavior name="myFaultBehavior">
        <myFault/>
      </behavior>
    </endpointBehaviors>

...

<!--Set the behaviorConfiguration of the endpoint-->
<endpoint address="" binding="customBinding" bindingConfiguration="customBinding0"
contract="SilverlightWCF.Web.MyCoolService" behaviorConfiguration="myFaultBehavior"  />

...

  <!--For debugging, it might be cool to have some more error information.
  to get this, set includeExceptionDetailInFaults to true-->
<serviceDebug includeExceptionDetailInFaults="true" /> 

Third: Create an operation contract which throws an exception.

Make sure not to throw a regular exception, but FaultException:

C#
[OperationContract]
public void ThrowException()
{
    throw new FaultException("this is my Exception");
}  

Fourth: Handle the error within your Silverlight application

C#
private void btnThrowException_Click(object sender, RoutedEventArgs e)
{
    serviceClient.ThrowExceptionCompleted += 
	new EventHandler<System.ComponentModel.AsyncCompletedEventArgs>
		(serviceClient_ThrowExceptionCompleted);
    serviceClient.ThrowExceptionAsync();
}

void serviceClient_ThrowExceptionCompleted
	(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
    if (e.Error == null)
    {
        // In case of success
    }
    else if (e.Error is FaultException)
    {
        FaultException fault = e.Error as FaultException;
        txtThrowException.Text =  e.Error.Message;
    }
} 

4. Unit Test the WCF Service (Using Sessions)

Here I have to say, to unit test a single WCF method is not that difficult (it's like if you unit test a library).

But if you use the ASP.NET session to store information, the client (browser) handles this with cookies. If you want to unit test a method which needs a value from the session, you have to handle the cookies manually. Here you can find a very good explanation for handling cookies in ASMX/WCF services.

First, you have to create a unit test project. I had to do this by hand, since create unit test on a method did not create a new project.

Then use Add service reference to add a reference of the WEB/WCF project to your unit test project.

Now implement the test method:

C#
[TestMethod]
public void TestMethod1()
{
     string _sharedCookie = string.Empty;

     //Creates connection to the WCF service
     //It's done by code, not via config file
     //The config file would be overwritten all the time
     EndpointAddress objEndpoint = null;
     CustomBinding cb = new CustomBinding();
     //binding depends how your web server is set up
     BinaryMessageEncodingBindingElement bmebe = 
		new BinaryMessageEncodingBindingElement();
     //TextMessageEncodingBindingElement tebe = 
	new TextMessageEncodingBindingElement(MessageVersion.Default, Encoding.UTF8);
     HttpTransportBindingElement htbe = new HttpTransportBindingElement();
     cb.Elements.Add(bmebe);
     cb.Elements.Add(htbe);
     objEndpoint = new EndpointAddress("http://localhost:11111/MyCoolService.svc");

     MyCoolServiceClient _serviceClient = new MyCoolServiceClient(cb, objEndpoint);

     //howto manage cookies: 
     //http://megakemp.wordpress.com/2009/02/06/managing-shared-cookies-in-wcf/
     using (new OperationContextScope(_serviceClient.InnerChannel))
     {
         _serviceClient.ServiceCallOne("my first message to the server");
         // Extract the cookie embedded in the received web service response
         // and stores it locally
         HttpResponseMessageProperty response = (HttpResponseMessageProperty)
         OperationContext.Current.IncomingMessageProperties
			[HttpResponseMessageProperty.Name];
         _sharedCookie = response.Headers["Set-Cookie"];
     }
     using (new OperationContextScope(_serviceClient.InnerChannel))
     {
         //this sets the cookie for the next service request
         HttpRequestMessageProperty request = new HttpRequestMessageProperty();
         request.Headers["Cookie"] = _sharedCookie;
         OperationContext.Current.OutgoingMessageProperties
			[HttpRequestMessageProperty.Name] = request;

         string returnValue = _serviceClient.ServiceCallTwo();
         Assert.AreEqual("my first message to the server", returnValue);
     }
} 

As you can see, I call two methods separately, and I return with the second method the session value which was set in the first method call.

Points of Interest

  • Silverlight 4 with Managed Extensibility Framework
  • Silverlight 4 binding
  • Downloading XAP dynamically in Silverlight 4

History

  • 15th September, 2010: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
Piyush has valuable experience in requirements gathering, designing, implementing, and maintaining data-driven, object-oriented and service based enterprise systems. In addition to his technical expertise, Piyush also published paper on knowledge management and he has excellent communication skills to cooperate with the clients.

He holds a Masters Degree in Computer Science from the University Of Michigan, USA

Comments and Discussions

 
QuestionSilverlight Session Management, Error handling, and Unit Test of WCF Service (View) Pin
nik kamble17-Jul-12 1:31
nik kamble17-Jul-12 1:31 
GeneralMy vote of 5 Pin
Manoj Kumar Choubey26-Feb-12 20:03
professionalManoj Kumar Choubey26-Feb-12 20:03 
GeneralMy vote of 4 Pin
Eric Xue (brokensnow)15-Sep-10 11:33
Eric Xue (brokensnow)15-Sep-10 11:33 
GeneralRe: My vote of 4 Pin
Piyush Patell15-Sep-10 18:20
Piyush Patell15-Sep-10 18:20 
Generalplease format Pin
Emilio Garavaglia14-Sep-10 20:45
Emilio Garavaglia14-Sep-10 20:45 
GeneralRe: please format Pin
Piyush Patell14-Sep-10 21:10
Piyush Patell14-Sep-10 21:10 
GeneralRe: please format Pin
BryanWilkins15-Sep-10 10:00
professionalBryanWilkins15-Sep-10 10:00 
GeneralRe: please format Pin
Piyush Patell15-Sep-10 18:16
Piyush Patell15-Sep-10 18:16 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.