Async task ASP.net HttpContext.Current.Items is empty - How do handle this? - asp.net

We are running a very large web application in asp.net MVC .NET 4.0. Recently we had an audit done and the performance team says that there were a lot of null reference exceptions.
So I started investigating it from the dumps and event viewer.
My understanding was as follows:
We are using Asyn Tasks in our controllers. We rely on HttpContext.Current.Items hashtable to store a lot of Application level values.
Task<Articles>.Factory.StartNew(() =>
{
System.Web.HttpContext.Current = ControllerContext.HttpContext.ApplicationInstance.Context;
var service = new ArticlesService(page);
return service.GetArticles();
}).ContinueWith(t => SetResult(t, "articles"));
So we are copying the context object onto the new thread that is spawned from Task factory. This context.Items is used again in the thread wherever necessary.
Say for ex:
public class SomeClass
{
internal static int StreamID
{
get
{
if (HttpContext.Current != null)
{
return (int)HttpContext.Current.Items["StreamID"];
}
else
{
return DEFAULT_STREAM_ID;
}
}
}
This runs fine as long as number of parallel requests are optimal. My questions are as follows:
1. When the load is more and there are too many parallel requests, I notice that HttpContext.Current.Items is empty. I am not able to figure out a reason for this and this causes all the null reference exceptions.
2. How do we make sure it is not null ? Any workaround if present ?
NOTE: I read through in StackOverflow and people have questions like HttpContext.Current is null - but in my case it is not null and its empty. I was reading one more article where the author says that sometimes request object is terminated and it may cause problems since dispose is already called on objects. I am doing a copy of Context object - its just a shallow copy and not a deep copy.

Your problem is that a instance members of the HttpContext are not thread safe:
Any public static (Shared in Visual Basic) members of this type are
thread safe. Any instance members are not guaranteed to be thread
safe.
When accessing it the way you are doing (multiple threads) you need to do your own synchronization.
static object locker = new object();
get
{
lock (locker)
{
if (HttpContext.Current != null)
{
return (int)HttpContext.Current.Items["StreamID"];
}
else
{
return DEFAULT_STREAM_ID;
}
}
}
MSDN: system.web.httpcontext

Maybe I'm misreading this, but I'm getting the impression that you're only trying to prevent the null reference error.
public class SomeClass
{
internal static int StreamID
{
get
{
int returnValue;
if (HttpContext.Current != null)
{
if(HttpContext.Current.Items["StreamID"] != null)
{
returnValue = (int)HttpContext.Current.Items["StreamID"];
}
else
{
returnValue = DEFAULT_STREAM_ID;
}
}
else
{
returnValue = DEFAULT_STREAM_ID;
}
return returnValue;
}
}
}

Related

ASP.NET HttpContext.Current inside Task.Run

I have a following code example that is used in ASP.NET MVC application.
The purpose of this code is to create "fire and forget" request for queuing some long running operation.
public JsonResult SomeAction() {
HttpContext ctx = HttpContext.Current;
Task.Run(() => {
HttpContext.Current = ctx;
//Other long running code here.
});
return Json("{ 'status': 'Work Queued' }");
}
I know this is not a good way for handling HttpContext.Current in asynchronous code, but currently our implementation not allows us to do something else.
I would like to understand how much this code is dangerous...
The question: Is it theoretically possible that setting the HttpContext inside Task.Run, will set the context to totally another request?
I think yes, but I'm not sure. How I understand it:
Request1 is handled with Thread1 from thread pool, then while Thread1 is handling absolutelly another request (Request2), the code inside Task.Run will set context from Request1 to Request2.
Maybe I am wrong, but my knowledge of ASP.NET internals not allows me to understand it correctly.
Thanks!
Let me bump a little internals on you:
public static HttpContext Current
{
get { return ContextBase.Current as HttpContext; }
set { ContextBase.Current = value; }
}
internal class ContextBase
{
internal static object Current
{
get { return CallContext.HostContext; }
set { CallContext.HostContext = value; }
}
}
public static object HostContext
{
get
{
var executionContextReader = Thread.CurrentThread.GetExecutionContextReader();
object hostContext = executionContextReader.IllogicalCallContext.HostContext;
if (hostContext == null)
{
hostContext = executionContextReader.LogicalCallContext.HostContext;
}
return hostContext;
}
set
{
var mutableExecutionContext = Thread.CurrentThread.GetMutableExecutionContext();
if (value is ILogicalThreadAffinative)
{
mutableExecutionContext.IllogicalCallContext.HostContext = null;
mutableExecutionContext.LogicalCallContext.HostContext = value;
return;
}
mutableExecutionContext.IllogicalCallContext.HostContext = value;
mutableExecutionContext.LogicalCallContext.HostContext = null;
}
}
So
var context = HttpContext.Current;
is equal to (pseudocode)
var context = CurrentThread.HttpContext;
and inside your Task.Run something like this happens
CurrentThread.HttpContext= context;
Task.Run will start new task with thread from thread pool. So you're telling that your new thread "HttpContext property" is reference to starter thread "HttpContext property" - so far so good (well with all the NullReference/Dispose exceptions you'll be facing after your starter thread finishes). Problem is if inside your
//Other long running code here.
You have statement like
var foo = await Bar();
Once you hit await, your current thread is returned to thread pool, and after IO finishes you grab new thread from thread pool - wonder what its "HttpContext property" is, right ? I don't know :) Most probably you'll end with NullReferenceException.
The issue you will run into here is that the HttpContext will dispose when the request is complete. Since you aren't awaiting the result of the Task.Run, you are essentially creating a race condition between the disposal of the HttpContext and it's usage within the task.
I'm pretty sure that the only issue your task will run into is a NullReferenceException or an ObjectDisposedException. I don't see any way where you could accidentally steal another request's context.
Also, unless you are handling & logging exceptions within your task, your fire and forget will throw and you'll never know about it.
Check out HangFire or consider using a message queue for processing backend jobs from a separate process.

ASP.NET, thread and connection-string stored in Session

My application can connect with multiple data bases (every data base have the same schema), I store the current DB, selected by user, in Session and encapsule access using a static property like:
public class DataBase
{
public static string CurrentDB
{
get
{
return HttpContext.Current.Session["CurrentDB"].ToString();
}
set
{
HttpContext.Current.Session["CurrentDB"] = value;
}
}
}
Other pieces of code access the static CurrentDB to determine what DB use.
Some actions start background process in a thread and it need access the CurrentDB to do some stuff. I'm thinking using something like this:
[ThreadStatic]
private static string _threadSafeCurrentDB;
public static string CurrentDB
{
get
{
if (HttpContext.Current == null)
return _threadSafeCurrentDB;
return HttpContext.Current.Session["CurrentDB"].ToString();
}
set
{
if (HttpContext.Current == null)
_threadSafeCurrentDB = value;
else
HttpContext.Current.Session["CurrentDB"] = value;
}
}
And start thread like:
public class MyThread
{
private string _currentDB;
private thread _thread;
public MyThread (string currentDB)
{
_currentDB = currentDB;
_thread = new Thread(DoWork);
}
public DoWork ()
{
DataBase.CurrentDB = _currentDB;
... //Do the work
}
}
This is a bad practice?
Actually, I think you should be able to determine which thread uses which database, so I would create a class inherited from Thread, but aware of the database it uses. It should have a getDB() method, so, if you need a new Thread which will use the same database as used in another specific Thread, you can use it. You should be able to setDB(db) of a Thread as well.
In the session you are using a current DB approach, which assumes that there is a single current DB. If this assumption describes the truth, then you can leave it as it is and update it whenever a new current DB is being used. If you have to use several databases in the same time, then you might want to have a Dictionary of databases, where the Value would be the DB and the Key would be some kind of code which would have a sematic meaning which you could use to be able to determine which instance is needed where.

nhibernate session manager implementation

I am new to Nhibernate and slowing working my way thru learning it. I tried to implement a session manager class to help me get the session for my db calls. Below is the code for it. Can someone please say if this is architecturally correct and foresee any issue of scalability or performance?
public static class StaticSessionManager
{
private static ISession _session;
public static ISession GetCurrentSession()
{
if (_session == null)
OpenSession();
return _session;
}
private static void OpenSession()
{
_session = (new Configuration()).Configure().BuildSessionFactory().OpenSession();
}
public static void CloseSession()
{
if (_session != null)
{
_session.Close();
_session = null;
}
}
}
and in my data provider class, I use the following code to get data.
public class GenericDataProvider<T>
{
NHibernate.ISession _session;
public GenericDataProvider()
{
this._session = StaticSessionManager.GetCurrentSession();
}
public T GetById(object id)
{
using (ITransaction tx = _session.BeginTransaction())
{
try
{
T obj = _session.Get<T>(id);
tx.Commit();
return obj;
}
catch (Exception ex)
{
tx.Rollback();
StaticSessionManager.CloseSession();
throw ex;
}
}
}
}
and then
public class UserDataProvider : GenericDataProvider<User>
{
public User GetUserById(Guid uid)
{
return GetById(uid)
}
}
Final usage in Page
UserDataProvider udp = new UserDataProvider();
User u = udp.GetUserById(xxxxxx-xxx-xxx);
Is this something that is correct? Will instantiating lot of data providers in a single page cause issues?
I am also facing an issue right now, where if I do a same read operation from multiple machines at the same time, Nhibernate throws random errors- which I think is due to transactions.
Please advice.
From what I can see you are building the session factory if you have a null session. You should only call BuildSessionFactory() once when the application starts.
Where you do this is up to you, some people build the SessionFactory inside Global.asax in the method application_start or in your case have a static property for sessionFactory instead of session in your StaticSessionManager class.
I suspect your errors are due to the fact that your session factory is being built multiple times!
Another point is that some people open a transaction _session.BeginTransaction() at the beginning of each request and either commit or rollback at the end of each request. This gives you a unit of work which means you can lose the
using (ITransaction tx = _session.BeginTransaction())
{
...
}
on every method. All of this is open for debate but I use this method for 99% of all my code with no trouble at all.

nHibernate logging with Log4Net, thread session issue

Hey there folks, having a little issue here which I'm trying to wrap my head around.
I'm currently starting out with nHibernate, such I have to due to work requirements, and am getting a little stuck with nHibernate's Sessions and multiple threads. Well the task I want to complete here is to have Log4Net log everything to the database, including nHibernate's debug/errors etc.
So what I did was create a very simple Log4Net:AppenderSkeleton class which fires perfectly when I need it. My intial issue I ran into was that when I used GetCurrentSession, obviously since Log4Net runs on a seperate thread(s), it errored out with the initial thread's session. So I figured that I had to create a new nHiberante Session for the Log4Net AppenderSkeleton class. The code is below:
public class Custom : AppenderSkeleton
{
protected override void Append(LoggingEvent loggingEvent)
{
if (loggingEvent != null)
{
using (ISession session = NHibernateHelper.OpenSession())
{
using (ITransaction tran = session.BeginTransaction())
{
Log data = new Log
{
Date = loggingEvent.TimeStamp,
Level = loggingEvent.Level.ToString(),
Logger = loggingEvent.LoggerName,
Thread = loggingEvent.ThreadName,
Message = loggingEvent.MessageObject.ToString()
};
if (loggingEvent.ExceptionObject != null)
{
data.Exception = loggingEvent.ExceptionObject.ToString();
}
session.Save(data);
tran.Commit();
}
}
}
}
Simple enough idea really, while it is at its basic form now, I will have more error checking info etc but for now the issue is that while this works perfectly it creates multiple sessions. That is, it creates a new session per error logged since I can't use GetCurrentSession as this will get the calling Session (the main program flow). I'm sure there is a way for me to create a session globally for Log4Net's thread, but I'm unsure gow to. Keeping in mind that I already bind a Session to the intial thread using the below in Global.asax (Application_BeginRequest):
ISession session = NHibernateHelper.OpenSession();
CurrentSessionContext.Bind(session);
And for those that will ask, the contents of my helper is below (this is in a DLL):
public static class NHibernateHelper
{
private static Configuration _nHibernateConfig;
private static ISessionFactory _nHibernateSessionFactory;
private static ISessionFactory BuildNHibernateSessionFactory
{
get
{
if (_nHibernateSessionFactory == null)
{
if (_nHibernateConfig == null)
{
BuildSessionFactory();
}
_nHibernateSessionFactory = _nHibernateConfig.BuildSessionFactory();
}
return _nHibernateSessionFactory;
}
}
private static Configuration BuildNHibernateConfig
{
get
{
if (_nHibernateConfig == null)
{
_nHibernateConfig = new ConfigurationBuilder().Build();
}
return _nHibernateConfig;
}
}
public static Configuration nHibernateConfig
{
get
{
return _nHibernateConfig;
}
}
public static ISessionFactory nHibernateSessionFactory
{
get
{
return _nHibernateSessionFactory;
}
}
public static Configuration BuildConfiguration()
{
return BuildNHibernateConfig;
}
public static ISessionFactory BuildSessionFactory()
{
return BuildNHibernateSessionFactory;
}
public static ISession OpenSession()
{
return _nHibernateSessionFactory.OpenSession();
}
public static ISession GetCurrentSession()
{
try
{
return _nHibernateSessionFactory.GetCurrentSession();
}
catch (HibernateException ex)
{
if(ex.Message == "No session bound to the current context")
{
// See if we can bind a session before complete failure
return _nHibernateSessionFactory.OpenSession();
}
else
{
throw;
}
}
catch (Exception ex)
{
throw;
}
}
}
I realise I could use the ADO appender in log4net but I wish to use nHibernate directly to add the data to the database. The reason being is I don't wish to mess around with connectionstrings etc when nHibernate is already working.
As always, any help is always appreciated.
-- Edit: --
So based on what I have been told initialy, I modified my custom Log4Net logger code. There are two versions below. My question, which is best or is there a better way?
The first, according to nHibernate Prof, creates only two sessions - The intial session is for the main program flow as intended and the second for my Log4Net logger code. Yet this has hundreds of enteries in the second session and complains about too many enteries and to many calls to the database.
The second, nHibernate prof shows many sessions, as many sessions as there are calls to the logger +1 for the main program flow. Yet no complaints anywhere on nHprof. Though I have this feeling that having that many sessions would have people frowning or is too much tasking.
Anyway the codes:
Code 1 -
protected override void Append(LoggingEvent loggingEvent)
{
if (!System.Web.HttpContext.Current.Items.Contains("Log4Net nHibernate Session"))
{
System.Web.HttpContext.Current.Items.Add("Log4Net nHibernate Session", NHibernateHelper.OpenStatelessSession());
}
IStatelessSession statelessSession = System.Web.HttpContext.Current.Items["Log4Net nHibernate Session"] as IStatelessSession;
if (statelessSession != null && loggingEvent != null)
{
using (ITransaction tran = statelessSession.BeginTransaction())
{
Log data = new Log
{
Id = Guid.NewGuid(),
Date = loggingEvent.TimeStamp,
Level = loggingEvent.Level.ToString(),
Logger = loggingEvent.LoggerName,
Thread = loggingEvent.ThreadName,
Message = loggingEvent.MessageObject.ToString()
};
if (loggingEvent.ExceptionObject != null)
{
data.Exception = loggingEvent.ExceptionObject.ToString();
}
statelessSession.Insert(data);
tran.Commit();
}
}
}
Code 2 -
protected override void Append(LoggingEvent loggingEvent)
{
if (loggingEvent != null)
{
using (IStatelessSession statelessSession = NHibernateHelper.OpenStatelessSession())
using (ITransaction tran = statelessSession.BeginTransaction())
{
Log data = new Log
{
Id = Guid.NewGuid(),
Date = loggingEvent.TimeStamp,
Level = loggingEvent.Level.ToString(),
Logger = loggingEvent.LoggerName,
Thread = loggingEvent.ThreadName,
Message = loggingEvent.MessageObject.ToString()
};
if (loggingEvent.ExceptionObject != null)
{
data.Exception = loggingEvent.ExceptionObject.ToString();
}
statelessSession.Insert(data);
tran.Commit();
}
}
}
You are right about creating a new session. You definitely don't want to share the same session across threads. In your logging instance I would even say to use an IStatelessSession. Also sessions should be fairly lightweight so I wouldn't worry about creating new sessions each time you log a statement.
NHibernate already uses Log4Net internally so you just need to enable the logger and use an AdoNetAppender to send the logs to your database.
<log4net>
<appender name="AdoNetAppender" type="log4net.Appender.AdoNetAppender">
...
</appender>
<logger name="NHibernate">
<level value="WARN"/>
<appender-ref ref="AdoNetAppender"/>
</logger>
</log4net>

ASP.NET Cache and File Dependancies

I want a ASP.NET cache item to be recycled when a specific file is touched, but the following code is not working:
HttpContext.Current.Cache.Insert(
"Key",
SomeObject,
new CacheDependency(Server.MapPath("SomeFile.txt")),
DateTime.MaxValue,
TimeSpan.Zero,
CacheItemPriority.High,
null);
"SomeFile.txt" does not seem to be checked when I'm hitting the cache, and modifying it does not cause this item to be invalidated.
What am I doing wrong?
Problem Solved:
This was a unique and interesting problem, so I'm going to document the cause and solution here as an Answer, for future searchers.
Something I left out in my question was that this cache insertion was happening in a service class implementing the singleton pattern.
In a nutshell:
public class Service
{
private static readonly Service _Instance = new Service();
static Service () { }
private Service () { }
public static Service Instance
{
get { return _Instance; }
}
// The expensive data that this service exposes
private someObject _data = null;
public someObject Data
{
get
{
if (_data == null)
loadData();
return _data;
}
}
private void loadData()
{
_data = GetFromCache();
if (_data == null)
{
// Get the data from our datasource
_data = ExpensiveDataSourceGet();
// Insert into Cache
HttpContext.Current.Cache.Insert(etc);
}
}
}
It may be obvious to some, but the culprit here is lazy loading within the singleton pattern. I was so caught up thinking that the cache wasn't being invalidated, that I forgot that the state of the singleton would be persisted for as long as the worker process was alive.
Cache.Insert has an overload that allows you to specify a event handler for when the cache item is removed, my first test was to create a dummy handler and set a breakpoint within it. Once I saw that the cache was being cleared, I realized that "_data" was not being reset to null, so the next request to the singleton loaded the lazy loaded value.
In a sense, I was double caching, though the singleton cache was very short lived, but long enough to be annoying.
The solution?
HttpContext.Current.Cache.Insert(
"Key",
SomeObject,
new CacheDependency(Server.MapPath("SomeFile.txt")),
DateTime.MaxValue,
TimeSpan.Zero,
CacheItemPriority.High,
delegate(string key, object value, CacheItemRemovedReason reason)
{
_data = null;
}
);
When the cache is cleared, the state within the singleton must also be cleared...problem solved.
Lesson learned here? Don't put state in a singleton.
Is ASP.NET running under an account with the proper permissions for the file specified in the CacheDependency? If not, then this might be one reason why the CacheDependency is not working properly.
I think you'll need to specify a path:
var d = new CacheDependency(Server.MapPath("SomeFile.txt"));
Prepend with ~\App_Data as needed.
Your code looks fine to me. However, beyond this snippet, anything could be going on.
Are you re-inserting on every postback by any chance?
Try making your cache dependency a class field, and checking it on every postback. Modify the file in between and see if it ever registers as "Changed". e.g.:
public partial class _Default : System.Web.UI.Page
{
CacheDependency dep;
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
dep = new CacheDependency(Server.MapPath("SomeFile.txt"));
HttpContext.Current.Cache.Insert(
"Key",
new Object(),
dep,
DateTime.MaxValue,
TimeSpan.Zero, CacheItemPriority.High, null);
}
if (dep.HasChanged)
Response.Write("changed!");
else
Response.Write("no change :("); }}
The only way I am able to reproduce this behavior is if the path provided to the constructor of CacheDependency does not exist. The CacheDependency will not throw an exception if the path doesn't exist, so it can be a little misleading.

Resources