Static class member is not shared between different users on different IP - asp.net

I have written a static class and suppose to share it's members between all sessions. it works for all the browsers running on the same cumputer but the data is not shared between different users from different location. my website is written with ASP.NET
this is my class
public static class GlobalPool
{
public static List<string> OnlineUsers;
}
and I instantiate the OnlineUSers property in Global.asax as
void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
GlobalPool.OnlineUsers = new List<string>(100);
}
I add username whenever a user logs into my website:
public void Login(int aMemberSerial)
{
User = new MemberDataAccess().Read(aMemberSerial);
new MemberDataAccess().Login(User);
GlobalPool.OnlineUsers.Add(User.Username);
Message = PostBusiness.NewPost(User);
}

This is not robust code. ASP.NET application routinely recycle (the App Pool) and so all your data will go away.
The best way to approach this is to store the list in a database, and when a Session expires, remove that user from the data store.

Related

ASP.NET Custom SQLite DeleteExpiredSessions()

The SQLiteSessionStateStoreProvider.DeleteExpiredSessions() is not running automatically on the IIS 7 at any interval. I understand that if my database for session state storage were on inProc, it would be managed automatically at runtime, and if it were on an actual SQL database, there would be a script that would be ran by the SQL agent on an interval to clear expired sessions. However, my issue is that despite having the function defined in the library supplied here: https://github.com/micahlmartin/SQLiteSessionStateStore (renamed to DeleteExpiredSessions() and made public) I am unsure how to have the IIS server call this function, or if I need to implement a scheduled function of my own on the back-end to call this. On the IIS server the session state is set to custom, at 20 minute intervals expiration. It is set to cookie mode, as is the same settings on the web.config for the server. The database is currently holding data from the past 7 days for session states, and I am unsure how to interact with it from the back-end, or if I am missing some service or implementation. Anyone with experience running a custom SessionStateProvider via SQLite through a C# MVC should know how to resolve this, thank you.
Try something like this from your Global.asax:
It runs DeleteExpiredSessions() after about 10 minutes. In the interim it checks every 0.25 seconds to see if the application is shutting down. If it is shutting down then it stops.
public class Global : System.Web.HttpApplication
{
public static Task SessionCleanup { get; private set; } = null;
public static bool ApplicationEnding { get; private set; }
public static void PeriodicallyRunSesssionCleanup()
{
int cleanupInterval = 600000;//10 minutes
int sleepInterval = 250;// 1/4 second
while(!ApplicationEnding && cleanupInterval > 0)
{
Thread.Sleep(sleepInterval);
cleanupInterval -= sleepInterval;
}
if(!ApplicationEnding)
{
throw new NotImplementedException("Place your code that cleans up sessions here to have it run every 10 minutes.");
SessionCleanup = new Task(PeriodicallyRunSesssionCleanup);
SessionCleanup.Start();
}
}
protected void Application_Start(object sender, EventArgs e)
{
SessionCleanup = new Task(PeriodicallyRunSesssionCleanup);
SessionCleanup.Start();
}
protected void Application_End(object sender, EventArgs e)
{
ApplicationEnding = true;
if (SessionCleanup != null) SessionCleanup.Wait();
}
}

How to count the number of times a website is visited and the number of online users in MVC

I need to find out the number of times a website has been visited and how many online users it has.
My code is this:
Global.asax
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
// Code that runs on application startup
Application["SiteVisitedCounter"] = 0;
//to check how many users have currently opened our site write the following line
Application["OnlineUserCounter"] = 0;
}
void Session_Start(object sender, EventArgs e)
{
// Code that runs when a new session is started
Application.Lock();
Application["SiteVisitedCounter"] = Convert.ToInt32(Application["SiteVisitedCounter"]) + 1;
//to check how many users have currently opened our site write the following line
Application["OnlineUserCounter"] = Convert.ToInt32(Application["OnlineUserCounter"]) + 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["OnlineUserCounter"] = Convert.ToInt32(Application["OnlineUserCounter"]) - 1;
Application.UnLock();
}
The HomeController class contains the following code.
I got an error on System.Net.Mime.MediaTypeNames.Application.
[HttpGet]
public ActionResult Index()
{
ViewBag.noofsitesvisited = "No of times site visited=" + System.Net.Mime.MediaTypeNames.Application["SiteVisitedCounter"].ToString();
ViewBag.onlineusers = "No of users online on the site=" + System.Net.Mime.MediaTypeNames.Application["OnlineUserCounter"].ToString();
}
You don't want to do it this way. One, reading and writing data from anything global in web environment is dangerous and inadvisable from the get go, and two, this will only store the count while the AppPool is active anyways. If the server restarts or the AppPool restarts or even just recycles, your counts all go away and you start over from zero.
If you want to store a count that needs to persist, then you need to use a persistent medium: database, text file, etc. Not only is this safer in general, it is also the only way to have a true persistent count.
That said, why not just use Google Analytics or some other form of website analytics. Not only are you reinventing the wheel, but actual analytics tracking will be more accurate and provide more useful statistics than anything you can do on your own.
You need to change the code in the controller as follows:
[HttpGet]
public ActionResult Index()
{
ViewBag.noofsitesvisited = "No of times site visited=" + HttpContext.Application["SiteVisitedCounter"].ToString();
ViewBag.onlineusers = "No of users online on the site=" + HttpContext.Application["OnlineUserCounter"].ToString();
}
In MVC Application variables are accessible via HttpContext

Asp.net global output cache

Last few days I thinkin about output cache in asp.net. In my task I need to implement output cache for the very big project. After hours of searching I did not find any examples.
Most popular way to use output cache is declarative, in this case you need to write something like this on the page which you want to cache.
But if you need to cache whole site you must write this on all pages or master pages on project. It is madness. In this case you cant store all configuration in one place. All page have his own configurations..
Global.asax could help me, but my site contains about 20 web progects and ~20 global.asax files. And i don't want copy same code to each project.
For these reasons, i made decision to create HTTPModule.
In Init method i subscribe to two events :
public void Init(HttpApplication app)
{
app.PreRequestHandlerExecute += new EventHandler(OnApplicationPreRequestHandlerExecute);
app.PostRequestHandlerExecute += new EventHandler(OnPostRequestHandlerExecute);
}
In method "OnPostRequestHandlerExecute" I set up output caching parameters for each new request :
public void OnPostRequestHandlerExecute(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication)sender;
HttpCachePolicy policy = app.Response.Cache;
policy.SetCacheability(HttpCacheability.Server);
policy.SetExpires(app.Context.Timestamp.AddSeconds((double)600));
policy.SetMaxAge(new TimeSpan(0, 0, 600));
policy.SetValidUntilExpires(true);
policy.SetLastModified(app.Context.Timestamp);
policy.VaryByParams.IgnoreParams = true;
}
In "OnApplicationPreRequestHandlerExecute" method I set calback method to cache validation:
public void OnApplicationPreRequestHandlerExecute(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication)sender;
app.Context.Response.Cache.AddValidationCallback(new HttpCacheValidateHandler(Validate), app);
}
And last part - callback validation method :
public void Validate(HttpContext context, Object data, ref HttpValidationStatus status)
{
if (context.Request.QueryString["id"] == "5")
{
status = HttpValidationStatus.IgnoreThisRequest;
context.Response.Cache.AddValidationCallback(new HttpCacheValidateHandler(Validate), "somecustomdata");
}
else
{
status = HttpValidationStatus.Valid;
}
}
To attach my HttpModule I use programmatically attach method :
[assembly: PreApplicationStartMethod(typeof(OutputCacheModule), "RegisterModule")]
This method works perfectly, but I want to know is there other ways to do this.
Thanks.
Try seeing if IIS caching provides what you need.
http://www.iis.net/configreference/system.webserver/caching

using a static member in WebApp

I have this code
//file Globals.cs in App_Code folder
public class Globals
{
public static string labelText = "";
}
and a simple aspx page which has textbox, label and button. The CodeFile is:
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
Label1.Text = Globals.labelText;
}
protected void Button1_Click1(object sender, EventArgs e)
{
Globals.labelText = TextBox1.Text;
}
}
That is when I click on the button the Globals.labelText variable initializes from the textbox; the question is: why when I open this page in another browser the label has that value, which I set by the first browser, that is the static member is common for the every users. I thought that the every request provides in the individual appDomain which created by the individual copy of IIS process. WTF?
Yes you may use static variable to store application-wide data but it is not thread-safe. Use Application object with lock and unlock method instead of static variables.
Take a look at ASP.NET Application Life Cycle Overview for IIS 7.0 and ASP.NET Application Life Cycle Overview for IIS 5.0 and 6.0
No, static in this case is static in that manner only for the lifecycle of the process the request lives on. So this variable will be static the entire time you're processing a single request. In order to have a "static" variable in the manner you describe, you'd have to make it an application variable. Something like this:
//file Globals.cs in App_Code folder
public class Globals
{
// I really recommend using a more descriptive name
public static string LabelText
{
get
{
return Application("LabelText") ?? string.Empty;
}
set
{
Application("LabelText") = value;
}
}
}
By making it an application variable it should survive multiple page requests. A vulnerability it has though is that it will not survive an application pool recycle, and for large applications this can be problematic. If you truly want this variable to behave in a static manner reliably you're probably better off storing its state in a database somewhere.

c# HttpModule to handle pseudo sub-domains

I'm new to development with .NET and working on a personal project. My project will allow users to create their own simple mobile site.
I would like to write a HTTP Module that will handle pseudo sub-domains.
I have already setup my DNS wildcard, so sub.domain.com and xxx.domain.com etc. point to the same application. I want to be able to extract sub and ID parts from sub.domain.com/pageID.html URL and load settings of the page from a database server in order to build and render the page to the client.
I can do it with URL rewrite tools like isapirewrite, but I want my application to be independent from OS so that the server doesn't require installation of any 3rd party app.
Is it possible to do it with HTTP handlers?
Can anyone post an example?
You can check the domain at any time. Where to do it dependings on your application's goals. E.g. if you want to serve different pages depending on the domain you could do like this:
public class MyModule : IHttpModule
{
public void Dispose()
{
}
public void Init(HttpApplication context)
{
context.BeginRequest += context_BeginRequest;
}
void context_BeginRequest(object sender, EventArgs e)
{
HttpApplication app = sender as HttpApplication;
string host = app.Request.Url.Host;
if(host == "first.domain.com")
{
app.Context.RewritePath("~/First.aspx");
}
}
}

Resources