Using ASP.NET Sessions from WCF

This is not a new topic. But interestingly I could not find a good example on the web while I wanted to find one for some other investigation. So I decided to create a sample here to show the following scenario:

· A WCF service has the access to a ASP.NET session. Different WCF client proxies can connect to the per-call WCF service and share the same ASP.NET session state.

Here I will show a simple example on how to achieve this.

ASP.NET Sessions vs WCF Sessions

WCF sessions are very different from ASP.NET Sessions. In short,

· WCF sessions are represented as service instances (as CLR objects) and the states are part of each service instance. The sessions are initiated by the calling WCF client. WCF relies on special context to provide correlation for the sessions: sessionful channels, secure conversations, reliable-messaging, etc.

· ASP.NET sessions are like shared data storage across different requests. The sessions are always initiated by the server. ASP.NET relies on cookies or special Uri goo to provide correlation for the sessions.

You can find more details for the comparisons of the two from the MSDN article “Using Sessions”.

While these two concepts are quite different and the usages are also drastically different, the WCF sample “HttpCookieSession” tried to use the similar idea as cookie-based ASP.NET sessions to provide WCF session support. The sample is a little bit complex and it does not use real ASP.NET sessions though.

There are many samples on how to use WCF sessions, for example, “How to: Create a Service That Requires Sessions” is a good one.

So I will only show how to use ASP.NET sessions from WCF below. It uses PerCall service to handle this. So different service instances have access to the same ASP.NET session state.

Using ASP.NET Session States

Storing Session States

It is known that ASP.NET session states can be stored in different places: 1) In-Memory (thus In-process), 2) Out-of-proc state service, 3) SQL server, 4) a custom session-state store provider, etc. More details can be found in “ASP.NET Session State Overview”.

For simplicity, I will just show the in-memory case. Here is a very simple session state:

class MySessionState

{

    int counter;

    public MySessionState() {}

    public int Counter { get { return this.counter; } }

    public int Touch() { return Interlocked.Increment(ref this.counter); }

}

The state just contains a single counter. This state is stored in the session object as following:

MySessionState state = (MySessionState)HttpContext.Current.Session["MySessionState"];

if (state == null) {

    state = new MySessionState();

    HttpContext.Current.Session["MySessionState"] = state;

}

When the service receives a request, the sample increments the counter to demonstrate the sharing effect:

int counter = state.Touch();

Enabling Session States

It is very straightforward to enable the ASP.NET session states. You only need to run WCF service in the ASP.NET Compatibility mode as being specified in the web.config:

<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>

Retrieving Session Id on the Client

Once the session states are enabled and there is session data populated as above, we can retrieve the Session Id from the cookie on the client side:

using (new OperationContextScope((IContextChannel)proxy))

{

    Console.WriteLine(proxy.Greet("Hello"));

    HttpResponseMessageProperty responseProperty = OperationContext.Current.IncomingMessageProperties[HttpResponseMessageProperty.Name]

        as HttpResponseMessageProperty;

    helper = HttpSessionCookieHelper.Create((string)responseProperty.Headers[HttpResponseHeader.SetCookie]);

}

Here the type “HttpSessionCookieHelper” is just a helper class that is used to retrieve the ASP.NET Session Id from the cookie string (with the cookie name “ASP.NET_SessionId”) and append the Session Id to the request. It is defined as following:

class HttpSessionCookieHelper

{

    const string AspNetSessionIdCookieName = "ASP.NET_SessionId";

    string aspNetSessionId;

    public static HttpSessionCookieHelper Create(string cookieString);

    public static HttpSessionCookieHelper CreateFromSessionId(string sessionId);

    public void AddSessionIdToRequest(HttpRequestMessageProperty requestProperty);

    public string AspNetSessionId { get { return this.aspNetSessionId; }

}

Sending Session Id

Once we get the Session Id retrieved from the response of the first request, we can share it for the subsequent client calls. Here is the example:

using (new OperationContextScope((IContextChannel)proxy))

{

    HttpRequestMessageProperty requestProperty = new HttpRequestMessageProperty();

    helper.AddSessionIdToRequest(requestProperty);

    OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = requestProperty;

    Console.WriteLine(proxy.Greet("Howdy"));

}

The implementation of the HttpSessionCookieHelper.AddSessionIdToRequest does nothing but the following:

requestProperty.Headers[HttpRequestHeader.Cookie] = string.Format("{0}={1}", AspNetSessionIdCookieName, this.aspNetSessionId);

With this logic, the second request would share the same ASP.NET Session State on the server side.

Sample Setup

Here are the steps on how to use it:

1) Unzip the attachment locally.

2) Create a virtual application with name “WCFAspNetSession” and the physical path points to the “WCFAspNetSession” directory.

3) Compile the client code and run it.

Here is the output of the sample:

[Session '1vgfq255e1lpe255whwprf55'] You said: Hello (counter: 1)

[Session '1vgfq255e1lpe255whwprf55'] You said: Howdy (counter: 2)

As you can see, the Session Ids for the two requests are the same and the counter for the shared state has increased.

Here is the sample code: [sample attachment].

WCFAspNetSession.zip