The following text is from the book I'm reading, 'MCTS Self-Paced Training Kit (Exam 70-515) Web Applications Development with ASP.NET 4". It gives the rundown of the Application Life Cycle.
A user first makes a request for a page in your site.
The request is routed to the processing pipeline, which forwards it to the ASP.NET runtime.
The ASP.NET runtime creates an instance of the ApplicationManager class; this class instance represents the .NET framework domain that will be used to execute requests for your application. An application domain isolates global variables from other applications and allows each application to load and unload separately, as required.
After the application domain has been created, an instance of the HostingEnvironment class is created. This class provides access to items inside the hosting environment, such as directory folders.
ASP.NET creates instances of the core objects that will be used to process the request. This includes HttpContext, HttpRequest, and HttpResponse objects.
ASP.NET creates an instance of the HttpApplication class (or an instance is reused). This class is also the base class for a site’s Global.asax file. You can use this class to trap events that happen when your application starts or stops. When ASP.NET creates an instance of HttpApplication, it also creates the modules configured for the application, such as the SessionStateModule.
Finally, ASP.NET processes request through the HttpApplication pipleline. This pipeline also includes a set of events for validating requests, mapping URLs, accessing the cache, and more.
The book then demonstrated an example of using the Global.asax file:
<script runat="server">
void Application_Start(object sender, EventArgs e)
{
Application["UsersOnline"] = 0;
}
void Session_Start(object sender, EventArgs e)
{
Application.Lock();
Application["UsersOnline"] = (int)Application["UsersOnline"] + 1;
Application.UnLock();
}
void Session_End(object sender, EventArgs e)
{
Application.Lock();
Application["UsersOnline"] = (int)Application["UsersOnline"] - 1;
Application.UnLock();
}
</script>
When does an application start? Whats the difference between session and application level? I'm rather confused on how this is managed. I thought that Application level classes "sat on top of" an AppDomain object, and the AppDomain contained information specific to that Session for that user. Could someone please explain how IIS manages Applicaiton level classes, and how an HttpApplication class sits under an AppDomain? Anything is appreciated.
Nothing lives outside an AppDomain so of course the HttpApplication has to be instantiated inside one.
Step 3 to 6 only happens ONCE in the lifetime of your application. When an ApplicationManager instance has been created it wont be created again for the next request. The same is for HostingEnvironment and HttpApplication. This means that values stored in the Application-collection will be remain there to get for all later requests during the lifetime of the application.
There is one AppDomain per application, not per session or per request.
Related
I am using the following code in the ASP.NET web forms Global.aspx page to count the number of current active users. It does work when I do local testing.
Understanding is that Application is an Application-level variable and can be accessed at the application level. So value stored in one session will be available in other sessions.
<%# Application Language="C#" %>
<script runat="server">
void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
Application["TotalOnlineUsers"] = 0;
}
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
Application.Lock();
Application["TotalOnlineUsers"] = (int)Application["TotalOnlineUsers"] + 1;
Application.UnLock();
}
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.
Application.Lock();
Application["TotalOnlineUsers"] = (int)Application["TotalOnlineUsers"] - 1;
Application.UnLock();
}
</script>
But while going through this link https://learn.microsoft.com/en-us/dotnet/api/system.web.httpapplication?redirectedfrom=MSDN&view=netframework-4.8, it says that member variables can be used to store per-request data.
Based on this statement and below complete paragraph, there can be multiple instances of HttpApplication for a single application, if not application will be super slow as one HttpApplication instance can process only one request at one point in time
Because of this, each HttpApplication will have its own Application variable and the count will be saved at HttpApplication level.
Instances of the HttpApplication class are created in the ASP.NET
infrastructure, not by the user directly. One instance of the
HttpApplication class is used to process many requests in its
lifetime. However, it can process only one request at a time. Thus,
member variables can be used to store per-request data.
An application raises events that can be handled by custom modules
that implement the IHttpModule interface or by event handler code that
is defined in the Global.asax file. Custom modules that implement the
IHttpModule interface can be put in the App_Code folder or in a DLL in
the Bin folder.
So, to get the exact count without any chance of miscalculating, should I use static variables?
I believe your code will work correctly, assuming you’re using InProc session state and aren’t running in a Web farm.
Confusingly, the word “application” can have three different meanings:
Your Web application (that is, the whole Web site)
One of the multiple HttpApplication instances that serve requests to your Web application
The HttpApplication.Application property
When the documentation says that the Application property returns “the current state of an application,” it means your Web application, not an individual HttpApplication instance.
It’s true that if you add a member variable to your Global class (which inherits from HttpApplication), then the member variable won’t be shared across HttpApplication instances. But the Application property is special: it returns the same HttpApplicationState object no matter which HttpApplication instance you use to access the property. Hence, any values you add via the Application property will be shared across all HttpApplication instances. (And that’s why you must call Lock and UnLock to synchronize access; you wouldn’t need to do so if the HttpApplicationState object weren’t shared.)
How to get caller IP addres in ASP.NET MVC4 Global.asax.cs Application_Start event ?
HttpContext.Current.Request object is not available there.
Thread.CurrentPrincipal.Identity exists.
I want to log user name and IP address which were used to start application.
MVC4 application runs in Windows and in Mono
As you can see by the ASP.NET Lifecycle on MSDN, the Application_Start event not only happens long before the AcquireRequestState event where the request object is built, it is also done out of band with the request lifecycle altogether. In other words, Application_Start occurs only once when the application starts or when the application pool recycles, not once per request.
So, the answer to your question is simply that you can't do that (unless of course you set a static variable in the Application_Start event and use either Application_BeginRequest as in Darin's answer or an MVC filter to actually do the logging).
But MVC includes authorization filters and action filters which are meant for implementing cross-cutting concerns such as logging and/or auditing of the current user's IP address. Authorization and action filters do not run until after the request object has been created.
The HttpContext is not available when the application starts. You could achieve that in the BeginRequest method in your global.asax:
private static bool initialized = false;
private static object syncRoot = new object();
protected void Application_BeginRequest()
{
if (!initialized)
{
lock (syncRoot)
{
if (!initialized)
{
// do your stuff with the user IP getting from the current context
initialized = true;
}
}
}
}
I want to change page content while it is going from the server because i need to add some advertisements inside the html elements that are advertisement holder.
protected void Application_PreSendRequestContent(object sender, EventArgs e) this is good but i couldn't get access to HttpContext . Should i, i don't know :)
But in this method:
protected void Application_EndRequest(object sender, EventArgs e) i could get the HttpContext but i couldn't find the server response in it.
How can i do this?
You might want to implement a HttpModule instead of global.asax. You can find an example of a module that manipulates the response in MSDN: Walkthrough: Creating and Registering a Custom HTTP Module
See also this page for some additional information (e.g. why a HttpModule instead of global.asax): HTTP Handlers and HTTP Modules Overview
To answer your comment: here are some reasons why to use a module instead of global.asax (have a look at the document linked above for more information):
You can implement much of the functionality of a module in the application's Global.asax file [...] however, modules have an advantage over the Global.asax file because they are encapsulated and can be created one time and used in many different applications.
In IIS 7.0, the integrated pipeline enables managed modules to subscribe to pipeline notifications for all requests, not just requests for ASP.NET resources.
You can enable/disable a module via web.config (without touching any code)
You should use a module whenever you must create code that depends on application events, and when the following conditions are true:
You want to re-use the module in other applications.
You want to avoid putting complex code in the Global.asax file.
The module applies to all requests in the pipeline (IIS 7.0 Integrated mode only).
protected void
Application_PreSendRequestContent(object
sender, EventArgs e) this is good but
i couldn't get access to HttpContext .
Should i, i don't know :)
You can always get access to the HttpContext for the current request by using HttpContext.Current
This question already has answers here:
Exclude certain pages from using a HTTPModule
(3 answers)
Closed 7 years ago.
I have an http module on a sharepoint site and this module instantiates a custom class and add it to the session and does other initial things for my site.
However, I'm noticing that the http module is being called for all request types (.aspx, .js, .png, .jpg).
Is there any way to have an http module only be called for .net specific page types?
In IIS you will set up the handler to be associated with your specific extension so the handler will only be applied to that extension. JavaScript files should not be processed.
I would also have a look at this article is you are looking at integrating your module/handler with SharePoint in any way.
While I do like the ease of deployment of this type of http handler (and the fact that you do not have to deploy a web.config entry for the handler), in cases where you may not want to use the _layouts directory OR you want to have a custom file extension, here is an alternative method that works as well (although it does take one manual configuration step in IIS so it may not be suitable for a "No Touch Deployment")
1) Create your http handler as you normally would for an asp.net application. You can add references to the SharePoint DLLs and interact with the object model since you are in the App Pool.
2) Add and entry into your web.config to register your handler and define the extension you are going to use. IE:
3) Define your custom extension in IIS through the IIS > Web SIte Properties > Home Directory > Configuration > Mappings
In this case, we defined a .proxy extension that the handler will pick up. Our handler is a .NET assembly so we need to add the mapping to route .proxy requests to the .net isapi dll (C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll).. also, make sure you UNcheck the "
From comments on http://msdn.microsoft.com/en-us/library/bb457204.aspx
I've done a bit more research and it seems there is no way to do what I'm intending.
I will have to check the request type and cancel from there.
Thanks everyone for their answers.
D
You can do this in a very lightweight manner using a HttpModule (before making any calls to the expensive SharePoint object model) by checking the extension in the content of the last Uri.Segments
void context_BeginRequest(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication)sender;
Uri uri = app.Request.Url;
string lastSegment = uri.Segments[uri.Segments.Length-1];
.. check your extension here an do nothing if it doesn't match.
..
}
We use this in our 'TinyURL' implementation for SharePoint to ensure the performance impact for regular URLs is almost 0.
Here is some simple example how to filter requests by extension... the example below exclude from the processing files with the specific extensions.
public class AuthenticationModule : IHttpModule
{
private static readonly List<string> extensionsToSkip = AuthenticationConfig.ExtensionsToSkip.Split('|').ToList();
// In the Init function, register for HttpApplication
// events by adding your handlers.
public void Init(HttpApplication application)
{
application.BeginRequest += new EventHandler(this.Application_BeginRequest);
application.EndRequest += new EventHandler(this.Application_EndRequest);
}
private void Application_BeginRequest(Object source, EventArgs e)
{
// we don't have to process all requests...
if (extensionsToSkip.Contains(Path.GetExtension(HttpContext.Current.Request.Url.LocalPath)))
return;
Trace.WriteLine("Application_BeginRequest: " + HttpContext.Current.Request.Url.AbsoluteUri);
}
private void Application_EndRequest(Object source, EventArgs e)
{
// we don't have to process all requests...
if (extensionsToSkip.Contains(Path.GetExtension(HttpContext.Current.Request.Url.LocalPath)))
return;
Trace.WriteLine("Application_BeginRequest: " + HttpContext.Current.Request.Url.AbsoluteUri);
}
}
In config file specify what extensions should be excluded and initiate the list of extensions in the module.
Is there a way to determine the number of users that have active sessions in an ASP.NET application? I have an admin/tools page in a particular application, and I would like to display info regarding all open sessions, such as the number of sessions, and perhaps the requesting machines' addresses, or other credential information for each user.
In global.aspx
void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
Application["OnlineUsers"] = 0;
}
void Session_Start(object sender, EventArgs e)
{
// Code that runs when a new session is started
Application.Lock();
Application["OnlineUsers"] = (int)Application["OnlineUsers"] + 1;
Application.UnLock();
}
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.
Application.Lock();
Application["OnlineUsers"] = (int)Application["OnlineUsers"] - 1;
Application.UnLock();
}
Note: The Application.Lock and Application.Unlock methods are used to prevent multiple threads from changing this variable at the same time.
In Web.config
Verify that the SessionState is "InProc" for this to work
<system.web>
<sessionState mode="InProc" cookieless="false" timeout="20" />
</system.web>
In your .aspx file
Visitors online: <%= Application["OnlineUsers"].ToString() %>
Note: Code was originally copied from http://www.aspdotnetfaq.com/Faq/How-to-show-number-of-online-users-visitors-for-ASP-NET-website.aspx (link no longer active)
ASP.NET Performance Counters like State Server Sessions Active (The number of active user sessions) should help you out. Then you can just read and display the performance counters from your admin page..
If you are using .net Membership you could use
Membership.GetNumberOfUsersOnline()
More about it: http://msdn.microsoft.com/en-us/library/system.web.security.membership.getnumberofusersonline.aspx
If you'd like to implement the same mechanism by yourself, you can define like a CurrentUserManager class and implement the singleton pattern here. This singleton object of class CurrentUserManager would be unique in the AppDomain. In this class you will create its self instance once, and you will prohibit the others from creating new instances of this class by hiding its constructor. Whenever a request comes to this object, that single instance will give the response. So, if you implement a list that keeps the records of every user (when a user comes in, you add him to the list; when he goes out, you remove him from the list). And lastly, if you want the current user count you could just ask the list count to this singleton object.
if you use sql server as the session state provider you can use this code to count the number of online users:
SELECT Count(*) As Onlines FROM ASPStateTempSessions WHERE Expires>getutcdate()
The way I've seen this done in the past is adding extra code to the Session_OnStart event in the Global.asax file to store the information in a session agnostic way, e.g. a database or the HttpApplicationState object. Depending upon your needs you could also use Session_OnEnd to remove this information.
You may want to initialise and clean up some of this information using the Application_Start and Application_End events.
The administration page can then read this information and display statistics etc.
This is explained in more depth at http://msdn.microsoft.com/en-us/library/ms178594.aspx and http://msdn.microsoft.com/en-us/library/ms178581.aspx.
You can use PerformanceCounter to get data from System.Diagnostics namespace. It allows you to get "Sessions Active" and much more. It allows you to get from local server as well as remote.
Here is an example of how to do it on local machine
void Main()
{
var pc = new PerformanceCounter("ASP.NET Applications", "Sessions Active", "__Total__");
Console.WriteLine(pc.NextValue());
}
or for remote server you would do:
void Main()
{
var pc = new PerformanceCounter("ASP.NET Applications", "Sessions Active", "__Total__", "ServerHostName.domain");
Console.WriteLine(pc.NextValue());
}
Performance Counters for ASP.NET provides full list of ASP.NET counters that you can monitor
Google Analytics comes with an API that can be implemented on your ASP.NET MVC Application.
It has RealTime functionality so the current amount of users on your website can be tracked and returned to your application.
Here's some information