Register Type to Handle Case When HttpContext.Current Doesn't Exist

Oct 30, 2013 at 5:15 PM
Hi, I have an ASP.NET MVC application and in my Application_Start event I have the following code:
container.RegisterType<ISessionFactory>(new ContainerControlledLifetimeManager(), new InjectionFactory(c => {
    return BuildSessionFactory();
}));
container.RegisterType<ISession>(new PerRequestLifetimeManager<ISession>(), new InjectionFactory(c => {
    return c.Resolve<ISessionFactory>().OpenSession();
}));
The session factory (ISessionFactory) lives for the length of the application. The session (ISession) lives for the length of the ASP.NET request. I also dispose the session in the Application_EndRequest event. This allows me to inject the ISession throughout my application and it works as expected.

I'm now trying to build task scheduling into my application. I have added the following code into the Application_Start event:
var timer = new System.Timers.Timer(5000);
timer.Elapsed += (sender, e) => {
    var thread = new Thread(new ThreadStart(() => {
        var session = DependencyResolver.Current.GetService<ISession>();

        ...
    }));
    thread.Start();
};
timer.Enabled = true;
This should run every 5 seconds. My issue arises when it tries to resolve the ISession as it tries to resolve it in a thread where HttpContext.Current is null and therefore an exception is thrown. I was wondering how I should register the session to handle this scenario. I'd appreciate the help.

Thanks

Here's my PerRequestLifetimeManager class incase it helps:
public class PerRequestLifetimeManager<T> : LifetimeManager, IDisposable {
    public override object GetValue() {
        return HttpContext.Current.Items[typeof(T).AssemblyQualifiedName];
    }

    public override void RemoveValue() {
        HttpContext.Current.Items.Remove(typeof(T).AssemblyQualifiedName);
    }

    public override void SetValue(object newValue) {
        HttpContext.Current.Items[typeof(T).AssemblyQualifiedName] = newValue;
    }

    public void Dispose() {
        RemoveValue();
    }
}
Oct 31, 2013 at 6:19 AM
Edited Oct 31, 2013 at 6:41 AM
That makes sense. The child thread won't have an HttpContext. Even if you pass in the HttpContext it could outlive the request lifetime and be "Disposed" or garbage collected. See Working With HttpContext.Current for a relatively short and readable post on the topic.

Probably the best option (if using threads) would be to extract the information you need from the context, copy (clone) it and pass it to your thread or create your own session object(s) that are independent of the actual request. (I'm assuming here you want to do something with session on every request.)

Re-reading your question I'm a bit confused about the intent. It looks like every 5 seconds you want to do something with a session? What ISession are you trying to resolve? Perhaps an HttpModule would better suit your needs if you are trying to do something with session on a per request basis? The current code will result in timer.Elapsed being invoked on a thread pool thread with a null HttpContext.

In my opinion if you are doing threading/long running tasks within IIS then you should step back and take a look at what your needs are and see if there are other ways of addressing them (e.g. separate service).

~~
Randy Levy
entlib.support@live.com
Enterprise Library support engineer
Support How-to
Oct 31, 2013 at 9:46 AM
Edited Oct 31, 2013 at 4:12 PM
Thanks for your response. I have a modular application and the goal is to add multiple services that implement an interface (e.g. IScheduledTask) and then execute the code at a particular time. The timer would run at intervals and detect which tasks to run.

An example of a task is reminder users to pay for their member as I would like this automated. The session (NHibernate) is used to query the database for the user's. What I guess i'm after is a way to store the session in a cache that exists for the length of the thread instead of using the HttpContext.Current.Items collection which exists for the length of the request when HttpContext.Current returns null.

EDIT:

After further research. I have modified my PerRequestLifetimeManager:
public class PerRequestLifetimeManager<T> : PerThreadLifetimeManager {
    public PerRequestLifetimeManager() : base() { }

    public override object GetValue() {
        if (HttpContext.Current == null)
            return base.GetValue();
        else
            return  HttpContext.Current.Items[typeof(T).AssemblyQualifiedName];
    }

    public override void RemoveValue() {
        if (HttpContext.Current == null)
            base.RemoveValue();
        else
            HttpContext.Current.Items.Remove(typeof(T).AssemblyQualifiedName);
    }

    public override void SetValue(object newValue) {
        if (HttpContext.Current == null)
            base.SetValue(newValue);
        else
            HttpContext.Current.Items[typeof(T).AssemblyQualifiedName] = newValue;
    }
}
Notice how it inherits from PerThreadLifetimeManager. I hoped that once the task is finished it would release the session. However using the NHibernate profiler I can see that all the queries are executed against the same session. I'm not quite sure why this happens but i'd appreciate any advice. Thanks
Nov 1, 2013 at 4:21 AM
Are you using Unity 3? Unity 3 has a PerRequestLifetimeManager that might suit your purposes (for web requests). Note that PerThreadLifetimeManager's RemoveValue is not implemented and Unity actually does not ever call RemoveValue. See Writing Custom Lifetime Managers for more information. At some point (end of the thread's work?) I would think you would want to call Dispose.

~~
Randy Levy
entlib.support@live.com
Enterprise Library support engineer
Support How-to
Nov 1, 2013 at 10:50 AM
Edited Nov 5, 2013 at 1:36 PM
Yeah i'm using Unity 3. Thanks I wasn't aware this had been bundled into version 3.

I posted the question on stack overflow and someone helped me out. Here's a link in-case anyone is interested.

http://stackoverflow.com/questions/19693635/dependency-injection-to-handle-case-when-httpcontext-current-doesnt-exist

Thanks for your help.
Marked as answer by nfplee on 11/5/2013 at 5:36 AM