Can I access virtual directory name in global.asax.cs?
-
18-09-2019 - |
Question
The property HttpContext.Current.Request.ApplicationPath
represents the virtual directory in IIS or WebDev.WebServer.
HttpContext.Current.Request.ApplicationPath evaluates to "/virtualdirectory"
This can be used in conjunction with VirtualPathUtility
to make a path root relative :
VirtualPathUtility.ToAbsolute("~/images/cat.jpg",
HttpContext.Current.Request.ApplicationPath)
// (this evaluates to "/virtualdirectory/images/cat.jpg")
In IIS6 and WebDev.WebServer the Request object is available in global.asax.cs
, but IIS7 complains that it is 'not available in current context'. Therefore the second line of code above works but not in IIS7.
The problem is I need to access the virtual directroy name within global.asax.cs
. I need it to construct some paths that are used in dynamically created CSS. Is there an alternative way to access this value?
Edit: This is the error you get in IIS 7 for calling HttpContext.Current.Request
in global.asax.cs under Application_Start:
HttpException (0x80004005): Request is not available in this context]
System.Web.HttpContext.get_Request() +8789264
Solution
Finally found the simple answer!
HttpRuntime.AppDomainAppVirtualPath
Available immediately during Application_Start
This is of the form /myapplication
including the /
prefix.
OTHER TIPS
Can you use ResolveUrl("~/images/cat.jpg") to build your path?
Edit: ResolveUrl is a method of Control, not just the Page class, so you can do it this way instead (bit ugly maybe):
System.Web.UI.Control c = new Control();
String s = c.ResolveUrl(@"~/images/cat.jpg");
Hmmm... I wasn't aware of the IIS7 change. I wonder if it wouldn't be simpler to defer this operation until you have got a page. For example, you could try putting something "once only" in Application_BeginRequest
or Session_Start
?
Or (completely untested) for a self-unsubscribing hook:
public override void Init() {
base.Init();
EventHandler handler = null;
handler = delegate {
// do stuff, once only
this.BeginRequest -= handler;
};
this.BeginRequest += handler;
}
The trick is doing it once only (if multiple requests arrive at once); perhaps a static ctor? For example, I think this fires once only, and only when there is a page available in context:
static class DelayedLoader {
static DelayedLoader() {
string s = VirtualPathUtility.ToAbsolute("~/images/cat.jpg",
HttpContext.Current.Request.ApplicationPath);
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static void Init() { }
}
public override void Init() {
base.Init();
EventHandler handler = null;
handler = delegate {
DelayedLoader.Init();
this.BeginRequest -= handler;
};
this.BeginRequest += handler;
}
This is the best I came up with : Application_BeginRequest (via mark)
I use asax so rarely that I had temporarily forgotten you get different events with it. Until now I'd been creating the CSS sprites in Application_Start
. Moving it to BeginRequest was the best I could come up with.
One boolean check for every request is negligible, but would be nice if there is a different way.
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
}
protected void Application_BeginRequest()
{
if (!_initialized)
{
lock (thisLock)
{
_initialized = true;
GenerateCSSSprites();
}
}
}
I had this problem too when switching to IIS7 but I was able to refactor out the need for Request. Which is what this guy also suggests and provides a workaround if you can't.