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.
Related
To improve the session handling in our application, we wanted to create a wrapper class which manages session access via properties (to prevent the common issues when working with strings).
Something like this:
public class Wrapper
{
public string Username
{
get { return HttpContext.Current.Session.GetString("username"); }
set { HttpContext.Current.Session.SetString("username", value); }
}
}
There are quite a few examples of how to implement this in classic ASP.Net, but we weren't able to port them to ASP.Net Core. The only other question about a session wrapper in ASP.Net Core (that I could find) is using a Singleton, which we didn't find suitable for our case.
Our problem comes down to two main issues:
How to wrap and access the current Session inside of another class properly (avoiding anti-patterns and security issues)
How to share the class across our application
We decided to use the dependency injection functionality of ASP.Net Core, since it seemed perfectly suitable for the job. To access the session inside of a service the IHttpContextAccessor is necessary. As stated in David Fowler's ASP.NET Core Diagnostic Scenarios it is recommended to not store the IHttpContextAccessor.HttpContext in a field, however using a scoped service should mitigate this issue, since it gets created for each request.
Our setup
Here we use the HttpContextAccessor to reference the current session. Access to the session is restricted through the use of properties (the strings could further be replaced with constants).
public class SessionService
{
private readonly ISession _session;
public SessionService(IHttpContextAccessor accessor)
{
_session = accessor.HttpContext.Session;
}
public string Username
{
get { return _session.GetString("username"); }
set { _session.SetString("username", value); }
}
}
The session wrapper then is configured as a scoped service inside of Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
// ...
// Register the service we use for session handling.
services.AddScoped<SessionService>();
}
Last but not least the service is injected into our page model and used to access the session through its property.
public class IndexModel : PageModel
{
private readonly ILogger<IndexModel> _logger;
private readonly SessionService _session;
public IndexModel(ILogger<IndexModel> logger, SessionService session)
{
_logger = logger;
_session = session;
}
public void OnPost()
{
// Set the session variable.
_session.Username = "foo#bar";
}
}
I am posting the answer in hopes of getting feedback on my solution and also providing a reference point for others struggling with this.
I am having MVC web application in which i have written some code to get forms cookie..Some times when i log out automatically some other users name were gets displays in text-box for which i have never logged in.I believe its due to private static variable but not sure.I have below code that i have implemented can anyone help me for this.Thanks
////This is code i am using to set from cookie
private static string _formsCookieName;
private static string FormsCookieName
{
get
{
if (string.IsNullOrWhiteSpace(_formsCookieName))
{
_formsCookieName = FormsAuthentication.FormsCookieName;
}
return _formsCookieName;
}
}
private static string _formsCookiePath;
private static string FormsCookiePath
{
get
{
if (string.IsNullOrWhiteSpace(_formsCookiePath))
{
_formsCookiePath = FormsAuthentication.FormsCookiePath;
}
return _formsCookiePath;
}
}
public static UserSession LogoutAuthentication(HttpContextBase context)
{
UserSession session = null;
string cookieName = FormsCookieName;
try
{
HttpCookie httpCookie = context.Request.Cookies[cookieName];
httpCookie.Expires = DateTime.Now;
FormsAuthentication.SignOut();
}
catch
{
}
return session;
}
Yes, a static variable are shared amongst all threads.
Don't use static properties for values that should live only in the lifespam of your request. You can't even use [ThreadStatic] in asp.net because you don't control the thread pool, and the same thread can be reused to handle different requests .
And even when you DO want a static value that is mutated by different threads, you need to have locks in place to avoid race conditions.
Your FormCookieName class is request dependent, therefore it should only exist during the life spam of it. The poor man way of doing it would be to instantiate it in Application_BeginRequest and disposing it on Application_EndRequest of Global.aspx.cs, assuming .NET Framework 4.5.
The correct way of doing it, though, is using a DI container. They not only inject dependency, but manages the objects lifecycles. All major DI Containers have an HttpContext lifecycle manager of sorts, and .NET Core comes with a DI Container built in. In it, your code would become:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IFormsCookieName, FormsCookieName>();
}
And your controller:
public class FooController : ControllerBase
{
public FooController(IFormsCookieName formsCookieName)
{
// receives a FormsCookieName instance that can safely use it's non-static properties
}
}
EDIT: Full configuration of Unity would be too long and off-topic for stack overflow. But the basic idea is that the Dependency Injector Container will create an instance of a non-static FormsCookieName in the scope of your HttpContext and then dispose that and the end of the request. This ensures that every HttpContext gets it's own copy of FormsCookieNameand no data will mess up.
I recommend unity as DI Container. It's maintained by Microsoft, and it's performance has seen a lot of improvements in latest versions.
Configuring a DI Container isn't hard, and provides lots of benefits.
I'm working on a Web App where I instantiated my a Singleton class below in Startup.cs in order to be reused (more like making a programmable session):
app.CreatePerOwinContext<XYZManager>(XYZManager.Create);
But I'm encountering a problem, as soon as UserA logs in on the app, the information inside XYZManager class gets overwritten when UserB enters and vice versa when they perform some action.
The problem I think is, they're sharing the same application pool, how can this be resolve, any hack?
An meanwhile the whole essence of this approach, I want to be able to call any getter / setter of method inside XYZManager for the current logged user for example:
HttpContext.GetOwinContext().Get<XYZManager>().GetFullDetails();
But sometimes throw details for another logged on user based on operations.
public class XYZManager : IDisposable
{
private static XYZManager instance { get; set; }
public static XYZManager Create()
{
var xyzManager = instance ?? (instance = new XYZManager());
xyzManager.ApplicationDbContext = new ApplicationDbContext();
return xyzManager;
}
public string GetFullDetails () {
return "blah blah";
}
}
As described in msdn, the CreatePerOwinContext method will accept a factory method to create an instance of your class (in this cas XYZManager), and it will keep it for all same context requests with.
HttpContext.GetOwinContext().Get<XYZManager>()
So each time a new Owin Context is created (a new http request received) XYZManager.Create will be invoked. In your case this method returns the same instance, so all contexts will share that instance.
Depending if you want to share that instance for all contexts or not you should return new or the same instances. Also note, that for singleton shared instances there is a different Owin extension method that will keep the singleton for you.
Check out this answer as it explains what is the purpose of the CreatePerOwinContext method, as well as provide some examples how to create a inter context shared instance.
This is how you create Context shared service
public class XYZManager : IDisposable
{
public static XYZManager Create()
{
return new XYZManager(new ApplicationDbContext());
}
private readonly ApplicationDbContext DbContext;
public XYZManager(ApplicationDbContext dbContext)
{
DbContext = dbContext;
}
public string SomeInfo {get;set;}
public string GetFullDetails ()
{
return dbContext.getFullDetails();
}
// dispose
}
Note: Since you will be creating instances each time a new owin context is creates it is advisable, to make sure any unmanaged objects are disposed.
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.
I have an issue with the static method in ASP.NET. I created the Singleton below. During the execution process I will call Context.getInstance() several times and I need the same context value. But, once I make another request (Get, Post, wherever) to the server I need a new context because my context is dependant from .NET HttpContext. But, unfortunately once I call getInstance for the first time the class never will be instantiated again.
Any ideas how I solve this problem?
public class Context
{
private static Context _context = null;
private Context()
{ ... }
public static Context getInstance()
{
if (Context._context == null)
_context = new Context();
return _context;
}
}
Get rid of your static variable and store it in HttpContext.Current.Items.
public static Context GetInstance()
{
if (HttpContext.Current.Items["MyContext"] == null)
{
HttpContext.Current.Items["MyContext"] = new Context();
}
return (Context)HttpContext.Current.Items["MyContext"];
}
If I understand you correctly, you need the context only in a single page request, correct?
If so, the method above certainly won't work - the static will live for the life of the app domain. This lifetime can vary depending on a number of factors.
I would start by creating a base page class (inheriting from the core ASP.NET Page class) and include a Context property on it. Since the page only "lives" for one request, that should work for you.
Another approach - I prefer using my own ThreadStatic variables (ala HttpContext.Current) rather than use the HttpContext Items collections simply because I think (an opinion) that it makes for cleaner code. YMMV.
public class Context
{
[ThreadStatic()]
private static Context _Context = null;
private HttpContext _HttpContext = null;
public Context()
{
_HttpContext = HttpContext.Current;
}
public static Context Current
{
if(_Context == null ||
_HttpContext != _HttpContext.Current)
{
_Context = new Context();
}
return _Context;
}
}
if your variable is static then all user will access that same variable and any change to that variable will effect all users in case of web-application only, the logic is when your are making a variable static then a memory location is allocated in server when this location is allocated all users share that location only because it is static.