Global.asax, global variables, and editing with code - asp.net

I have two questions:
1) I have a few global variables for my website declared in my global.asax file. They are simple, small key/value pairs and there are only a few of them. Is this a good practice for values that are small and need to be accessed by almost every page on my website? Storing them in the database and requiring a db lookup seems as thought it would waste resources for values that don't change rapidly.
2) If one of the values changes once per week, is it possible to allow a user to edit the global variable with a form or some other means?
Example:
<script runat="server">
Overloads Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
Application("default_scoring_id") = 3
Application("week_current") = 3
End Sub
</script>
In the above example, the system needs to know which week (out of 15 of them) that the current date is within. So, once every Monday morning, the "week_current" value needs to change.
I can easily do this, but is there a way to give a user access to changing this value without touching the code?

The typical practice is to put these into the web.config, which can be edited. These <appSettings> values will then be available on every page, yet the file can be edited.

1) Good practice is usually to store those sort of values in the web.config. See here and here for some guides.
2) a) If you store this in the web.config it will be easily updatedable without the need for recompiling and the web application should immediatly pick up the changes.
b) Does updating something like the 'week number' really need to be a manual process? It sounds a bit error prone and i would suggest automating this if at all possible by calculating it based on the current date.

I would consider using the built in Cache (System.Web.Caching.Cache)
That way you can store them where ever you want (say in a database), change them from within the app easily, and have quick and cheap retrieval.
From within a class which inherits from BasePage use the given Cache object (eg Cache.Add(..)) and from elsewhere use HttpContext.Current.Cache (eg. HttpContext.Current.Cache.Remove(Key))

The other answers suggest the different ways this can be done, and must be done. But, even then if you want to allow the user to edit the global variable, you'll have to take a lock or Mutex on a shared object, change the value of your global variable, and release the lock or Mutex.
lock(globalsharedobject) //This is C# code.
{
Application("default_scoring_id") = 3;
}

Web.config is the .NET way, or ASP.NET way, it's not always the most efficent or most suitable.
You're Global.asax file is much more than some events, you can put static data in any class that subclass System.Web.HttpApplication and inherit that in your Global.asax file.
The HttpSessionState and HttpApplicationState are relics, from the classic ASP time and you would do well to avoid them, becuase the serve no real purpose.
Depending on the type (System.Type) of your objects you can design your own strongly typed objects that store information about your application and session, for application level data a bunch of static fields would be enough.
They have to be static becuase each HttpModule as well as HttpApplication instance are pooled object, so to avoid that confusion, make sure your persistent data is stored in a static or several static dicionaries. But be aware of concurrency issues when modyfying these collections. A good strategy is to lock the object, only for the duration you're modifying it and make sure you don't call any other code while modyfiny the collection, a simple swap idiom, might be helpful here, it's fast and it's a non-deadlock guarntee.

<%# Application Language="C#" %>
<script runat="server">
void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
}
void Application_End(object sender, EventArgs e)
{
// Code that runs on application shutdown
}
void Application_Error(object sender, EventArgs e)
{
// Code that runs when an unhandled error occurs
}
void Session_Start(object sender, EventArgs e)
{
// Code that runs when a new session is started
}
void Session_End(object sender, EventArgs e)
{
// Code that runs when a session ends.
// Note: The Session_End event is raised only when the sessionstate mode
// is set to InProc in the Web.config file. If session mode is set to StateServer
// or SQLServer, the event is not raised.
}
protected void Application_BeginRequest(Object sender, EventArgs e)
{
Context.Items.Add("Request_Start_Time", DateTime.Now);
}
protected void Application_EndRequest(Object sender, EventArgs e)
{
TimeSpan tsDuration = DateTime.Now.Subtract((DateTime)Context.Items["Request_Start_Time"]);
Context.Response.Write("<b>Request Processing Time: From Global.asax file " + tsDuration.ToString());
Application["time"] = tsDuration.ToString();
}
</script>

Related

AsyncLocal Value is Null after being set from within Application_BeginRequest()

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.

Context is null in the global.asax file

I have global.asax file, and I use this two functions to update cache value every 15 seconds by this code:
protected void Application_Start(object sender,EventArgs e)
{
Context.Cache.Insert("value","some value",null,DateTime.Now.AddSeconds(15),Cache.NoSlidingExpiration,CacheItemProirirt.Default,new CacheItemRemovedCallback(updating));
}
private void updating(string key,object value,CacheItemRemoveReason reason)
{
Context.Cache.Insert("value","updated value",null,DateTime.Now.AddSeconds(15),Cache.NoSlidingExpiration,CacheItemProirirt.Default,new CacheItemRemovedCallback(updating));
}
but it give me an NullReferenceException, and the context is null, please why I can't use context at the "updating" function?
Application_Start doesn't have any context.
The first event that does is Begin_Request.
Application_Start occurs when the particular website gets fired up for the first time, or after been recycled.
To keep the cache item renewed I suggest you do that in the Begin_Request, where you check if it's there, and if not, initiate it again.
This way it's only use memory while the site is being hit, otherwise not.

how to get a asp.net page event on user control

I have asp.net page which loads the user controls at run time I want to capture the save/cancel event of page on the user control.
User control will be load at run time so, for further development I don't want to change my code on page level.
Any help.
I solve it, with the help of observer pattern but with some more modification because my challenge is Page don't know which user control will be load on run-time in its place holder.
I create change-manager which holds the Observers references with their intended types(submit,cancel,viewonly)
Page holds the reference of change-manager and call the notify of change manager with (eventtype(enum),response(this.response object)) then change manager calls the update method In this method call Observers, kept in the dictionary of change-manager.
Iterate the dictionary and call their updates according to their interested event types.
Now I am free to remember the user-control reference of any type of casting.
see below example of get user control from page you need to use public modifier
///user control code see event is public
public void btn1_Click(object sender, EventArgs e)
{
///do you stuff
}
/// page code
protected void Page_Load(object sender, EventArgs e)
{
uc1.btn1_Click(sender,e);
}

Event handlers can only be bound to HttpApplication events during IHttpModule initialization

I am getting the following error
'Event handlers can only be bound to HttpApplication events during IHttpModule initialization.' at the following code (line in bold or double **)
protected void Application_BeginRequest(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication)sender;
**app.EndRequest += new EventHandler(Application_EndRequest);**
}
protected void Application_EndRequest(object sender, EventArgs e)
{
UnitOfWork.Commit();
}
which is mentioned in Global.asax file. Can anybody figure out, where I am lacking? Thanks.
The event handler lives the entire life of your application, so, you only need to add it once not add it every request. The event itself will fire every request, and the only-one handler will be called every time the event is raised.
Add it to Application_Start in global.asax not Application_BeginRequest, or better, create an HTTP Module instead.
Also, I think you may not even need an event handler. The method with current name will be called by convention similar to Page/Control AutoEventWireup (like Page_Load). Just note that this might have issues in ASP.NET MVC applications as some people report. So, my suggestion is to rename the function, add the event handler in Application_Start, or better in a new HTTP Module you create.
Try to comment out line marked with "**". Asp.Net will call appropriate methods by itself if followed by naming conventions: "{Scope}"_"{Event}", where "{Scope}" is Application if you want to handle application level events or "Session" if you want to handle session level events, and "{Event}" is name of the event, like Start, End, etc.
More info: http://msdn.microsoft.com/en-us/library/bb470252.aspx#Stages

need suggestion on C# coding (efficiency)

In my code I have a dropdown selection and upon selection from dropdown the code performs further processing and generates report/data.
Further, the entire program depends on data which is gathered from 3 different operation
Operation1: processing a text files of size of size > 6MB
Operation2: SQL Query to a DB (Query takes around 1 minute)
Operation 3: HTTP POST request to server (The main costliest part of the programe)
So, to make it efficient I am thinking to perform this operation only once and use the data for all the different selection from dropdown.
Question is how can I do so as below:
I can't put it in "page_load" event because every time page loads the operations will carry out
I can't put it inside "dropdownlist_selectedindexchanged" event because then it will be same as #1.
I thought of doing it in "page_load" as below
void Page_Load(object sender, EventArgs e)
{
if(!ispostback)
{
Operation1();
Operation2();
Operation3();
}
}
This is fine; the operations gets performed only once and I can use the data throughout, but then my page will take time to load as the operations takes time.
Is there any other way I can achieve what I want? Please let me know.
Thanks,
Rahul
If the data set will not change, you probably could manage to do it once at Application_Start().
Edit - something like this (typing from memory and away from VS, i do VB):
Protected void page_load(object sender, eventargs e)
{
// the name can be anything
if (!System.Web.HttpContext.Current.Session["data_cache_filled"])
{
// code to fill the cache.
// ...
//mark it as filled
System.Web.HttpContext.Current.Session["data_cache_filled"] = "yes";
}
}
Cache it. Using the CacheHelper class from here, you could do:
internal List<Employee> Operation1()
{
List<Employee> employeeData;
if (!CacheHelper.Get("employeeData", out employeeData))
{
employeeData = (from x in db.Employees select x).ToList(); // or whatever
CacheHelper.Add(employeeData, "employeeData");
}
return employeeData;
}

Resources