I have read source code of one web application and seen that it used Cache object( Web.Caching.Cache) for caching data. In code behind files( aspx.cs files), it uses Page.Cache to get Cache while in others class-define files it uses HttpContext.Current.Cache to get Cache. I wonder why it not use the same option to get Cache. Can someone explain differences between Page.Cache and HttpContext.Current.Cache? Why use each one for each context above. Can I use Page.Cache or HttpContext.Current.Cache for both contexts above?
Thanks in advance.
There is no difference, the former uses the current page instance and it's Cache property, the latter uses the static approach via HttpContext.Current.Cache which would work also in a static method without page instance.
Both are referring to the same application cache.
So you can get the Cache via Page, for example in Page_Load:
protected void Page_load(Object sender, EventArgs e)
{
System.Web.Caching.Cache cache = this.Cache;
}
or in a static method (which is used in a HttpContext) via HttpContext.Current:
static void Foo()
{
var context = HttpContext.Current;
if (context != null)
{
System.Web.Caching.Cache cache = context.Cache;
}
}
Related
In the following example, I am setting a value to an AsyncLocal<string> variable on my HttpApplication subclass (i.e. Global.asax) from within Application_BeginRequest():
public class Global : System.Web.HttpApplication
{
public static AsyncLocal<string> AsyncLocalState = new AsyncLocal<string>();
protected void Application_BeginRequest(object sender, EventArgs e)
{
AsyncLocalState.Value = HttpContext.Current.Request.Path;
}
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
var path = AsyncLocalState.Value;
}
protected void Application_EndRequest(object sender, EventArgs e)
{
var path = AsyncLocalState.Value;
}
}
Later on, I will attempt to access the value of this AsyncLocal variable from within a handler, such as an MVC action method, or even just a plain IHttpHandler.
If I send a large enough request (e.g. a POST with more than 15KB of data -- the larger the request, the easier it is to observe), there is a very good chance that the value of AsyncLocalState is NULL when accessed from a handler even though it was set on BeginRequest.
This is reproducible from a brand-new ASP.NET project without any other libraries/modules/handlers loaded.
Is this a bug? Or maybe I'm doing something wrong? Or is ASP.NET just too unstable for this?
Addition note: the exact same behavior is observed if I instead use CallContext.LogicalGetData/CallContext.LogicalSetData.
Platform: ASP.NET, .NET 4.6.2, on Windows 7
Update: After trying to dig around, I've found a lot of references to, but nothing authoritatively saying that the ExecutionContext does not flow between ASP.NET pipeline events (except when it does?). And both AsyncLocal and the logical call context are based on the ExecutionContext.
The closest thing to an authoritative answer is this comment by David Fowl on GitHub.
The ExecutionContext does not flow between ASP.NET pipeline events if these events do not execute synchronously. Therefore, don't use AsyncLocal or the logical CallContext to persist state; use HttpContext.Items.
Update: .NET 4.7.1 adds a new callback method, HttpApplication.OnExecuteRequestStep, which per documentation "provides extensibility to the ASP.NET pipeline to make it easy for developers to implement features in ambient context scenarios and build libraries that care about ASP.NET execution flow (for example, tracing, profiling, diagnostics, and transactions)."
This is precisely what someone would need in order to restore the AsyncLocal state or the logical CallContext between ASP.NET pipeline events.
This article explains that the PreRequestHandlerExecute event does not fire for PageMethod calls for whatever reason. However, I'm trying to use that event to populate the Principal object with the user's permissions so they can be checked within any web request (PageMethod call or not). I'm caching the permissions in the Session, so I need an event that fires whenever a PageMethod is called, and I need to have access to the Session. This way I can populate the Principal object with the security permissions cached in the session, and User.IsInRole() calls will work as expected. What event can I use?
You should implement an authorization module that will be run with every request that goes up to the server. This way you are able to authorize your principal for any request that come up to the server (page request, method, etc.)
public class AuthorizationModule : IHttpModule, IRequiresSessionState
{
//not going to implement it fully, might not compile
public void Init( HttpApplication context )
{
//you'll prolly want to hook up to the acquire request state event, but read up to make sure this is the one you want on the msdn
context.AcquireRequestState += AuthorizeRequest;
}
public void AuthorizeRequest( HttpContextBase httpContext )
{
// do you work in here
// you can redirect them wherever if they don't have permssion, log them out, etc
}
}
}
After you've crated the module, you'll need to hook it up in the web.config. Your type should include the namespace if it has one.
<httpModules>
<add name="AuthorizationModule" type="AuthorizationModule"/>
</httpModules>
I hope this helps.
You can use the Application_OnPostAuthenticateRequest as shown below (assuming you are using Forms Authentication. Else, pls replace the code with your Authentication mechanism):
public void Application_OnPostAuthenticateRequest(object sender, EventArgs e)
{
IPrincipal usr = HttpContext.Current.User;
if (usr.Identity.IsAuthenticated && usr.Identity.AuthenticationType == "Forms")
{
var fIdent = (FormsIdentity)usr.Identity;
var ci = new CustomIdentity(fIdent.Ticket);
var p = new CustomPrincipal(ci);
HttpContext.Current.User = p;
Thread.CurrentPrincipal = p;
}
}
Page Methods are static, and bypass the normal Page lifecycle, its objects and its events. The best you can do is pass authentication information as parameters to the Page Method itself.
From my point of view, you can:
1.- Use a common method you can call from every page method server code that have access to Session variables. Please refer to:
http://mattberseth.com/blog/2007/06/aspnet_ajax_use_pagemethods_pr.html
2.- Try to capture a similar behaviour later using __doPostBack() function to run server code. See if this work for you to capture page method async posbacks:
http://www.dotnetcurry.com/ShowArticle.aspx?ID=256
Hope that helps,
I've written a singleton class that exposes the web.config properties in a nice get property kind of way.
I want a Load method to parse the data in the config and set the public properties, and I want to throw exceptions (so they are logged in the EventLog) when a configuration key is missing or can't be parsed.
I tried placing the Load() code in Application_Start of the global.asax but then remembered this will only be run once, or until the application restarts.
Where is the best place to put code that you need to run 'everytime' your site is started/run by the user? I basically want the website to stop functioning if certain config properties cannot be loaded.
Thanks.
When you change your web.config file, the application pool is recycled. This means that the next hit will cause your Application_Start method to be called.
Altering the following files will also
trigger an immediate restart of the
application pool:
- web.config
- machine.config
- global.asax
- Anything in the bin directory or it's sub-directories
On that basis, as soon as your configuration is changed, it will be reloaded the next time a user hits the site, which should resolve the problem with the minimum number of configuration reloads, as opposed to reloading whenever a session starts for example. Therefore, you can do this (in your global.asax):
static bool configValid = false;
void Application_BeginRequest(object sender, EventArgs e)
{
HttpContext context = base.Context;
HttpResponse response = context.Response;
HttpRequest request = context.Request;
// Redirect users to an alternate page if the current config is invalid
// I happen to pass the Url they were attempting to access in the query string
// that way you can give them a "try again" link
if ((!configValid) && (!request.Url.ToString().Contains("BadConfig.aspx")))
{
response.Redirect("BadConfig.aspx?originalUrl=" + context.Server.UrlEncode(request.Url.ToString()));
}
}
void Application_Start(object sender, EventArgs e)
{
// Load config and determine if it's valid, thus setting configValid to true/false
//
//
configValid = false;
}
I have the following assemblies in my ASP.NET app:
Website - this is an ASP.NET website
ClassLib - this is just a class lib that contains all the business logic
Class Lib needs to interact with the HttpContext Session and Request objects. This is a code upgrade from an old ASP app, where I've hoovered all the VBScript that contained the logic and put it into VB.NET. We simply didn't have time to rewrite.
Instead of ClassLib interacting directly with HttpContext, which I thought was BAD and also prevented us from unit testing, I introduced the following abstraction layer:
Public Class Request
Private Shared _requestWrapper as IRequestWrapper
Public Shared ReadOnly Property RequestWrapper()
Get
If _requestWrapper Is Nothing Then
Throw New Exception("_requestWrapper is null. Make sure InitRequest() is called with valid parameters")
End If
Return _requestWrapper
End Get
End Property
Public Shared Sub InitRequest(ByRef requestWrapper As IRequestWrapper)
_requestWrapper = requestWrapper
End Sub
Public Shared Function GetVal(ByVal key As String) As Object
Return RequestWrapper.GetVal(key)
End Function
etc.
This means in the unit tests I can supply my own MockRequest object into this Request class, which is just a simple NameValue collection. The code in the ClassLib and the Website then simply use the Request class and are none the wiser that it isn't coming from the HttpContext, but rather this mock class.
When it comes to the real deal, I simply have the following (C#) class:
public class RealRequest : IRequestWrapper
{
public void Initialize(HttpContext context)
{
}
#region Implementation of IRequestWrapper
public object GetVal(string index)
{
return HttpContext.Current.Request[index];
}
etc.
This is initialised in Session_Start of global.asax in the Website, as follows:
protected void Session_Start(object sender, EventArgs e)
{
IRequestWrapper requestWrapper = new RealRequest();
WebSession.Request.InitRequest(ref requestWrapper);
}
I think this is similar to the Static Gateway pattern.
Now, I am aware of singletons and static vars in a multi threaded environment such as ASP.NET, but this is slightly different. When it gets down to the RequestWrapper.GetVal(), its actually going to the HttpContext for that running thread - and pulling the value from that.
Certainly, any concurrent tests that we do with multiple users hitting the same server have never shown up any strange behaviour.
I'm just looking for re-assurance that this is a sound design, and if not why not?
Thanks
Duncan
This is fine. We have a very similar case in our applications that either uses HttpContext if it exists or fake implementations otherwise.
The one thing to watch out for is that there is a very specific instance where HttpContext.Current will return a value but HttpContext.Current.Request will throw an exception when triggered by the Application_Start event. In framework code, you really don't know (or want to know) what triggered the call though.
Workaround for HttpContext.HideRequestResponse being internal? Detect if HttpContext.Request is really available?
Let's say I have a list of categories for navigation on a web app. Rather than selecting from the database for every user, should I add a function call in the application_onStart of the global.asax to fetch that data into an array or collection that is re-used over and over. If my data does not change at all - (Edit - very often), would this be the best way?
You can store the list items in the Application object. You are right about the application_onStart(), simply call a method that will read your database and load the data to the Application object.
In Global.asax
public class Global : System.Web.HttpApplication
{
// The key to use in the rest of the web site to retrieve the list
public const string ListItemKey = "MyListItemKey";
// a class to hold your actual values. This can be use with databinding
public class NameValuePair
{
public string Name{get;set;}
public string Value{get;set;}
public NameValuePair(string Name, string Value)
{
this.Name = Name;
this.Value = Value;
}
}
protected void Application_Start(object sender, EventArgs e)
{
InitializeApplicationVariables();
}
protected void InitializeApplicationVariables()
{
List<NameValuePair> listItems = new List<NameValuePair>();
// replace the following code with your data access code and fill in the collection
listItems.Add( new NameValuePair("Item1", "1"));
listItems.Add( new NameValuePair("Item2", "2"));
listItems.Add( new NameValuePair("Item3", "3"));
// load it in the application object
Application[ListItemKey] = listItems;
}
}
Now you can access your list in the rest of the project. For example, in default.aspx to load the values in a DropDownList:
<asp:DropDownList runat="server" ID="ddList" DataTextField="Name" DataValueField="Value"></asp:DropDownList>
And in the code-behind file:
protected override void OnPreInit(EventArgs e)
{
ddList.DataSource = Application[Global.ListItemKey];
ddList.DataBind();
base.OnPreInit(e);
}
Premature optimization is evil. That being a given, if you are having performance problems in your application and you have "static" information that you want to display to your users you can definitely load that data once into an array and store it in the Application Object. You want to be careful and balance memory usage with optimization.
The problem you run into then is changing the database stored info and not having it update the cached version. You would probably want to have some kind of last changed date in the database that you store in the state along with the cached data. That way you can query for the greatest changed time and compare it. If it's newer than your cached date then you dump it and reload.
If it never changes, it probably doesn't need to be in the database.
If there isn't much data, you might put it in the web.config, or as en Enum in your code.
Fetching all may be expensive. Try lazy init, fetch only request data and then store it in the cache variable.
In an application variable.
Remember that an application variable can contain an object in .Net, so you can instantiate the object in the global.asax and then use it directly in the code.
Since application variables are in-memory they are very quick (vs having to call a database)
For example:
// Create and load the profile object
x_siteprofile thisprofile = new x_siteprofile(Server.MapPath(String.Concat(config.Path, "templates/")));
Application.Add("SiteProfileX", thisprofile);
I would store the data in the Application Cache (Cache object). And I wouldn't preload it, I would load it the first time it is requested. What is nice about the Cache is that ASP.NET will manage it including giving you options for expiring the cache entry after file changes, a time period, etc. And since the items are kept in memory, the objects don't get serialized/deserialized so usage is very fast.
Usage is straightforward. There are Get and Add methods on the Cache object to retrieve and add items to the cache respectively.
I use a static collection as a private with a public static property that either loads or gets it from the database.
Additionally you can add a static datetime that gets set when it gets loaded and if you call for it, past a certain amount of time, clear the static collection and requery it.
Caching is the way to go. And if your into design patterns, take a look at the singleton.
Overall however I'm not sure I'd be worried about it until you notice performance degradation.