I am having an intermittent problem with an ASP.NET 4.0 Web Forms application.
In Session_Start, I store the master page file path in the session:
protected void Session_Start(Object sender, EventArgs e)
{
// Not shown: get master page path from database
Session["MasterPagePath"] = PathIGotFromTheDatabase;
}
Then in my pages' Page_PreInit, I use that session value to set the Page.MasterPageFile
protected void Page_PreInit(object sender, EventArgs e)
{
Page.MasterPageFile = Session["MasterPagePath"] + #"/MyMasterPage.Master";
}
This works 99% of the time, but occasionally something breaks, and Session["MasterPagePath"] is null. The users report that they have to close all of their active browser sessions in order to use the site again.
My understanding is that since I populate the Session["MasterPagePath"] in Session_Start, it should always be available in my pages' PreInit method. If my session had expired, it would always be repopulated by Session_Start before Page_PreInit is called.
Am I missing something here? Under what conditions could what I describe happen? I am using InProc session state for what it's worth.
I don't think Session objects added on Application_Start are available at control level. Application["myObj"] would be available but for all users.
More information about the life cycle here.
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.)
In my Web Application, i am getting an error. "Session state has created session ID. But It can not save it because it was already flushed by application".
I googled for this issue and found that i have to store session id in Global.asax Session_Start Event.
string id = Session.SessionID;
But it was already exist in my application. I am not sure what else is causing issue. I was not using Response.Flush() also.
Can anyone please explain about this issue & fix for it.
That happens because sometimes (depending on the web.config configuration) the SessionID is not set in the cookie when Session_Start event executes in the global asax.
You encounter this error because at somepoint in the pagelifecycle a variable is set in the session. After the request ends, ASP.NET tries to set the SessionID too, but if the Request was flused (eg. this can be done by Response.Write or AJAX itself flushes the response) this exception will be thrown.
A simple fix would be (in the global.asax file):
void Session_Start(object sender, EventArgs e)
{
// Code that runs when a new session is started
//Ensure SessionID in order to prevent the folloing exception
//when the Application Pool Recycles
//[HttpException]: Session state has created a session id, but cannot
// save it because the response was already flushed by
string sessionId = Session.SessionID;
}
What is the best way to set up authentication against a custom database of users, in ASP.NET? My current setup is not great, and I am sure that there is a better way to do authentication, but all I can find are some articles that are seven or eight years old. My current setup is as follows:
Application uses the Windows username (via Windows Authentication), to work out whether a user is in a database of allowed users. This is done via a call to an Authenticate method in Page_Load.
If the user isn't in the allowed users, then the page redirects to a login screen.
The flaw with this method is that it calls:
Response.Redirect("~/login.aspx", false)
Which executes the entire body of the Page_load method. Is there a better way of doing authentication? Would something like custom Page classes, or HTTPModules do the job?
You could do your check earlier in the request, like in OnInit, or you could do something a little more robust, like implement your own membership provider: MSDN article / Video tutorial
Okay, so this is basically how I done it. I wrote this class that inherits from System.Web.UI.Page. I override the OnInit event and this is where the authentication happens (looks up the Windows username against the database of users). If the user doesn't get authenticated, isTerminating gets set to true, and the OnLoad event only runs if isTerminating is false. I tried leaving a Response.Redirect with the second parameter set to false on its own, but this still ran all the subsequent page events. (even with a call to HttpApplication.CompleteRequest())
public class BasePageClass : Page
{
private bool isTerminating = false;
protected override void OnInit(EventArgs e)
{
isTerminating = !AuthenticationManager.Authenticate();
base.OnInit(e);
}
protected override void OnLoad(EventArgs e)
{
if (!isTerminating)
{
base.OnLoad(e);
}
}
}
I have no idea whether not running the OnLoad event is the best thing to do, but it "seems" to work fine.
I have a problem. While migrating from classic pipeline mode to integrated pipeline mode at IIS 7.0 we encounter the problem :
Server Error in '/' Application.
Request is not available in this context...
We found solution for this problem at
mvolo.com/blogs/serverside/archive/2007/11/10/Integrated-mode-Request-is-not-available-in-this-context-in-Application_5F00_Start.aspx
As solution shortly ,in global.asax I must forward the application_start event to Application_BeginRequest event.
void Application_Start(object sender, EventArgs e) { // sender has type 'System.Web.HttpApplicationFactory' }
Application_BeginRequest(Object source, EventArgs e) | {
// sender has type 'System.Web.HttpApplication' }
Or another solution is, Application_Start event can start later then Application_BeginRequest .
any suggestions ?
I have no option like choosing "classic mode "
Move the code to Application_BeginRequest or Session_Start. You shouldn't use the Request object in Application_Start anyway.
The Request object contains information that is specific for one page request. It doesn't really make any sense to do anything with this information in the Application_Start event.
So, change you app pool mode to classic.
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