views:

1680

answers:

4

I have one ASP.NET web application running at the web server root which provides multiple (similar) web sites by using URL redirection. To give a real world example:

http://webshopserver/company1/ProductList.aspx -> http://webshopserver/ProductList.aspx?showProductsFrom=company1
http://webshopserver/company2/ProductList.aspx -> http://webshopserver/ProductList.aspx?showProductsFrom=company2
...

This works very fine; the only problem is that, obviously, all of these different shops share the same session object (since the InProc session manager stores the session object in the AppDomain). I would like the shops of company1 and company2 to have different session objects, so that, for example, if a user opens the shops of company1 and company2 in different tabs of the same browser window, the items put in the shopping cart of company1 won't show up in the cart of company2.

There are a few obvious approaches to solve this problem that I don't like:

  • Create my own Session object which encapsulates everything into a HashMap<CompanyName, whatever> and then stores it in the "real" session: That breaks all existing code that uses the session object.
  • Use URLs like http://company1.webshopserver/ and a wildcard DNS record, because the session ID cookie is tied to the domain: That's ugly (because the real-world equivalent of "webshopserver" is already long enough).
  • Write my own custom Session Provider: That would be kind of like reinventing the wheel.
  • Create a separate IIS application for every company: Won't work, because creating new companies should be possible through something like http://webshopserver/CreateYourOwnWebshop.aspx without any human (server admin) intervention afterwards.

I'm thinking more of a solution along the lines of:

So, any information on how to achieve one of these points (or maybe a different solution altogether) would be helpful.

+1  A: 

Depending on how you're adding and retrieving items to and from the Session (and possibly dependent on whether you're deriving your pages from a common base page), you might be able to do a custom object that wraps the session without breaking all of your code (as mentioned in your first option where you have a session wrapper implemented as a hash map keyed by site). If you create a custom object and expose it as "Session" at the base page level, it will take precedence over the Session object you inherit from the Page class. Then, your custom object can override the indexers and make the determination where in the hashmap to store this object based on the request url of the current http context.

If you're not deriving from a common base page, this kinda goes out the window because then you'd have to either implement a base page for all pages to derive from or add code to each page to get a reference to your object. Just and idea...

Rich
+4  A: 

Wouldn't the simplest solution be to update references to Session objects that are company dependent with a dynamic key based on the Company?

For example...

Session["IsTest"]

becomes

Session[createSessionKey(CompanyID, "IsTest")]

where createSessionKey generates the corresponding key possibly by a simple concatenation of Company and Key

This would then differentiate the two or more companies by accessing the session via generated keys.

Following the example above, company1 would access the "IsTest" Session variable via the key "company1_IsTest" and company2 would access the "same" "IsTest" Session variable via the key "company2_IsTest".

Hopefully you have don't have things like Session("IsTest") littered all over your code base as that would make refactoring your code a real pain.

Typically I abstract my Session variables into a strongly typed class. Then my session management is contained in one place.

Using the idea of having a base Page class and overriding the Session property is a nice way to go if all you Session variables are to be company specific. Though if you can determine if a particular Session key is a generic Session variable or company specific then it may still be workable.

BlackMael
Although it's one of the approaches ruled out in the question (creating a Session wrapper), I will mark it as the answer because that's what I ended up using.
Heinzi
+1  A: 

Creating your own Session wrapper, or a custom Session Provider would be the right answer. But, you may be able to hack it by moving existing session data as the user goes to another company with an HttpModule hooked to PostAcquireRequestState.

Basically, compare this ProductsFromCompany with the previous ProductsFromCompany. If they're different, move all of the existing Session values to a Dictionary<string, object> (or prepend a company id to them) and restore the saved Dictionary<string, object> for this ProductsFromCompany to Session.

Something like:

void PostAcquireRequestState(object sender, EventArgs e) {
   if (Session["ProductsFromCompany"] != Request["ProductsFromCompany"]) {
      var lastCompanySession = new Dictionary<string, object>(Session.Count);
      var sessionKeys = Session.Keys;
      foreach (string key in sessionKeys) {
         if (key == "CompanyState" || key == "ProductsFromCompany") {
            continue;
         }
         lastCompanySession[key] = Session[key];
         Session.Remove(key);
      }
      Session["CompanyState"].Add(Session["ProductsFromCompany"], lastCompanySession);

      var thisCompanySession = Session["CompanyState"][Request["ProductsFromCompany"]];
      foreach (string key in thisCompanySession.Keys) {
         Session[key] = thisCompanySession[key];
      }
      Session["CompanyState"].Remove(Request["ProductsFromCompany"]);

      Session["ProductsFromCompany"] = Request["ProductsFromCompany"];
   }
}
Mark Brackett
+1  A: 

I suggest writing your own session wrapper. Here's a good example from Martin in another question:

http://stackoverflow.com/questions/621549/how-to-access-session-variables-from-any-class-in-asp-net/621620#621620

Nick