Passing Session Data down through the Tiers

So here's a conundrum that my customer and I worked through yesterday. We have an ASP.NET site, with a user id retrieved from wherever and carried about in the Session for general-purpose use to identify each of our users.

In our data layer, we have an auditing capability, the purpose of which is to record when any data element is changed, by which user, and when. This is in a base class, and needs to be called on many items of data for which a user id is not actually part of the data being changed. So how does the audit function know which user changed the data? Being in a separate layer, it has no access to the Session, nor do we want it to - this same layer could later be used in a SmartClient application, which does not use Session, and should not need modification.

Well one approach might be to always have every function pass the user id down to the data layer for every call. This is rather wasteful and puts a constraint on the data layer that subsequent development teams will have to know about and code for - a very brittle design.

One team member suggested using a public static variable in a common class to hold this data. This static member would be set by the UI layer with the user id, and would also be visible to the Data Layer. This would work, if the web site were single-user; the second a second user comes along, the one and only variable is going to get overwritten by the id for the second user and the audit function is going to record all changes made by the first user as if the second had made them.

The solution we came up with was to use a LocalDataStoreSlot. This is a local in-memory store that holds thread-specific data. A new thread is created (or pulled from a pool) for each request to an ASP.NET site, and usually executes all the data in the various layers too. So what we do is at the beginning of each request, in a custom HTTPHandler, we retrieve the value that we need to store (the securityIdentityID in our example) and drop it in a slot. Here is how we do it:

LocalDataStoreSlot myslot = Thread.GetNamedDataSlot("aSlot");
Thread.SetData(myslot, securityIdentityID );

Then, later down in our Data Access Layer, we can pull the value we need out of this slot and use it for whatever we please.

LocalDataStoreSlot myslot = Thread.GetNamedDataSlot("aSlot");
object identityID = Thread.GetData(myslot);
securityIdentityID = Convert.ToInt32(identityID); 

This time, this data is completely local to this Thread and there is one Thread per request so there is no danger of this value getting corrupted by the fact other users are on our Application. Problem solved!