Introduction
When scalability is the main issue in your application, it is common to design and build a stateless solution.
Stateless design allows our application to be duplicated to several servers as the amount of users grow, load balancing could be taken to the max, plus it saves resources "wasted" on session management.
The downside is that in most applications having session identifier or other session level parameters, it is really convenient (sometimes even necessary).
So if scalability is part of your design - distributed session management is probably something you are considering...
But if you don't really need a big session management solution that will cost you in performance (no matter what..) and you only want 2-3 parameters that will help you identify some user's preference without all the fuss, consider - soap extension.
Implementation
We start implementing by building a class that represents these parameters we want to pass constantly from client to server.
Surprisingly this class will inherit from SoapHeader
.
[XmlRoot("Keystone", Namespace = "urn:com-sample-dpe:soapextension")]
public class SessionSoapHeader : SoapHeader
{
private string _customer;
private string _version;
public string Version
{
get { return _version; }
set { _version = value; }
}
public string Customer
{
get { return _customer; }
set { _customer = value; }
}
}
Next we'll create an attribute to make our proxy implement the use of this extension.
[AttributeUsage(AttributeTargets.All)]
public class SessionSoapHeaderExtensionAttribute : SoapExtensionAttribute
{
public override Type ExtensionType
{
get { return typeof(SessionSoapHeaderExtension); }
}
public override int Priority
{
get
{
return 100;
}
set
{
}
}
}
And to the actual Soap extension...
public class SessionSoapHeaderExtension : SoapExtension
{
public override object GetInitializer
(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute)
{
return null;
}
public override object GetInitializer(Type WebServiceType)
{
return null;
}
public override void Initialize(object initializer)
{
return;
}
public override Stream ChainStream(Stream stream)
{
return stream;
}
public override void ProcessMessage(SoapMessage message)
{
switch (message.Stage)
{
case SoapMessageStage.BeforeSerialize:
if (message is SoapClientMessage)
{
AddHeader(message);
}
break;
case SoapMessageStage.AfterSerialize:
break;
case SoapMessageStage.BeforeDeserialize:
break;
case SoapMessageStage.AfterDeserialize:
foreach (XmlNode headerItem in headerXml.ChildNodes)
{
if (headerItem.Name.IndexOf("customer")!= -1)
{
HttpContext.Current.Items.Add
(headerItem.Name, headerItem.InnerText);
SessionSoapHeaderExtension.Customer =
HttpContext.Current.Items[headerItem.Name].ToString();
}
if (headerItem.Name.IndexOf("version") != -1)
{
HttpContext.Current.Items.Add
(headerItem.Name, headerItem.InnerText);
SessionSoapHeaderExtension.Version =
HttpContext.Current.Items[headerItem.Name].ToString();
}
}
if (message is SoapClientMessage)
{
}
break;
}
}
private void AddHeader(SoapMessage message)
{
SessionSoapHeader header = new SessionSoapHeader();
header.Customer =
(!string.IsNullOrEmpty(Customer) ? Customer : string.Empty);
header.Version =
(!string.IsNullOrEmpty(Version) ? Version : string.Empty); ;
header.MustUnderstand = false;
message.Headers.Add(header);
}
}
Final step, configure both server & client to use this extension.
In the server:
<webServices>
<soapExtensionTypes>
<add type="myNS.SessionSoapHeaderExtension, myNS"/>
</soapExtensionTypes>
</webServices>
In the client, you can configure similarly using strong name & registering the DLL in GAC or add the attribute to the reference.cs of the required proxy.
client config:
<system.web>
<webServices>
<soapExtensionTypes>
<add type="myNS.SessionSoapHeaderExtension, myNS,Version=1.0,
Culture=neutral, PublicKeyToken=f54c79bbbb6454bc" />
</soapExtensionTypes>
</webServices>
</system.web>
That's it. This simple implementation will allow you to pass user's preference or identification elegantly in the header and save you the use of heavy session management solution when not needed.
Any kind of feedback or questions would be appreciated.
Till next time.
Diego