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.
Related
I am working on a test suit implementation which uses the SpecFlow + SpecRunner and XUnit. and we are trying to do parallel test execution and i would like to know is there are a way that i can run a hook in the begining of the test run and store the token value in a static variable so that that can be shared among threads.
to summarize is there a way that specflow offers a mechanism to share data between threads during parallel execution.
We can share the data using any one of the below approach
Scenario Context
Context Injection
Here, Approach 1 and 2 will not have any issue in multiple thread. Since, Context Injection life is specific to the scenario Level.
Approach 1 : we can define the Token Generation Step within the BeforeScenario hooks and the generated Token values can be updated in the ScenarioContext.
we can directly access the token from the scenario context in any place like below
Here, Token will be generated before each scenario run and it will not affect the Parallel execution.For more Details, Parallel-Execution
Scenarios and their related hooks (Before/After scenario, scenario block, step) are isolated in the different threads during execution and do not block each other. Each thread has a separate (and isolated) ScenarioContext.
Hooks Class:
public class CommonHooks
{
[BeforeScenario]
public static void Setup()
{
// Add Token Generation Step
var adminToken = "<Generated Token>";
ScenarioContext.Current["Token"] = adminToken;
}
}
Step Class:
[Given(#"I Get the customer details""(.*)""")]
public void WhenIGetTheCustomerDetails(string endpoint)
{
if(ScenarioContext.Current.ContainsKey("Token"))
{
var token = ScenarioContext.Current["Token"].ToString();
//Now the Token variable holds the token value from the scenario context and It can be used in the subsequent steps
}
else
{
Assert.Fail("Unable to get the Token from the Scenario Context");
}
}
If you wish to share the same token across multiple Step, then you can assign this token value within constructor and it can be used
For Example,
[Binding]
public class CustomerManagementSteps
{
public readonly string token;
public CustomerManagementSteps()
{
token= ScenarioContext.Current["Token"].ToString();
}
[Given(#"I Get the customer details""(.*)""")]
public void WhenIGetTheCustomerDetails(string endpoint)
{
//Now the Token variable holds the token value from the scenario context and It can be used in the subsequent steps
}
}
Approach 2: Context Injection details can be referred in the below link with an example
Context Injection
Updated
Given the downvote and comments, I've updated my code example to better show exactly one way you can use dependency injection here with code of your own design. This shared data will last the lifetime of the scenario and be used by all bindings. I think that's what you're looking for unless I'm mistaken.
//Stores whatever data you want to share
//Write this however you want, it's your code
//You can use more than one of these custom data classes of course
public class SomeCustomDataStructure
{
//If this is run in paralell, this should be thread-safe. Using List<T> for simplicity purposes
//Use EF, ConcurrentCollections, synchronization (like lock), etc...
//Again, do NOT copy this code for parallel uses as List<int> is NOT thread-safe
//You can force things to not run in parallel so this can be useful by itself
public List<int> SomeData { get; } = new List<int>();
}
//Will be injected and the shared instance between any number of bindings.
//Lifespan is that of a scenario.
public class CatalogContext : IDisposable
{
public SomeCustomDataStructure CustomData { get; private set; }
public CatalogContext()
{
//Init shared data however you want here
CustomData = new SomeCustomDataStructure();
}
//Added to show Dispose WILL be called at the end of a scenario
//Feel free to do cleanup here if necessary.
//You do NOT have to implement IDiposable, but it's supported and called.
public void Dispose()
{
//Below obviously not thread-safe as mentioned earlier.
//Simple example is all.
CustomData.SomeData.Clear();
}
}
[Binding]
public class SomeSteps
{
//Data shared here via instane variable, accessable to multiple steps
private readonly CatalogContext catalogContext;
//Dependency injection handled automatically here.
//Will get the same instance between other bindings.
public SomeSteps(CatalogContext catalogContext)
{
this.catalogContext = catalogContext;
}
[Given(#"the following ints")]
public void GivenTheFollowingInts(int[] numbers)
{
//This will be visible to all other steps in this binding,
//and all other bindings sharing the context
catalogContext.CustomData.SomeData.AddRange(numbers);
}
}
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;
}
}
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.
I just have a question related to "best practice". If this question isn't constructive, then just vote it down :)
I had a discussion with a colleague some days ago, we have two completely different philosophies when it comes to best practice regarding open connections to a web service, COM-object, database etc.
I prefer wrapping the connection code in a class that implements IDisposable and let that handle all that comes to connection etc. A short example.
public class APIWrapper : IDiposable
{
public APIWrapper(...)
{
DataHolder = new DataHolder(...);
/// Other init methods
Connect();
}
public [Webservice/Database/COM/etc.] DataHolder { get; private set; }
public void Connect()
{
DataHolder.Connect(...);
}
public void Disconnect()
{
DateHolder.Disconnect();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if(disposing)
{
if (DataHolder != null)
{
Disconnect();
DataHolder = null;
}
}
}
}
And I will use it like this in my Data controller.
public class UserController
{
....
public IList<User> getUsers()
{
using(APIWrapper datalayer = new APIWrapper(...))
{
var users = datalayer.DataHolder.GetUsers();
/// Map users to my enity of users
return mappedUsers;
}
}
}
And my colleagues would look like this:
public class UserController
{
protected [Webservice/Database/COM/etc.] DataHolder { get; set; }
public UserController()
{
DataHolder = new DataHolder(...);
DataHolder.Connect(...);
}
public IList<User> getUsers()
{
var users = DataHolder.GetUsers();
/// Map users to my enity of users
return mappedUsers;
}
/// Who should call this?
public void Dispose()
{
DataHolder.Disconnect();
}
}
The code above are just examples that are simplified (and written i stackoverflow text editor), but I think they show the necessary philosophies.
So, the first example will open and close to connection at each call. The second example will hold the connection open for a longer amount of time.
What is "generally" best practice in your opinion?
I would recommend disposing of any unmanaged resources as soon as possible, as you outline in your example. Garbage collection will get there eventually, but why wait? Comprehensive answer here: Proper use of the IDisposable interface
Specifically re SQL server the connection pool has an upper limit of connections, either a default or defined in your connection string. If you don't close the connections you open, you will exhaust the pool.
Agree with James Thorpe comment that this might depend on the cost of creating the resource, but the examples you specify (db connection, web service call) shouldn't be particularly expensive
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.