How to get all active session from asp.net application - c#

My purpose here is to make sure that all the logged in users should not get logged out if application pool stops or restarts.
One way is that i can use out prox sessions and directly store them in database but this is going to impact application performance so i dont want to do that.
Another way i found is that, i can register a "IRegisteredObject" implemented class object in hosting environment.
public class SessionTracker : IRegisteredObject
{
//I created this class as singleton and exposed GetInstance method
public void Stop(bool immediate)
{
//Here i want to get all the sessions and store them in database
}
}
Register it in global object
public class Global : System.Web.HttpApplication
{
protected void Application_Start(Object sender, EventArgs e)
{
HostingEnvironment.RegisterObject(SessionTracker.GetInstance());
}
}
I want to get all the active session of application so that i can store them in database at the time of application pool restarts or stops suddenly.
Now when my application starts, at that time i will load all the database stored session states again and all the session states will be valid.
Also i found that i can use Session start and Session end events and add sessions in a list object. I am not sure how efficient it will be as it can take more memory in server side.
Please suggest a approach by which i can get all the sessions at once. Or Is it okay if i use session start and session end event of global.ascx.cs file? Is it going to impact memory utilization?

I did further investigation and created a POC to check if it creates copy of Sessions by reference or by value.
What i found is that it creates copy by reference. So technically there is no memory utilization issue if i add their reference in SessionTracker class. Here the complete implementation of SessionTracker class if somebody is interested to know about it
public class SessionTracker : IRegisteredObject
{
static readonly SessionTracker Tracker = new SessionTracker();
private static readonly Dictionary<string, HttpSessionState> _sessions = new Dictionary<string, HttpSessionState>();
private static readonly object padlock = new object();
private SessionTracker()
{
}
public void Stop(bool immediate)
{
try
{
//store them in database
}
catch (Exception ex)
{
throw;
}
}
public void AddSession(HttpSessionState session)
{
lock (padlock)
{
_sessions.Add(session.SessionID, session);
}
}
public void RemoveSession(HttpSessionState session)
{
lock (padlock)
{
_sessions.Remove(session.SessionID);
}
}
public static SessionTracker GetInstance()
{
return Tracker;
}
}

Related

ASP.NET how to use Sessions, when and where to declare it?

I am trying to retrieve some data from db and store it some Session variable in order to have it in _Layout.cshtml on all pages, no matter what page the user will access from the start.
Global.asax:
protected void Application_Start()
{
...
Manager mng = new Manager();
HttpContext.Current.Session["company-cellphone"] = mng.GetContacts().CompanyCellphone;
}
Error: Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.
you are trying to access the session from Application_Start but there is no live Session yet.
session is not available in all events of global.asax
as a workaround try this:
protected void Application_PreRequestHandlerExecute(object sender, EventArgs e)
{
if (Context.Handler is IRequiresSessionState || Context.Handler is IReadOnlySessionState)
{
HttpContext context = HttpContext.Current;
...
Manager mng = new Manager();
HttpContext.Current.Session["company-cellphone"] = mng.GetContacts().CompanyCellphone;
}
}
I'm not sure about your requirement but I would recommend to access the session in controller.initialize method
protected override void Initialize(System.Web.Routing.RequestContext requestContext)
{
base.Initialize(requestContext);
//// access session here
requestContext.HttpContext.Session["company-cellphone"]=mng.GetContacts().CompanyCellphone;
}
Application_Start runs before any sessions can be created. And a session is specific to a single client connecting to your application.
You can create a static class and store the company-cellphone value in it.
In your Models folder create a new class file named Global.cs in that file create a static class with properties that will hold your application level information.
public static class Global
{
static string companyCellPhone;
public static string companyCellPhone
{
get
{
return this.companyCellPhone;
}
set
{
this.companyCellPhone= value;
}
}
Then your Application_Start method would look something like this:
protected void Application_Start()
{
...
Manager mng = new Manager();
Global.companyCellPhone = mng.GetContacts().CompanyCellphone;
}
I'm going to go out on a limb and guess that this is a single global value to be viewed by all users. In that case you could store the value in HttpApplicationState rather than HttpSessionState:
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
Application["YourValue"] = "SomeValue";
}
}
I'm not necessarily advocating its use. But just as Session can store user-specific values, Application stores values that are global to the application.

How to access a variable that I only need to create once on Application.Start

According to this guide:
https://github.com/mspnp/azure-guidance/blob/master/Retry-Service-Specific.md
They say:
Note that the StackExchange.Redis client uses multiplexing through a single connection. The recommended usage is to create an instance of the client at application startup and use this instance for all operations against the cache. For this reason, the connection to the cache is made only once, and so all of the guidance in this section is related to the retry policy for this initial connection—and not for each operation that accesses the cache.
Right now I have something like this:
public static Models.UserProfile GetUserProfile(string identityname)
{
/// It needs to be cached for every user because every user can have different modules enabled.
try
{
var cachekeyname = "UserProfileInformation|" + identityname;
IDatabase cache = CacheConnectionHelper.Connection.GetDatabase();
Models.UserProfile userProfile = new Models.UserProfile();
object obj = cache.Get(cachekeyname);
I can move the connection line to global.asax
protected void Application_Start()
{
IDatabase cache = CacheConnectionHelper.Connection.GetDatabase();
}
If I move that line, then how can I get that instance on the other methods where I need to use it?
this is the cache connection helper
public class CacheConnectionHelper
{
private static Lazy<ConnectionMultiplexer> lazyConnection = new Lazy<ConnectionMultiplexer>(() =>
{
return ConnectionMultiplexer.Connect(SettingsHelper.AzureRedisCache);
});
public static ConnectionMultiplexer Connection
{
get
{
return lazyConnection.Value;
}
}
}
You can make it static inside the global.asax file
public class Global : HttpApplication {
public static IDatabase Cache = CacheConnectionHelper.Connection.GetDatabase();
void Application_Start(object sender, EventArgs e) {
}
.....
}
Now you can access the database object in any class by simply accessing Global.Cache which is your single instance of the database.

Lazy Singleton persisting across users

I have a Lazy Singleton that is used as a reference object for a web application.
It stores basic properties that are refrenenced throughout the application:
public class Context
{
public string UserName;
public Guid TenantId;
public static Context Current { get { return lazy.Value; } }
private static readonly Lazy<Context> lazy =
new Lazy<Context>(() => new Context());
}
//In Action
public static Something GetSomethingForUser()
{
return DataAccess.GetSomethingForCurrentUser(Context.Current.UserName);
}
The problem is, if a user logs in and out my Context is persisting across sessions.
This isn't unexpected, I'm not disposing it.
Where should I do that? What's the best method?
Should I do it on sign out?
public void SignOut()
{
Context.Dispose();
HttpContext.GetOwinContext().Authentication.SignOut(
OpenIdConnectAuthenticationDefaults.AuthenticationType,
CookieAuthenticationDefaults.AuthenticationType);
}
In my opinion, signout should be an event other classes can register to and act properly in response to it.
The way you are using the singleton seems okay to me if you only have 1 active user at a time.
Another solution which would removed the signout problem, such has the data inside the singleton should be put inside the session itself.

C# web-service instances

I have the following class in Web-service App_Code:
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class MyService : WebService
{
private readonly MyServiceFacade myService;
static MyService()
{
}
public MyService()
{
myService = new MyServiceFacadeImpl();
}
}
And now I have a question about how instances of this service are created.
For example, let we have the following class:
public class MyServiceFacadeImpl()
{
private List<DateTime> dts;
public MyServiceFacadeImpl()
{
dts.Add(DateTime.Now);
}
}
Now, 15 users connect to the server and authenticate using basicauth and what will happen?
There will be 15 instances of MyServiceFacadeImpl with one DateTime in each dts
There will be a single instance of MyServiceFacadeImpl with 15 DateTimes in dt
And, now, if I make this List static, what will happen?
I just need to implement a mechanism which will restrict the number of requests from a single user \ session per minute.
And, now, if I make this List static, what will happen?
There will be 15 instances of MyServiceFacadeImpl with 15 DateTime in static instance of dts
I just need to implement a mechanism which will restrict the number of
requests from a single user \ session per minute.
You can use Dictionary <string, DateTime> where string stores the user name. It would be static or you can store dictionary in Application object. If you want to use Application object to store the state of user then this MSDN article How to: Save Values in Application State explains it.
The above method are not safe to store the information that you do not want to loose. You would consider to store in information in persistent medium like database if you want it even the web services go down.
There are several different ways you can solve this.
The easiest is to implement a singleton pattern where which has an internal dictionary to register a user to the number of times a user has made a request.
public sealed class UserRequests{
private static readonly UserRequests instance = new UserRequests();
public static UserRequests Instance { get { return instance; } }
static UserRequests() {}
private UserRequests() {}
private Dictionary<Users,List<DateTime>> _userRequestList;
private void AddRequest(User user){
//Add request to internal collection
}
public bool CanUserMakeRequest(User user){
//Call clean up method to remove old requests for this user
// check the requests to see if user has made too many
// if not call AddRequest and return true, else return false
}
}

Should this "singleton" be thread-safe in an ASP.NET app?

Note that "singleton" used in slightly uncommon sense - "object visible as single instance like HttpContext.Current" unlike normal "object with one shared instance".
I make use of a singleton type of UserContext class for my asp.net MVC applications. This class allows me to store user data as a strongly-typed session object. I ran across this CodeReview question and wondered if it was necessary to be concerned about thread safety in this application context.
Here's a simplification of my code:
public class UserContext
{
private UserContext()
{
}
public static UserContext Current
{
get
{
if (HttpContext.Current.Session["UserContext"] == null)
BuildUserContext();
return (UserContext)HttpContext.Current.Session["UserContext"];
}
}
private static void BuildUserContext()
{
if (!user.Identity.IsAuthenticated) return;
var uc = new UserContext { IsAuthenticated = true };
// ...snip...
// Set up user data
// Save it into the session
HttpContext.Current.Session["UserContext"] = uc;
}
#region Class members
public bool IsAuthenticated { get; internal set; }
public string Name { get; internal set; }
// ...snip...
// Other properties
public void Refresh()
{
BuildUserContext();
}
public void Flush()
{
HttpContext.Current.Session["UserContext"] = null;
}
#endregion
}
I haven't had any locking issues so far, but right now the site is not very high traffic. Should I adopt Jon Skeet's thread-safe model or does IIS manage that for me?
Access the Session is already Thread safe.
In general as long as you access any shared state in your static properties in a thread-safe manner, you won't have any problems.
ASP session state comes with a synchronizing logic.
If the executed page needs write access to the session state, the session state is locked and other request on the same session has to wait until the first one finishes.
See Synchronizing Access to the Session State.

Categories

Resources