I've come across a curious behaviour of ASP sessions. You can force a controller to be outside of the user's session - I want to be able to do this so that multiple requests can execute at the same time and using a session causes them to execute consecutively.
Disabling session state works as expected:
[SessionState(SessionStateBehavior.Disabled)]
public class SampleController : Controller
{
public ActionResult Test()
{
// Access to the session should be denied
object test = Session["test"];
return Content(test);
}
}
Going to ~/Sample/Test will throw a System.Web.HttpException, as expected.
However, read-only sessions appear to behave a little strangely:
[SessionState(SessionStateBehavior.ReadOnly)]
public class SampleController : Controller
{
public ActionResult Test()
{
// Read from the session should be fine
object test = Session["test"];
return Content(test);
}
public ActionResult SetTest(string value)
{
// Write to the session should fail
Session["test"] = value;
// Read it back from the session
object test = Session["test"];
return Content(test);
}
}
So now I expect ~/Sample/Test to work, and it does. The odd bit is that the set does too: I go to ~/Sample/SetTest?value=foo and it doesn't throw an exception, in fact it returns "foo". If I call ~/Sample/SetTest?value=bar and then ~/Sample/Test I get "bar", indicating that the session has been written to.
So in a SessionStateBehavior.ReadOnly I have successfully written to the session and read my value back.
I think this could be due to one of three things:
In MVC 3 [SessionState(SessionStateBehavior.ReadOnly)] is broken/ignored.
The [SessionState] is overridden when the session is written to and becomes writeable.
The SessionStateBehavior.ReadOnly actually indicates some kind of dirty/optimistic access.
Can anyone confirm?
I suspect the last one is true, based off the custom session provider documentation - if it is then how does the implementation work? Does writing to a 'read only' session risk concurrency errors (i.e. last write wins) or does it risk corrupt sessions and breaking exceptions?
Update
It looks like this is by design (from Microsoft's docs):
Note that even if the EnableSessionState attribute is marked as
ReadOnly, other ASP.NET pages in the same application might be able to
write to the session store, so a request for read-only session data
from the store might still end up waiting for locked data to be freed.
It looks like the second option above is what it actually does - the session is locked and the mode changed to writable.
~/Sample/SetTest?value=foo
Yes it won't throw any error, but it also didn't persist the session at the end of the request. By design any thing that you write to session gets updated (only if the session is writable) at the very end of the request life-cycle.
In my test ~/Sample/Test returns nothing.
I think they should have failed fast here when the session is readonly.
By the way your sample need to be rewritten
string test = (string)this.Session["test"];
Related
I have this code in my Controller:
List<string> order = new List<string>();
[Route("Reservations/Overview/Refresh/id")]
[AllowAnonymous]
public JsonResult AddOrder(string id)
{
if(!order.Contains(id))order.Add(id);
return Json($"ok", JsonRequestBehavior.AllowGet);
}
[Route("Reservations/Overview/Check/id")]
[HttpPost]
public JsonResult Check(string id)
{
if (order.Contains(id))
{
order.Remove(id);
return Json(true);
}
else return Json(false);
}
Everything is working but my global order list is always empty. Why?
HTTP is stateless. Each request instantiates a new instance of the controller class, which wouldn't reflect any changes made to class-level variables on a previous instance (which has long since been disposed) from a previous request.
Basically you need to persist your data somewhere. There are a variety of places to do this:
Session state
Static values
A database
A cache mechanism
On the page itself (posted back with form data)
Cookies
A file
and so on...
Each option is going to have its pros and cons, depending on the full set of functionality you need. Session state may be the simplest approach to get you started. On the page itself may be considered more RESTful and, thus, more scalable/portable. A database would be more secure than on the page itself because users can modify anything on the page. Etc.
But the point remains regardless of which option you want... The data needs to be persisted (saved) somewhere in order to be able to read it again at a later time.
Global variables are not persisted across multiple requests to the controller. You can persist them to a database, or store the orders in session:
Session["Orders"] = orders;
You have to store the updated list of orders to session every time you modify the list or data within the list in any way. Session is per user; just be careful how much data you put in session if you do. If you use a database, you need to persist the record change anytime a value changes on a record, or when creating a new record.
This question might sound dumb, but I am new to asp.net mvc and can't find the answer to my question.
In my application ( a game) I have a model of the game GameModel (it contains a multidimensional array). What I want is to be able to use the same object in every controller I use. So I create it once and after that use it in every controller function.
Basically there is one view, and all other functions in the controller edit the object with functions of the model.
My idea was put the object in a session variable, make a function to check the session variable if the object is not set set the object. But this does not look logic to me, hopefully someone has a better solution.
According to your question, you want to keep track of a user's data (game data).
Storing GameModel in Session variable make sense for that scenario.
If you see yourself calling that Session variable from a lot of places, you can create a BaseController and keep it there. Then inherit all controllers from it.
For example,
public class BaseController : Controller
{
public GameModel CurrentGameModel
{
get
{
var model = Session["GameModel"] as GameModel;
if (model == null)
{
model = new GameModel();
Session["GameModel"] = model;
}
return model;
}
set { Session["GameModel"] = value; }
}
}
public class HomeController : BaseController
{
}
Note: You have to keep in mind that if Application Pool recycles or Application crashes, all data stored in Session variable will be lost.
If you want to persist data, you need to store in persistent storage like database.
I don't understand why you don't think Session looks good. It's purpose is exactly keeping data per user througout multiple requests.
You could also return the state of the game to the client using hidden fields. That would be even better than Session, given that your game state doesn't change in the server, as a response to someone else's action.
And finally you can use a static property of a class. Static properties in ASP.NET are kept alive througout the application lifecicle and are visible equally to all users. Meaning, if a user writes something there, another user can read it. You can allocate data per user using a Dictionary<>, though, where the key is the user Id.
I have a helper class which reads a big XML document and generates a list of c# objects.
I work with these objects quite a lot, so i thought the best way of doing this would be to save them in memory and then access them from there.
I made a simple repository, which gets an object from memory, and if doesn't exists, it adds it.
The Repository looks like this:
public class XmlDocumentRepository
{
private readonly ICacheStorage _cacheStorage;
public XmlDocumentRepository(ICacheStorage cacheStorage)
{
_cacheStorage = cacheStorage;
}
private readonly object _locker = new object();
private void DeserializeXmlDocument()
{
lock (_locker)
{
// I deserialize the xml document, i generate the c# classes, and save them in cache
IEnumerable<Page> pages = new XmlDeserializerHelper().DeserializeXml();
foreach(var page in pages)
{
_cacheStorage.Add(page_Id, page);
}
}
}
public Page GetPage(Guid page_Id)
{
Page page = _cacheStorage.Get<Page>(page_Id);
if (page != null)
return page;
lock (_locker)
{
page = _cacheStorage.Get<Page>(page_Id);
if (page != null)
return page;
DeserializeXmlDocument();
page = _cacheStorage.Get<Page>(page_Id);
return page;
}
}
}
The XmlDocumentRepository is used inside a web application (asp.net mvc more exacly).
Is the implementation of the repository good? I am using the lock statements properly?
In my comments on the question I misunderstood the cache being shared. I think you will need to do one of the following options:
Make XmlDocumentRepository a singleton which is used across all requests because the lock object is a private field so each request will have a new instance of the repository with a new field.
Make the lock object a static field so that it is shared across all XmlDocumentRepository instances.
As a primary rule, you want to protect all access variations to data stores that are used by multiple threads. I see several potential problems with your implementation;
1: ICacheStorage is provided from the outside, which means that this collection could be modified elsewhere, which may or may not be protected by locks. Maybe you should require that the collection itself uses locking internally, or other types of thread safety mechanisms?
2: You have inconsistent lock protection of data access. In GetPage you access _cacheStorage before applying the lock, while in Deserialize, you access it inside a lock. This means that you may get a result where one is adding to the cache while another is getting from it.
3: Do you require thread safety for the cache, for xml reading, or both?
If you only need to protect the cache, move reading of xml outside the lock. If protecting both, you should put the entire GetPage function inside the lock.
Right now I'm having an issue with a Singleton that I just wrote for use in ASP.NET MVC -- My Singleton looks like this:
public sealed class RequestGenerator : IRequestGenerator
{
// Singleton pattern
private RequestGenerator()
{
requestList = new Stack<Request>();
appSettings = new WebAppSettings();
}
private static volatile RequestGenerator instance = new RequestGenerator();
private static Stack<Request> requestList = new Stack<Request>();
// abstraction layer for accessing web.config
private static IAppSettings appSettings = new WebAppSettings();
// used for "lock"-ing to prevent race conditions
private static object syncRoot = new object();
// public accessor for singleton
public static IRequestGenerator Instance
{
get
{
if (instance == null)
{
lock (syncRoot)
{
if (instance == null)
{
instance = new RequestGenerator();
}
}
}
return instance;
}
}
private const string REQUESTID = "RequestID";
// Find functions
private Request FindRequest(string component, string requestId)
private List<Request> FindAllRequests(string component, string requestId)
#region Public Methods required by Interface
// Gets and increments last Request ID from Web.Config, creates new Request, and returns RequestID
public string GetID(string component, string userId)
// Changes state of Request to "submitted"
public void SetID(string component, string requestId)
// Changes state of Request to "success" or "failure" and records result for later output
public void CloseID(string component, string requestId, bool success, string result)
// Verifies that Component has generated a Request of this ID
public bool VerifyID(string component, string requestId)
// Verifies that Component has generated a Request of this ID and is owned by specified UserId
public bool VerifyID(string component, string userId, string requestId)
// Returns State of Request ID (Open, Submitted, etc.)
public Status GetState(string component, string requestId)
// Returns Result String of Success or Failure.
public string GetResult(string component, string requestId)
#endregion
}
And my controller code looks like this:
public ViewResult SomeAction()
{
private IRequestGenerator reqGen = RequestGenerator.Instance;
string requestId = reqGen.GetID(someComponentName, someUserId);
return View(requestId);
}
Everything works okay the first time I hit the controller. "reqGen" is assigned the instance of the Singleton. A new instance of Request is added to the internal list of the Singleton. And then we return a View(). The next time I hit this controller's SomeAction(), I'm expecting the Singleton to contain the List with the instance of SomeClass that I had just added, but instead the List is empty.
What's happened? Has Garbage Collection gobbled up my object? Is there something special I need to consider when implementing the Singleton pattern in ASP.NET MVC?
Thanks!
EDIT: Ahh, the lightbulb just went on. So each new page request takes place in a completely new process! Got it. (my background is in desktop application development, so this is a different paradigm for me...)
EDIT2: Sure, here's some more clarification. My application needed a request number system where something being requested needed a unique ID, but I had no DB available. But it had to be available to every user to log the state of each request. I also realized that it could double as a way to regulate the session, say, if a use double-clicked the request button. A singleton seemed like the way to go, but realizing that each request is in its own process basically eliminates the singleton. And I guess that also eliminates the static class, right?
EDIT3: ok, I've added the actual code that I'm working with (minus the implementation of each Method, for simplicity sake...) I hope this is clearer.
EDIT4: I'm awarding the green check mark to Chris as I'm beginning to realize that an application-level singleton is just like having a Global (and global's are evil, right?) -- All kidding aside, the best option really is to have a DB and SQLite seems like the best fit for now, although I can definitely see myself moving to an Oracle instance in the future. Unfortunately, the best option then would be to use an ORM, but that's another learning curve to climb. bugger.
EDIT5: Last edit, I swear. :-)
So I tried using HttpRuntime.Cache, but was surprised to find that my cache was getting flushed/invalidated constantly and couldn't figure out what was going on. Well, I was getting tripped up by a side-effect of something else I was doing: Writing to "Web.config"
The Answer --> Unbeknownst to me, when "web.config" is altered in anyway, the application is RESTARTED! Yup, everything gets thrown away. My singleton, my cache, everything. Gah. No wonder nothing was working right. Looks like writing back to web.config is generally bad practice which I shall now eschew.
Thanks again to everyone who helped me out with this quandary.
The singleton is specific to the processing instance. A new instance is being generated for each page request. Page requests are generally considered stateless so data from one doesn't just stick around for another.
In order to get this to work at the application level, the instance variable will have to be declared there. See this question for a hint on how to create an application level variable. Note that this would make it available across all requests.. which isn't always what you want.
Of course, if you are trying to implement some type of session state then you might just use session or use some type of caching procedure.
UPDATE
Based on your edits: A static class should not maintain data. It's purpose is to simply group some common methods together, but it shouldn't store data between method calls. A singleton is an altogether different thing in that it is a class that you only want one object to be created for the request.
Neither of those seem to be what you want.
Now, having an application level singleton would be available to the entire application, but that crosses requests and would have to be coded accordingly.
It almost sounds like you are trying to build an in memory data store. You could go down the path of utilizing one of the various caching mechanisms like .NET Page.Cache, MemCache, or Enterprise Library's Caching Application Block.
However, all of those have the problem of getting cleared in the event the worker process hosting the application gets recycled.. Which can happen at the worst times.. And will happen based on random things like memory usage, some timer expired, a certain number of page recompiles, etc.
Instead, I'd highly recommend using some type of persisted storage. Whether that be just xml files that you read/write from or embedding something like SQL Lite into the application. SQL Lite is a very lightweight database that doesn't require installation on the server; you just need the assemblies.
You can use Dependency Injection to control the life of the class. Here's the line you could add in your web.config if you were using Castle Windsor.
<component id="MySingleton" service="IMySingleton, MyInterfaceAssembly"
type="MySingleton, MyImplementationAssembly" lifestyle="Singleton" />
Of course, the topic of wiring up your application to use DI is beyond my answer, but either you're using it and this answer helps you or you can take a peak at the concept and fall in love with it. :)
I am writing a user authentication class. During the request there are a lot of references to the current user, so I would like to cache it in memory instead of calling the database ala singleton. I am thinking about using session and clearing it at the end of every request.
like:
public static User Current() {
if (Session["current-user"] == null) {
Session["current-user"] = GetUserFromDB(); // example function, not real
}
return (User)Session["current-user"];
then in app_end request:
Session.Clear();
HttpContext.Items["user"] = user;
You can reference the context items during the entire request and it will be cleaned up at the end of it.
Use the HttpContext class. You can get to it either in the context of a controller of HttpContext.Current.
The HttpContext.Items collection is what you want to use.