I have a static class with several static methods. In these methods, I'm trying to access the current thread's context using HttpContext.Current. For example:
var userName = HttpContext.Current.User.Identity.Name;
However, when I do that, I receive a NullReferenceException, the infamous "Object reference not set to an instance of an object."
Any ideas?
It isn't clear from the original post that the HttpContext is actually what's missing. The HttpContext.User property can also be null at certain stages of the lifecycle, which would give you the exact same exception. All other issues aside, you need to step through the source and see which part of the expression is actually null.
When you write code that references static methods/properties like HttpContext.Current, you have to write them knowing that your code isn't guaranteed to be run when the methods/properties are actually available. Normally you have something like this:
static string GetCurrentUserName()
{
HttpContext context = HttpContext.Current;
if (context == null)
return null;
IPrincipal user = context.User;
if (user == null)
return null;
return user.Identity.Name;
}
Although I suspect that this wouldn't really solve your problem here, it would just get rid of the exception. The issue is more likely that you're calling this method at a time or place when the context is simply not available, such as on a background thread, static constructor or field initializer, or in the Application_BeginRequest method, or some similar place.
I might start by changing the static methods to instance methods of a class that depends on an HttpContext instance (i.e. taken in the constructor). It's easy to fool yourself into thinking that methods like GetCurrentUserName are simple "utility" methods, but they're really not, and it is generally invalid to be invoking a method that references HttpContext.Current through the static property from any place where you don't already have an instance reference to the same HttpContext (i.e. from the Page class). Odds are, if you start rewriting your classes like this:
public class UserResolver
{
private HttpContext context;
public UserResolver(HttpContext context)
{
if (context == null)
throw new ArgumentNullException("context");
this.context = context;
}
public string GetUserName()
{
return (context.User != null) ? context.User.Identity.Name : null;
}
}
...then you will likely find out very quickly where the chain is being broken, which will be the point at which you need to reference HttpContext.Current because you can't get it from anywhere else.
In this specific case, obviously, you can solve the problem just by taking the stack trace of the NullReferenceException to find out where/when the chain begins, so you don't have to make the changes I've described above - I'm simply recommending a general approach that will help reduce these sorts of "missing singleton" errors in the future.
I've run into this a few times, especially with static methods in another library and not my main project. I've resorted to passing the HttpContext to the static method as a param when nothing else seems to work.
Where exactly is the null exception being thrown? Have you debug and see what is null? Is the HttpContext.Current is null or the User?
Related
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.
im building a high traffic asp.net webforms site. I have componentized all the urls in a class. When looking up the current route of the request what is most efficient? Calling HttpContext.Current.Request.RequestContext.RouteData.Route or sending Page.RouteData.Route into the method that checks to see if we are at a certain route url. Example:
public static bool IsCurrentRoute(string routeName)
{
var route = HttpContext.Current.Request.RequestContext.RouteData.Route;
if (route == System.Web.Routing.RouteTable.Routes[routeName])
return true;
return false;
}
Page.Routedata just wraps around Context.RouteData.
Taken from JustDecompile
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Browsable(false)]
public RouteData RouteData
{
get
{
if (base.Context != null && base.Context.Request != null)
{
return base.Context.Request.RequestContext.RouteData;
}
return null;
}
}
So to answer your Question - no difference. Though you are less likely to get an exception if you use the Page method due to null checks. Though how often your context is going to be null is another question and is possibly a case you want to design for yourself if you think it has potential.
Also rather than accessing HttpContext.Current directly think about wrapping it in a HttpContextBase and using that.
An example can be found here http://www.agileatwork.com/bolt-on-multi-tenancy-in-asp-net-mvc-with-unity-and-nhibernate/ look at the line with
new HttpContextWrapper(HttpContext.Current)
I'm considering putting the ObjectContext inside HttpContext.Current so that all logic in the same request can access to it without having to open/destroy each time.
In ObjectContextManager class i created this.
get {
string ocKey = "ocm_" + HttpContext.Current.GetHashCode().ToString("x");
if (!HttpContext.Current.Items.Contains(ocKey))
HttpContext.Current.Items.Add(ocKey, new JEntities());
return HttpContext.Current.Items[ocKey] as JEntities;
}
and then I call this static property every time i execute a query on current request.
public static JEntities CurrentObjectContext {
get {
if (ObjectContextManager == null)
InstantiateObjectContextManager();
return ObjectContextManager.ObjectContext;
//return new JobsEntities();
}
}
But it gets disposed when it tries to execute second query.
Can you tell me where i went wrong?
Disposed? Your code has nothing to do with disposing. If you get disposed context it means you most probably enclosed the context retrieval into using and you disposed the instance yourselves.
We are using the approach described here to log our webservice errors with Elmah. And this actually works, but sadly the username beeing logged is empty.
We did some debugging and found, that when logging the error in the ErrorHandler the HttpContext.Current.User has the correct User set.
We also tried:
HttpContext context = HttpContext.Current;
ErrorLog.GetDefault(context).Log(new Error(pError, context));
and
ErrorLog.GetDefault(null).Log(new Error(pError));
Without success.
Any ideas on how we can make Elmah log the username?
On a sidenote, when logging the error directly within the Webservice, the username is logged as expected. But taking this approach is not very DRY.
Elmah take the user from Thread.CurrentPrincipal.Identity.Name and not from HttpContext.Current.User.
Since there ins't a convenient way to add custom data to Elmah, I would suggest recompiling the code, and calling HttpContext.Current.User instead.
This is a question that I see over and over again. While re-compiling the code is a possibility, I would rather suggest using features built into ELMAH already, as explained in my blog post Enrich ELMAH errors using error filtering hook.
In your case, setting the User property on all errors, can be achieved by adding the ErrorLog_Filtering-method:
void ErrorLog_Filtering(object sender, ExceptionFilterEventArgs args)
{
var httpContext = args.Context as HttpContext;
if (httpContext != null)
{
var error = new Error(args.Exception, httpContext);
error.User = httpContext.User.Identity.Name;
ErrorLog.GetDefault(httpContext).Log(error);
args.Dismiss();
}
}
As an alternative to recompile Elmah, we can use a global method, which populates Thread.CurrentPrincipal.Identity.Name before calling elmah.
public static void LogError(Exception exception, string username)
{
Thread.CurrentPrincipal = new GenericPrincipal(new GenericIdentity(username), new string[] {});
Elmah.ErrorSignal.FromCurrentContext().Raise(exception);
}
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.