views:

45

answers:

2

I have a very subtle bug that I'm having trouble identifying.

Background: We have 2 sites running off the same application on the same web server.

  • SiteA -- accessed by www.SiteA.com
  • SiteB -- accessed by www.SiteB.com

When the request first comes in, the siteId is identified based on the Host and stored in the Session as follows:

protected void Application_BeginRequest(object sender, EventArgs e)
{
    string host = Request.Url.Host;
    int siteId = new SiteManager().GetSiteByUrl(host).SiteId;
    if (SessionUser.Instance().SiteId != siteId)
    {
        SessionUser.Instance().SiteId = siteId;
    }
}

This ID used later in determining what data to retreive and to determine what style to present:

 // this happens during an initialization phase
 _siteConfiguration = _siteManager.GetSiteById(SessionUser.Instance().SiteId);

// then later:
private void SetPageTheme()
{
    string theme = null;

    switch (_siteConfiguration.SiteId)
    {
        case ConfigurationHelper.SITE.A:
            theme = "SiteATheme";
            break;
        case ConfigurationHelper.SITE.B:
            theme = "SiteBTheme";
            break;                
    }

    Page.Theme = theme;
}

The problem:

the problem I'm facing is if you load both sites at almost exactly the same time, i.e. within milliseconds, sometimes SiteA will load with SiteB's theme and vice versa. This doesn't happen very often but it has come to the attention of the client so it's now a problem.. Something is happening somewhere within those few milliseconds in the difference between SiteA loading and SiteB loading, and I don't know how to identify what that is.

The question:

Does anyone have any ideas what could be going wrong here? Something is getting confused somewhere. Is it IIS mixing up the requests? Is it the Session mixing up the site it's supposed to return the SiteId for?

If any more info is needed, I'll supply it.

Update:

For reference, this is the definition of SessionUser (basically, create a static instance of an object to get the SiteId value from the Session):

public class SessionUser
{
    private static SessionUser _instance;


    public int SiteId { get; set; }


    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    public static SessionUser Instance()
    {
        if (null == _instance)
        {
            _instance = new SessionUser();

            if (null != HttpContext.Current.Session)
            {
                if (null == HttpContext.Current.Session["User"])
                {
                    if (HttpContext.Current.Request.QueryString["sid"] != null)
                    {
                        int nSiteId = int.Parse(HttpContext.Current.Request.QueryString["sid"]);
                        _instance.SiteId = nSiteId;

                        HttpContext.Current.Session["User"] = _instance;

                    }
                }
                else
                {
                    _instance = (SessionUser) HttpContext.Current.Session["User"];
                }
            }
        }

        return _instance;
    }
}
+4  A: 

Without knowing what the 'SessionUser' class looks like, I can only speculate.

I will assume that SessionUser.Instance() returns a 'static' instance (or member rather).

Now, these will be shared across the entire application. So it makes sense that this cannot be shared between 2 sites.

I suggest you rather use HttpContext to store the setting at BeginRequest.

The code will then look like the following:

class SessionUser
{
  public static SessionUser Instance()
  {
    var ctx = HttpContext.Current;
    var su = ctx["SessionUser"] as SessionUser;
    if (su == null)
    {
      ctx["SessionUser"] = su = new SessionUser();
    }
    return su;
  }
}
leppie
Hi leppie, I've updated the question with the code that defines SessionUser.
DaveDev
@DaveDev: Replace that huge mess with mine :) And remove those `static` fields!
leppie
@leppie, I'll be more than happy to replace that code with yours - my colleague is a 'yoda programmer' so this is typical of what to expect from him! But I've a question - your code specifies `class SessionUser` should that be `public class SessionUser`?
DaveDev
@DaveDev: I meant, only replace the `Instance()` method.
leppie
A: 

I guess you could put the code that stores the current Site ID inside a lock block, but this may hamper performance. It makes more sense to use something not shared by both sites, as leppie says.

For the lock example:

if (SessionUser.Instance().SiteId != siteId)
{
    lock(somePrivateStaticObject)
    {
        if (SessionUser.Instance().SiteId != siteId)
        {
            SessionUser.Instance().SiteId = siteId;
        }
    }
}
Graham Clark
That's all good and well, but you will still be screwed on the next access. :)
leppie
@leppie: oh yeah, I didn't realise when this code was running. I guess it would need to be in Page_Init or something for this to work...
Graham Clark