Introduction

Because of the stateless nature of the HTTP protocol a web server considers every request as a new one. This means that, if you want to track a series of related calls, you must establish a way of identifying if the current request is a new request or is related to another request.

One way of doing that is to generate a token for the first request and then reuse the same token for related requests. The token can be anything from a number to a complex object, the only important thing is that the token is guaranteed to be unique. If that token is sent with every request, then everything works just fine, as we can say for sure to which group the current request is a part of. We can do it the other way around too: the client makes a call with a unique token and, based on that token, the communication occurs. This can be implemented when a unique username is used as a token, for instance.

However, another problem arises, with even greater importance. What if all of the requests are related to the same object? What if we need to call the method several times on the same object and the method modifies the object? We need a way of storing the object between calls so we can modify the object with each new call.

This is what we will be talking about. Using the token concept, I will describe a way of maintaining object state between sequent calls to the same object.

The idea

There are several ways of implementing this functionality. I will discus only two of them in this article. The idea is common to both ways, what differs is the implementation details.

Let’s assume that we use the approach when the client uses a username for a token and makes a request to a web service.

The idea: There is a list of all the tokens that previously made the request. When the request is made, the web service retrieves the token from the parameters of the request and compares it to the list. If the token is not found there, then this is the first request made for that token, and a new object is created for that request. The token is added to the list of tokens.

If the token is found in the list, then this is a sequent request, and the object is available. The object is then retrieved. It will contain the current data (the data modified by the last request).

Now we have the object (newly instantiated or retrieved). We can now use the object and call the methods we need.

After we are done using the object, it is time to store it away so it will be available when we need it again.

Sounds complicated? Well, it’s not. Fortunately for us, there are already implemented systems that help us with the token list. In what follows, I will discuss two systems that are available for use with no extra effort: The Application object and… the file system.

Using the Application object of web services

Every ASP.NET web application (including web services) has a facility called the Application State. This facility, accessed via the Application property enables the storage of different objects between requests. Objects stored in this way are available to the entire application.

A long discussion would be required about Session State and the ways to store it. Actually, you can use a State Server, or even a SQL Server to hold the State data. If you are developing large applications, this is the way to go. However, for smaller applications, these solutions are, most of the times, not necessary.

I hope it is clear that the Application object is what we need. Objects can be stored in the Application object like in a hash table. You could use the token (which is unique) as a key and store the object as the value.

This solution is great; however, it does have a drawback: the state is not persistent all the way. When the server is rebooted, the state is lost. Also, the ASP.NET worker process might be recycled by the Internet Information Services (IIS). This means that, sometimes, you might end up losing the data you have inside the Application object.

Programming using this facility is extremely simple, as you can see in the code below:

publicstring MethodCall_ver1(int token)
{
UserDefinedClass d = null;
//If the token not found in the Application
//object, we retrieve it
if (Application[token.ToString()] != null)
{
d = (UserDefinedClass)Application[token.ToString()];
}
else
{
//if the token was not found then we create a new object
d = new UserDefinedClass();
}
//Call methods on the object
//We save the object into the Application object
Application[token.ToString()] = d;
//The service returns
returnstring.Format("{0}", d.something.ToString());
}

We only have to check if an object is stored at the location specified by the token. If it is not, then we instantiate a new object. After we are done using it, we store it in the Application object.

This solution is great if you have requests that are closer in time to one another and you don’t care very much about losing the data. However, when you are interested in keeping the state between requests whatever happens, and don’t what to use additional servers, the solution presented below can be used.

Using the File System

The file system is a great way of storing objects between system restarts. Also, even if the file system is arbores cent in nature, there still exists a way of uniquely identifying a file inside a folder: by using the file name. So, the token can be used as a file name.

When the request comes, the method will search for a file with the name as the token. The idea remains the same, only the implementation changes. Instead of looking in the Application object, you check to see if the file exists. There is a catch though. We need to take an object from memory and store it on the file system. This means that we have to invoke a process known as serialization in which the bytes of the object are stored into a file stream. The reverse process is know as deserialization, and consists in reading the bytes of the object from a stream and recreating the object.

Fortunately for us, the .NET framework allows us to do just that, and very simple! The BinaryFormatter class does just that. Below we have the code that does that. As you can see, the only thing that changed is the way we save and retrieve the object:

//We have a setting in web.config which tells
//us where should we store the files.
// The setting is called TemporaryDirectory
//Check to see if the file if the specified directory
//exists. If it does, this is not the first request and we retrieve it
if (File.Exists(string.Format("{0}{1}.dat",
System.Configuration.ConfigurationManager.
AppSettings["TemporaryDirectory"], id )))
{
//Get a FileStream to point to that file
FileStream sr = new FileStream(string.Format("{0}{1}.dat",
System.Configuration.ConfigurationManager.
AppSettings["TemporaryDirectory"], id),
FileMode.Open);
//Create an object used in serializing/deserializing objects
BinaryFormatter bf = new BinaryFormatter();
//deserialize the object
d = (UserDefinedClass)bf.Deserialize(sr);
sr.Close();
}
else
{
//if the file does not exist then we create a new object
d = new UserDefinedClass();
}
//Call methods on the object
//We serialize the object back to the file system
FileStream sw = new FileStream(string.Format("{0}{1}.dat",
System.Configuration.ConfigurationManager.
AppSettings["TemporaryDirectory"], id),
FileMode.OpenOrCreate);
BinaryFormatter abf = new BinaryFormatter();
abf.Serialize(sw, d);
sw.Close();
//The service returns
returnstring.Format("{0}", d.something.ToString());

The TemporaryDirectory is stored in web.config and the entry in that file is:

This is all that needs to be done in order for us to have a web service that persists its state between requests.

This solution has the advantage that the state is persisted even between system crashes and system reboots. This is a great thing. However, we have the problem of authorized access. The files stored on a server can be removed by people with not so good intentions. This means that, if the objects contain sensitive data, we have a breach. In order for this not to happen, we have to set the appropriate ACL (Access Control List) on the folder where we store the serialized objects and make sure that IIS does not serve them to anyone!

Testing the solution

Well, to do that, we just have to call the web service repeatedly with the same token, and see how the something property continues to increment.

Performance consideration

While storing objects in the Application object might seem the better solution, as the number of requests that need to be served increases, all of the objects get stored in the server memory. This might be a huge problem, and could even result in a DDoS attack.

On the other hand, while using the file based approach, you incur a penalty for each read and write to the disk. However, the memory is only allocated when it is needed, so no DDoS.

Conclusion

If you want to have persistent state between sequent calls to a web service, you can employ one of the solutions I presented. Either use the Application object for storing the objects, or serialize the objects and store them in a file, on disk.

The Application object approach is better suited for small objects that are accessed frequently, while the file approach is better suited for big objects that are accessed not as frequently.

In any case, the solution you decide to implement is entirely up to you and the technical requirements of the application you develop.

Comments and Discussions

If I have several users accessing my web service at once, is there a way of passing the object stored in Application between the connected users? The data is small (about 100k) but I could save a lot of trips to the SQL server if the data could be shared between all the connections.

Thank you for your apreciation. It is always good to hear that I helped .

Regarding your question about sharing an object between calls. The Application object in ASP.NET is a shared object between all of the users accessing the application at one time. This means that you can have something stored in the Application object and it could be accessed by all of the requests. However, you should be careful to have some sort of concurency control if the object shared can be modified.

This means that you can use a ReaderWriterLock to block the object from being accessed while you update it with data from the database.