Introduction
The Univar library essentially provides an all-in-one toolset for dealing with local storages in ASP.NET.

Here is a list of the main benefits it provides:
1. Minimal syntax usage.
2. Strong typing.
3. Support for generic types in cookies and query strings.
4. Keyless encryption support for data exposed to the client(the key is auto generated from the client machine key).
5. Compression support for size constrained data storage types.
6. A server side implementation of the cookie to avoid data size constraints and exposure to the client.
4. Enhanced manageability by promoting maintainable code.
5. Ability to manipulate data in a disconnected manner to reduce overhead.
6. Persistence of object properties in one line of code.
7. Javascript compatibility via Json serialization.
8. Interoperability amongst different storage types.

PS A fully reworked version of this project with a lot more features is available on https://github.com/ZiadJ/Univar. However I've yet to publish some documentation for it. Feel free to post a message on the issue section of the page if you have any question and I'll do my best to answer your queries.

Using the code
I will assume that you are already familiar with the session, cookie, query string and cache. Below is an enum containing a list of the local storage types you can use:
public enum Sources
{
    None = 0,
    Session = 1,
    Cookie = 2,
    QueryString = 4,
    Cache = 8,
    ServerCookie = 16
}
The server cookie is a text based data storage system I implemented to overcome those common issues in the conventional cookie model, like lack of security and size limit. Being stored on the server it cannot be accessed by the client and each server cookie is associated with its user via a tamper proof ID stored in a cookie on the client. It is therefore a better way to store sensitive information. Note that it can use form authentication to identify the user wherever available.

To start with lets create an object to reference an integer in a session under the key "counter":
Local.Var<int> count = new Local.Var<int>("counter", Sources.Session);
This is the non generic version of the above example:
Local.Integer count = new Local.Integer("counter", Sources.Session);
For simplicity the Sources.Session argument can be omitted since it is the default value when none is specified:
Local.Integer count = new Local.Integer("counter");
Our object can now be used like a normal integer through its Value property as shown below:
count.Value = count.Value + 10;
Note that the Sources.Cache and Sources.ServerCookie do not allow sharing amongst users by default. However this can be set using the
ShareCache and ShareServerCookie properties as shown respectively below:
Local.Integer sharedCacheCount = new Local.Integer("counter", Sources.Cache) { ShareCache = true };
Local.Integer sharedSercerCookieCount = new Local.Integer("counter", Sources.ServerCookie) { ShareServerCookie = true };
Let's now proceed to a more advanced scenario involving several storage types. The code below declares an object that will access a value stored in the query string or the cookie, under the key "Id", with priority given over the query string whenever both are present:
Local.Integer personId = new Local.Integer("Id", Sources.QueryString, Sources.Cookie);
int id = personId.Value;
In other words the object personId will attempt to retrieve its value from the query string but upon failure to do so it will use the cookie instead. By taking advantage of generics and Json serialization Univar can store complex data structures within text based storage types as well. Thus a custom object, say Person, can be directly stored in the query string as shown below:
Local.Var<Person> person1 = new Local.Var<Person>("person_1", Sources.QueryString);
For optimization and compatibility purposes, primitive types are not serialized. This way the types Sources.Session, Sources.Cookie or Sources.QueryString remain compatible with external objects like the ASP.NET data sources.
Since it often happens that keys share a common prefix, the latter can be specified within a common constructor for simplicity. For example the code below maps the person1 and person2 objects to the keys "person_1" and "person_2" respectively:
var persons = new Local.Var<Person>("person", Sources.QueryString);
Person person1 = persons[1];
Person person2 = persons[2];
Note that "_" is the default delimiter used to separate the key and the subkey. Note that, to save space, cookies inherently provide support for storing data in the key/subkey/value format. So the use of the default delimiter helps taking advantage of this when using the Sources.Cookie type.

Query strings and cookies can be made tamper proof using machine key based encryption and they can also be compressed to meet with the tight size limits imposed. The following example shows how to enable encryption and compression on both cookies and query strings:
var person = new Local.Var<Person>("PersonInfo", Sources.QueryString, Sources.Cookie)
{
    CompressCookie = true,
    EncryptCookie = true,
    CompressQueryString = true,
    EncryptQueryString = true
};
Another feature is the ability to apply changes to our storage types in a disconnected manner. It helps to eliminate unnecessary overhead when several changes need to be saved only ounce. To illustrate it let's consider the object person declared above and try to assign a few values successively to it:
person.Value = new Person();
person.Value.Name = "Paul";
person.Value.Gender = Gender.Male;
Since the query string is being used as storage type, all changes applied to its value requires updating the url. Under the hood this change is translated by a call to the Response.Redirect method which has the particularity of killing the page thread right away, thus leaving the name and gender properties unassigned. One workaround is to use Disconnect and SaveChanges methods as shown below:
person.Disconnect();
person.Value = new Person();
person.Value.Name = "Paul";
person.Value.Gender = Gender.Male;
person.SaveChanges(true);
After calling the Disconnect method, all changes are carried out in memory such that the final value can be saved to the target storage, only ounce, by calling the SaveChanges method. Alternatively, when dealing with several key/value pairs, a NameValueCollection can be used instead as shown below:
Local.SetQueryString(true, new NameValueCollection() 
{ 
    { "Action", "Print" }, 
    { "Id", "123" } 
});
For more advanced requirements note that the method BuildQueryString also provides several overrides for editing query strings.

So far, using the Sources enum, we have seen how to specify a storage type but this applies for both read and write operations. If we want to use certain storage types for write operations while using different types for reading we can easily do so by using the Targets class below:
public struct Targets
{
    public static readonly byte
    None = 0,
    Session = 1,
    Cookie = 2,
    QueryString = 4,
    Cache = 8,
    ServerCookie = 16;
}
The example below declares an object personId that reads from the query string but writes to the cookie:
Local.Integer personId = new Local.Integer("Id", Targets.Cookie, Sources.QueryString);
The only reason why the Targets is not declared as an enum is to allow its elements to be added up as flags in the constructor. In the example below the object personId reads from the query string or the cookie but write operations are directed towards the cookie and cache instead:
Local.Integer personId = new Local.Integer("Id", Targets.Cookie + Targets.Cache, Sources.QueryString, Sources.Cookie);

Note that to delete a stored key you only need to set the object value as null. I should also point out that cookies automatically clear themselves when they grow beyond 4Kb and query strings are usually allowed a maximum data size of 2048Kb. The provided library will throw an error whenever the query string becomes too big but this won't happen for cookies because I haven't found a way to determine their size yet. So its up to you to keep an eye on it. The best I could do is to implement compression ability and provide the the server cookie as an alternative.

An additional feature is the ability to persist an object property by making a call to the PersistentProperty class. The code below demonstrates how the selected values of several DropDownList controls can be persisted through a cookie:
Local.PersistentProperty<string>.Persist("SelectedValue", Sources.Cookie, IsPostBack,
    ddlSourceTypes1, ddlSourceTypes2, ddlSourceTypes3);

Better documentation for this project will be added with time if it gets enough traction from the community and meanwhile, of course, your suggestions, questions and critics are most welcome.

Future Plans:
1. Use Json.Net instead of the default DataContractSerializer for serialization.
2. Add ability to use web services as persistence mechanism.
3. Enhance the actual server cookie implementation.
4. Add web farm support using MemCache and/or Velocity(.NET 4.0 only).

Last edited Apr 15 at 2:51 PM by ZiadJ, version 191