Saturday, 29 December 2007

Improve web service efficiency by using SoapDocumentMethod(OneWay = true)

Sometimes we have a long running process in a web method but we don't want our client application to wait until the process is finished. We can decorate our web method with this attribute:
[SoapDocumentMethod(OneWay = true)]

This setting tells web service client not to wait for the web server to finish the process of the web method. This is ideal for logging client events because client application doesn't care if the web server is sucessfull with doing logging.

Friday, 28 December 2007

Custom SoapExtension, SoapHeader and Web Method authentication

Currently I am working on a project which involves Web Service and Windows Client. The web service will be public and we are going to give the Windows client application to our suppliers across the Internet. The communication between client and our server is through Web Service.

To make our Web Service secure, login is required. Users provide their user name and password in the initial Web Service call through a login web method. In the subsequent calls, the user name and password are provided all the time. By doing this an anounimous user will fail when making a call to our secured methods.

This makes me think how I am going to transfer login information. In my previous project for another client, I used to concatenate user name and password into one string and pass to each web method through an extra parameter. And we validate the user in each web method. I feel this is tedious and not quite right. Fortunately I came across SoapHeader and SoapExtention when I read the book: .NET Framework 2.0 Distributed Application Development published by Microsoft Press. And I realized this is what I need to implement web method authentication.

According to SOAP specification, a SOAP message contains a Header, an Envelope, and a Body element. The Header is optional, but if included, it's inside the Envelope element and usually contains authentication information.

.NET we can have our custom header class which inherits from SoapHeader class. Here is my example:

public class ServiceAuthHeader : SoapHeader
{
private string userName;

public string UserName
{
get { return userName; }
set { userName = value; }
}
private string password;

public string Password
{
get { return password; }
set { password = value; }
}
}


In the client side consuming code, we must pass user data through Header as below:

RemoteService.RemoteService service = new RemoteService.RemoteService();
RemoteService.ServiceAuthHeader authHeader = new RemoteService.ServiceAuthHeader();
authHeader.UserName = userName;
authHeader.Password = password;
service.ServiceAuthHeaderValue = authHeader;

DataSet ds = service.GetMasterData();

In the Web Service class, we declare a public ServiceAuthHeader property.

private ServiceAuthHeader authSoapHeader;
public ServiceAuthHeader AuthSoapHeader
{
get { return authSoapHeader; }
set { authSoapHeader = value; }
}


And decorate the web method with SoapHeaderAttribute, as below:

[SoapHeader("AuthSoapHeader")]
[WebMethod]
public DataSet GetMasterData()
{
string userName = string.Empty;
if (AuthSoapHeader != null)
{
userName = AuthSoapHeader.UserName;
// then do business with userName.
}
}

Note there is nothing related with Authentication so far. Authentication is implemented through SoapExtension as discussed below.

SoapExtension privides a powful way of allowing developers to add additional functionalities to standard web methods. In another project we used SoapExtension to provide data compression for communication between a Windows Mobile device and a web server through GPRS network.

We simply inherit our custom extension class from SoapExtension as below:
public class AuthSoapExtension : SoapExtension

There are several abstract methods from SoapExtension, we are only interested in ProcessMessage(SoapMessage message) method. This method gives developers a chance to handle the SOAP message in each stage. There are four stages during the processing of a SOAP message in the web server: BeforeSerialize, AfterSerialize, BeforeDeserialize, and AfterDeserialize. The Stage property of message parameter indicates which stage the current message is in. The Header object is available in the AfterDeserialize stage because the SOAP message is deserialized into .NET objects.

The following code is used to get user information from header and validate.

public override void ProcessMessage(SoapMessage message)
{
if (message.Stage == SoapMessageStage.AfterDeserialize)
{
ServiceAuthHeader header = message.Headers[0] as ServiceAuthHeader;
if (header != null)
{
bool valid = ServiceValidation.ValidateUser(header.UserName, header.Password);
if (!valid)
{
throw new Exception("Invalid username or password.");
}
}else{
throw new ArgumentNullException("No ServiceAuthHeader provided in SoapMessage.");
}
}
}

To apply this custom soap extension to our web method, we need another attribute inherits from SoapExtensionAttribute.

[AttributeUsage(AttributeTargets.Method)]
public class AuthExtensionAttribute : SoapExtensionAttribute
{
private int _priority;

public override Type ExtensionType
{
get { return typeof(AuthSoapExtension); }
}

public override int Priority
{
get
{
return _priority;
}
set
{
_priority = value;
}
}
}


Then in our web method, we decorate it with this attribute:

[SoapHeader("AuthSoapHeader")]
[WebMethod]
[AuthExtension]
public DataSet GetMasterData()

The decoration tells the web method to use our custom AuthSoapExtension. Each time this web method is called, the ProcessMessage method in AuthSoapExtension is triggered, and the username and password information is retrieved from our custom SoapHeader and validated against a database.

Friday, 1 June 2007

Pass a list of values as a parameter to stored procedure

I encountered this problem long time ago and I used some complex approach to solve it. Today I come across a excellent approach by converting delimited string into a table return as an table-value function.

The original article is here:

http://www.codeproject.com/cs/database/TableValuedFnsAsArrays.asp

The function is as below.

CREATE FUNCTION [dbo].[StringToTable]
(
@string VARCHAR(MAX),
@delimiter CHAR(1)
)
RETURNS @output TABLE(
data VARCHAR(256)
)
BEGIN

DECLARE @start INT, @end INT
SELECT @start = 1, @end = CHARINDEX(@delimiter, @string)

WHILE @start <= LEN(@string) + 1 BEGIN
IF @end = 0
SET @end = LEN(@string) + 1

INSERT INTO @output (data)
VALUES(SUBSTRING(@string, @start, @end - @start))
SET @start = @end + 1
SET @end = CHARINDEX(@delimiter, @string, @start)
END

RETURN

END

Sunday, 1 April 2007

Use Mutex class to ensure only one instance of an application is running

Mutex class is an operating system level class to provide a data locking mechanism in multi-threading application. It works by locking data across AppDomain and process boundaries. Thus this class is ideal to ensure only one instance of an application is running.

In the Main() function of Progam.cs, add this piece of code will ensure one instance of an application, because the MutexName is shared across application domains.

Mutex one = null;
const string MutexName = "RunOnce";
try
{
one = Mutex.OpenExisting(MutexName);
}
catch (WaitHandleCannotBeOpenedException)
{
}

if (one == null)
{
one = new Mutex(true, MutexName);
}
else
{
one.Close();
return;
}

Wednesday, 28 March 2007

Select a random row from a table

It looks like a simple task to read a random record from a table. Many people would spend lot of time try to do this with lot of t-sql code. When I was reading an article, I found this could be easiely achieved by using NewID() function. The use of this function is like this:

SELECT TOP 3 newid(), ProductID, ProductName
FROM Products
order by newid()

Each time you execute this sql, the result will be 3 random records. And you can select one record back by limiting TOP 1.

Friday, 2 March 2007

Change Pocket PC system datetime

There is a need in a mobile device application to change local date time programatically. After a search I found we have to P/Invode Windows API. The API used in SetLocalTime.

To start with, we need to import the library:

[DllImport("kernel32.dll")]
static extern bool SetLocalTime([In] ref SYSTEMTIME lpLocalTime);

Then there is a piece of code from web to change time (http://pinvoke.net/default.aspx/kernel32.SetLocalTime):

using System;
using System.Runtime.InteropServices;

public class MyClass1
{
///
/// SYSTEMTIME structure with some useful methods
///

public struct SYSTEMTIME
{
public ushort wYear;
public ushort wMonth;
public ushort wDayOfWeek;
public ushort wDay;
public ushort wHour;
public ushort wMinute;
public ushort wSecond;
public ushort wMilliseconds;

///
/// Convert form System.DateTime
///

///
public void FromDateTime(DateTime time)
{
wYear = (ushort)time.Year;
wMonth = (ushort)time.Month;
wDayOfWeek = (ushort)time.DayOfWeek;
wDay = (ushort)time.Day;
wHour = (ushort)time.Hour;
wMinute = (ushort)time.Minute;
wSecond = (ushort)time.Second;
wMilliseconds = (ushort)time.Millisecond;
}

///
/// Convert to System.DateTime
///

///
public DateTime ToDateTime()
{
return new DateTime(wYear, wMonth, wDay, wHour, wMinute, wSecond, wMilliseconds);
}
///
/// STATIC: Convert to System.DateTime
///

///
///
public static DateTime ToDateTime(SYSTEMTIME time)
{
return time.ToDateTime();
}
}

//SetLocalTime C# Signature
[DllImport("Kernel32.dll")]
public static extern bool SetLocalTime( ref SYSTEMTIME Time );

//Example
private void Add7Days

{
//Get current time and add 7 days to it
DateTime t = DateTime.Now.AddDays(7);;
//Convert to SYSTEMTIME
SYSTEMTIME st = new SYSTEMTIME();
st.FromDateTime(t);
//Call Win32 API to set time
SetLocalTime(ref st);
}
}

Monday, 15 January 2007

ASP.NET Sitemap and Role management

The asp.net 2.0 navigation system is a bit of confusion and the setup of the navigation is tricky. I spent several hours to make it work properly.

When we have a Menu control in a page and want the menu to display menu items based on the login user's role, we need to configure several places. There are several components in a navigation system: Membership, Role, SiteMap xml file such as web.sitemap, SiteMap datasource, web.config and Menu control. All these components work together to build a dynamic, user role aware navigation system.

Let's describe how to configure these components.

The membership and role modules are easy to configure, we can enable and configure them through the built in ASP.NET Configuration tool.

In the Web.sitemap file, we define the structure of the site.

 

<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0">
<siteMapNode url="" title="Top" description="" roles="*">
<siteMapNode url="GeneralUser.aspx" title="General User" description="" roles="GeneralUser" />
<siteMapNode url="PowerUser.aspx" title="Power User" description="" roles="PowerUser" />
</siteMapNode>
</siteMap>



The siteMapNode element has an attribute "roles", which defines what roles have the access to this item in a navigation control.

In the Web.config file, we define the site access in section:

 


<location path="GeneralUser.aspx">
<system.web>
<authorization>
<allow roles="GeneralUser"/>
<deny users="*" />
</authorization>
</system.web>
</location>

<location path="PowerUser.aspx">
<system.web>
<authorization>
<allow roles="PowerUser"/>
<deny users="*" />
</authorization>
</system.web>
</location>



The sections in Web.config define the actual access right for each page to roles.

The above two files should match with roles to make the navigation system work properly.

The Menu control and SiteMapDataSource control are easy to configure. In the web page which needs a menu (normally in a master page), just drag a SiteMapDataSource onto the page. Then drag a Menu control onto the page, set the DataSourceID property to the site map data source.

After all these are done, the whole site navigation system should be working properly.