What is the best way to store string data across postback. I need to store an ID and name for multiple entities. I was thinking of using a datatable in viewstate, but would that make viewstate grow too large? I can't use a database yet because I'll be inserting a record that those other records need to be related to. So I'll just be storing them temporarily until the user submits the form.
You actually have a lot of options - the one you choose will entirely depend on the requirements of your own application.
ViewState - you can add the data to the page's viewstate. The advantages of this is that the data will live only for the lifetime of the user being on the page and posting it back to the server. Another advantage of it over hidden fields is that it is harder for users to hack into it and alter your values (I believe, in fact, that you can encrypt your viewstate). The disadvantage, of course, lies in page sizes - everything you add to the view state is one more thing that gets dropped on a user's page and then gets posted back to the server. This makes it non-optimal for storing large amounts of data.
Cookies - You can toss the information back to the user in the form of cookies. In this case, you can declare how long the information will last - for the scope of the user having their browser open, or for a specific calendar time. The information will be available to any page of your application each time the user hits that page. The bad news is that you are limited in the amount of information you can store, and that users can very readily alter their own cookies.
Session - You're storing the user's information on your own server's memory (I'll leave aside the discussion of various types of session storage). In this case the information will live as long as your user's session lives, and will be available to all pages of your application. There is no risk of user's modifying those values directly, though session hijacking is a risk you may want to explore. The disadvantage, though, is that you are using precious server resources in this case - if your application has a large load, it may affect your scalability in the future.
As I said - what you choose to do will entirely depend on the needs and requirements of your application.
several ways (though not an exhaustive list):
ViewStatehidden fieldssessionquery stringcookies
ViewState is fine. If you are storing it across postbacks, a client-side solution is best. So, you'd be adding size somewhere--either in ViewState or hidden fields.
If you want to do this server-side, you can use the Session, but remember to clean it up when you can.
you could just store them to a cookie, this would allow you to access them from Javascript too. Alternatively you could store a simple string array to the view state. A lot depends on what and how much information you wish to store.
When I have this scenario I create a structure for my fields that I stuff into Viewstate. I'm okay with having a small structure added into the page size and lifecycle considering the entire page's controls set is there already :)
Furthermore it cleans up after itself after you're done with the page, so there's no worrying about filling your Session with crap.
I concur with the accepted answer but I would also add that if you only want to keep track of a simple key/value collection you would be better putting a generic Dictionary into either ViewState or Session:
Dictionary<int, string> myValues = new Dictionary<int, string>();
myValues.Add(1, "Apple");
maValues.Add(2, "Pear");
Related
Are there any other ViewState alternatives? I've heard a lot Like Session, holding the state of some of the page's controls' state and getting destroyed the moment the user leaves the page.
I know I'm describing ViewState itself, but I'm looking for a pattern of sorts or suggestions so I can avoid ViewState altogether.
An example of how I'm using it is where I'm storing the contents of my grid (A list of ViewModels) to ViewState. This helps to know which entries are dirty, which ones have been modified, their indexes, the currently selected objects, etc.
One of my colleagues has developed a way to store viewstate data in a file. So that, heavy viewstate data are not transmitted between client and server. Just a key (which is the viewstate data file) that represents the viewstate data file is held as a session variable. In our tests, we've found that saving viewstate in file decreased the server response time by decreasing the amount of viewstate (which was very huge then).
In this article under "Keeping View State on the Server", you can find out how that method can be implemented. You can even store viewstate data in a database table, which gives extra flexibilty if your application is on a web farm.
I don't think you are making a case to move away from ViewState.
If you are holding a large amount of data, you'll face issues when persisting it anywhere else. Session? it'll blow your memory consumption, or if its out of process you'll be moving all of that around each time the Session is loaded/written (once per request). You can of course, try to limit the issue by freeing the stored data as soon as possible / like TempData in asp.net MVC.
You can minimize the amount of info you need to store to check for modified records by introducing a timestamp / or record version. This way you can just check if a new version has been added, and show the user both what they tried to save and what someone else saved.
Another option is compressing your ViewState. It still adds bulk to the round trip, but generally it's minimal.
If you're using .Net 4, there are some useful new ViewState additions:
ASP.NET 4.0: more control on viewstate management
You have Session, and you have Cache.
Session is per user, Cache is global.
Do you really need to store all this in ViewState? why can you on submit (but you're very vague in your question so i'm making a few assumptions here) get all the old data from the DB, compare it with your new data, and update what is changed?
I have a web app, that consumes a web service. The main page runs a search - by passing parameters to a particular web service method, and I bind the results to a gridview.
I have implemented sorting and paging on the grid. By putting the datatable that the grid is bound to in the viewstate and then reading / sorting / filtering it as necessary - and rebinding to the grid.
As the amount of data coming back from the web service has increased dramatically, when I try to page/sort etc I receive the following errors.
The connection was reset
The connection to the server was reset while the page was loading.
I have searched around a bit, and it seems that a very large viewstate is to blame for this.
But surely the only other option is to
Limit the results
Stick the datatable in the session rather than the viewstate
Something else I am unaware of
Previously I did have the datatable in the session, as some of this data needed to persist from page to page - (not being posted however so viewstate was not an option). As the amount of data rose and the necessity to persist it was removed, I used the viewstate instead. Thinking this was a better option than the session because of the amount of data the session would have to hold and the number of users using the app.
It appears maybe not.
I thought that when the viewstate got very big, that .net split it over more than one hidden viewstate field, but it seems all I'm getting is one mammoth viewstate that I have trouble viewing in the source.
Can anyone enlighten me as to how to avoid the error I'm getting? If it is indeed to do with the amount of data in the viewstate?
It sounds like your caching the whole dataset for all pages even though you are only presenting one page of that data. I would change your pagination to only require the data for the current page the user is on.
If the query is heavy and you don't want to have to be constantly calling it over and over because there is a lot of paging back and forth (you should test typical useage pattern) then I would implement some type of caching on the web service end to cache page by page (by specific user if the data is specific to a user) and have it expire rather quick (eg a few minuites).
I think you need to limit the total amount of data your dealing with. Change your code to not pass back extra data that might never be needed is a good place to start.
EDIT: Based on your comments:
You can't change the web service
The user can manipulate the query by filtering or sorting
There is a large amount of data returned by the web service
The data is user specific
Well I think you have a perfect case for using the Session then. This can be taxing the the server with large amounts of users and data so you might want to implement some logic to clear the data from the Session and not wait for it to expire (like on certain landing pages you know the user will go when they are done, clear the session data).
You really want to get it out of the ViewState beacuse it is a huge bandwidth hog. Just look at your physical page size and that data is being passed back and forth with every action. Moving it to the Session would eliminate that bandwidth useage and allow for you to do everything you need.
You could also look at the data the web service is bringing back and store it in a custom object that you make as 'thin' as possible. If your storing a DataSet or a DataTable in your Session, those objects have some extra overhead you probably don't need so store the data as an array of some custom thin object and just bind to that. You would need to map the result from the WS to your custom object but this is a good option you cut down on memory useage.
Let me know if there is something else I am missing.
I wouldn't put the data in either the view state or the session. Instead store the bare minimum information to re-request the dataset from the web service and store that (in either view state or session, or even on the URL). Then call the web service using that data and reaction the data on each request. If necessary, look to use some form of caching (memCache) to improve performance.
I need to store IDs (Contact IDs, Claim IDs, etc.) between multiple .aspx pages.
At the moment I am storing the ID in the Session and have set the Session timeout to 300 minutes. However, I am still getting errors because users are attempting to perform operations after the Session has expired.
I think users are leaving their web broswers open, locking their computers, going home for the evening, coming in the next morning and attempting to pick up where they left off.
I don't want to use the Querystring. Cookies are more for User IDs than Contact IDs and Claim IDs. Viewstate is only maintained per page. Persisting Session to a database seems unnecessarily complicated. I don't want to extend the Session timeout too much.
I'd ideally like them to be able to pick up where they left off in the morning.
What are the best practices for dealing with storing IDs between pages? How can I do this without them receiving a message saying their session has expired? Am I overlooking something obvious?!
I would put all this information into a database. This is classic use of a database, a means to store information needed for the application.
Cookies are bad because you have to assume they will be at the same PC from session to session. You have seem the problem with sessions.
If you do not want to use a database then you can use a file on the server. And read from that file.
You might actually find it much easier than complicated to create a sort of "landing pad" database where a user's item history is stored, or MRU list. I wouldn't tie it to their immediate session or cookie though. Let the session maintain the user state, let the database handle where they were when they went home or took that 7 hour lunch break.
As #David said this is a classic use of databases.
If you are in need of doing this without using databases, you can fall back on some of the "tricks" (read hacks) that are used in non-dynamic languages:
Create a hidden field which exists on every page, name the field appropriately for the data you are storing.
When you render the page create an XML fragment containing your ids and other data, encrypt it for display purposes (using base64 etc), place the value into the hidden field you created.
When the page is posted back, reverse #2 and recover your data, then apply the page changes.
Rinse and repeat. :)
Really this is not a very scalable solution, as the XML and base64 encoding will add 50% or more to your data size, and if there is significant data (100's of KB) this process can get out of control.
The last time I was asked to use this solution it was for moving a list of well defined data around with an application that did not always have access to the DB. It worked ok for the purposes, but was definitely not ideal.
You could go ahead and store the IDs in viewstate on the page, then on each page that requires that ID have a settable property for that page. Before redirecting to the page instantiate it and set the ID property.
You're correct, storing this type of data in session isn't really what it's meant for. Viewstate is a better mechanism for this.
For the sake of argument assume that I have a webform that allows a user to edit order details. User can perform the following functions:
Change shipping/payment details (all simple text/dropdowns)
Add/Remove/Edit products in the order - this is done with a grid
Add/Remove attachments
Products and attachments are stored in separate DB tables with foreign key to the order.
Entity Framework (4.0) is used as ORM.
I want to allow the users to make whatever changes they want to the order and only when they hit 'Save' do I want to commit the changes to the database. This is not a problem with textboxes/checkboxes etc. as I can just rely on ViewState to get the required information. However the grid is presenting a much larger problem for me as I can't figure out a nice and easy way to persist the changes the user made without committing the changes to the database. Storing the Order object tree in Session/ViewState is not really an option I'd like to go with as the objects could get very large.
So the question is - how can I go about preserving the changes the user made until ready to 'Save'.
Quick note - I have searched SO to try to find a solution, however all I found were suggestions to use Session and/or ViewState - both of which I would rather not use due to potential size of my object trees
If you have control over the schema of the database and the other applications that utilize order data, you could add a flag or status column to the orders table that differentiates between temporary and finalized orders. Then, you can simply store your intermediate changes to the database. There are other benefits as well; for example, a user that had a browser crash could return to the application and be able to resume the order process.
I think sticking to the database for storing data is the only reliable way to persist data, even temporary data. Using session state, control state, cookies, temporary files, etc., can introduce a lot of things that can go wrong, especially if your application resides in a web farm.
If using the Session is not your preferred solution, which is probably wise, the best possible solution would be to create your own temporary database tables (or as others have mentioned, add a temporary flag to your existing database tables) and persist the data there, storing a single identifier in the Session (or in a cookie) for later retrieval.
First, you may want to segregate your specific state management implementation into it's own class so that you don't have to replicate it throughout your systems.
Second, you may want to consider a hybrid approach - use session state (or cache) for a short time to avoid unnecessary trips to a DB or other external store. After some amount of inactivity, write the cached state out to disk or DB. The simplest way to do this, is to serialize your objects to text (using either serialization or a library like proto-buffers). This helps allow you to avoid creating redundant or duplicate data structure to capture the in-progress data relationally. If you don't need to query the content of this data - it's a reasonable approach.
As an aside, in the database world, the problem you describe is called a long running transaction. You essentially want to avoid making changes to the data until you reach a user-defined commit point. There are techniques you can use in the database layer, like hypothetical views and instead-of triggers to encapsulate the behavior that you aren't actually committing the change. The data is in the DB (in the real tables), but is only visible to the user operating on it. This is probably a more complicated implementation than you may be willing to undertake, and requires intrusive changes to your persistence layer and data model - but allows the application to be ignorant of the issue.
Have you considered storing the information in a JavaScript object and then sending that information to your server once the user hits save?
Use domain events to capture the users actions and then replay those actions over the snapshot of the order model ( effectively the current state of the order before the user started changing it).
Store each change as a series of events e.g. UserChangedShippingAddress, UserAlteredLineItem, UserDeletedLineItem, UserAddedLineItem.
These events can be saved after each postback and only need a link to the related order. Rebuilding the current state of the order is then as simple as replaying the events over the currently stored order objects.
When the user clicks save, you can replay the events and persist the updated order model to the database.
You are using the database - no session or viewstate is required therefore you can significantly reduce page-weight and server memory load at the expense of some page performance ( if you choose to rebuild the model on each postback ).
Maintenance is incredibly simple as due to the ease with which you can implement domain object, automated testing is easily used to ensure the system behaves as you expect it to (while also documenting your intentions for other developers).
Because you are leveraging the database, the solution scales well across multiple web servers.
Using this approach does not require any alterations to your existing domain model, therefore the impact on existing code is minimal. Biggest downside is getting your head around the concept of domain events and how they are used and abused =)
This is effectively the same approach as described by Freddy Rios, with a little more detail about how and some nice keyword for you to search with =)
http://jasondentler.com/blog/2009/11/simple-domain-events/ and http://www.udidahan.com/2009/06/14/domain-events-salvation/ are some good background reading about domain events. You may also want to read up on event sourcing as this is essentially what you would be doing ( snapshot object, record events, replay events, snapshot object again).
how about serializing your Domain object (contents of your grid/shopping cart) to JSON and storing it in a hidden variable ? Scottgu has a nice article on how to serialize objects to JSON. Scalable across a server farm and guess it would not add much payload to your page. May be you can write your own JSON serializer to do a "compact serialization" (you would not need product name,product ID, SKU id, etc, may be you can just "serialize" productID and quantity)
Have you considered using a User Profile? .Net comes with SqlProfileProvider right out of the box. This would allow you to, for each user, grab their profile and save the temporary data as a variable off in the profile. Unfortunately, I think this does require your "Order" to be serializable, but I believe all of the options except Session thus far would require the same.
The advantage of this is it would persist through crashes, sessions, server down time, etc and it's fairly easy to set up. Here's a site that runs through an example. Once you set it up, you may also find it useful for storing other user information such as preferences, favorites, watched items, etc.
You should be able to create a temp file and serialize the object to that, then save only the temp file name to the viewstate. Once they successfully save the record back to the database then you could remove the temp file.
Single server: serialize to the filesystem. This also allows you to let the user resume later.
Multiple server: serialize it but store the serialized value in the db.
This is something that's for that specific user, so when you persist it to the db you don't really need all the relational stuff for it.
Alternatively, if the set of data is v. large and the amount of changes is usually small, you can store the history of changes done by the user instead. With this you can also show the change history + support undo.
2 approaches - create a complex AJAX application that stores everything on the client and only submits the entire package of changes to the server. I did this once a few years ago with moderate success. The applicaiton is not something I would want to maintain though. You have a hard time syncing your client code with your server code and passing fields that are added/deleted/changed is nightmarish.
2nd approach is to store changes in the data base in a temp table or "pending" mode. Advantage is your code is more maintainable. Disadvantage is you have to have a way to clean up abandonded changes due to session timeout, power failures, other crashes. I would take this approach for any new development. You can have separate tables for "pending" and "committed" changes that opens up a whole new level of features you can add. What if? What changed? etc.
I would go for viewstate, regardless of what you've said before. If you only store the stuff you need, like { id: XX, numberOfProducts: 3 }, and ditch every item that is not selected by the user at this point; the viewstate size will hardly be an issue as long as you aren't storing the whole object tree.
When storing attachments, put them in a temporary storing location, and reference the filename in your viewstate. You can have a scheduled task that cleans the temp folder for every file that was last saved over 1 day ago or something.
This is basically the approach we use for storing information when users are adding floorplan information and attachments in our backend.
Are the end-users internal or external clients? If your clients are internal users, it may be worthwhile to look at an alternate set of technologies. Instead of webforms, consider using a platform like Silverlight and implementing a rich GUI there.
You could then store complex business objects within the applet, provide persistant "in progress" edit tracking across multiple sessions via offline storage and easily integrate with back-end services that providing saving / processing of the finalised order. All whilst maintaining access via the web (albeit closing out most *nix clients).
Alternatives include Adobe Flex or AJAX, depending on resources and needs.
How large do you consider large? If you are talking sessions-state (so it doesn't go back/fore to the actual user, like view-state) then state is often a pretty good option. Everything except the in-process state provider uses serialization, but you can influence how it is serialized. For example, I would tend to create a local model that represents just the state I care about (plus any id/rowversion information) for that operation (rather than the full domain entities, which may have extra overhead).
To reduce the serialization overhead further, I would consider using something like protobuf-net; this can be used as the implementation for ISerializable, allowing very light-weight serialized objects (generally much smaller than BinaryFormatter, XmlSerializer, etc), that are cheap to reconstruct at page requests.
When the page is finally saved, I would update my domain entities from the local model and submit the changes.
For info, to use a protobuf-net attributed object with the state serializers (typically BinaryFormatter), you can use:
// a simple, sessions-state friendly light-weight UI model object
[ProtoContract]
public class MyType {
[ProtoMember(1)]
public int Id {get;set;}
[ProtoMember(2)]
public string Name {get;set;}
[ProtoMember(3)]
public double Value {get;set;}
// etc
void ISerializable.GetObjectData(
SerializationInfo info,StreamingContext context)
{
Serializer.Serialize(info, this);
}
public MyType() {} // default constructor
protected MyType(SerializationInfo info, StreamingContext context)
{
Serializer.Merge(info, this);
}
}
How would I store the value of a GridView's selected rows to a session variable?
From the codebehind file you will want to use something like this to access the underlying data item (MyDataItem) from the selected row.
MyDataItem item = (MyDataItem)GridView1.Rows[GridView1.SelectedIndex].DataItem;
Session["myItem"] = item;
Remember though, the gridview is already storing this data for you, so you may just want to access it from the GridView directly whenever you need it.
On a side note: can I stronly advise you NOT to use the session state.
Unless you are using it as a store where data is cached for the current user, which you can retrieve back at any time from e.g. a database.
If not, the "session" will come back and bite you. At some point there will be a user that leaves the browser open for longer time than your session lives (e.g. they get a telephone call, go out to lunch in a hurry, rush of to a meeting...). And when they return, they wish to complete what they are doing. And if you cannot restore all of your session data back at that point, you will either have to redirect your user to start over again (very annoying for your users), or you will have lost some information (very embarrasing), or the worst case, and most common case: your application will no longer work and crash (just plain: very bad).
It is a better approach to define small serializable objects that store your state (query parameters, selected items, etc) and use ASP.NET Viewstate to store that state across page requests. Note that most ASP.NET controls already use the viewstate to store their data. Then disable the Viewstate of your grids in the page, to vastly reduce the amount of data in your viewstate, and request the data upon each request (here it is safe to use the session or ASP.NET cache to improve performance of your application). You will have a much more robust and much more scalable application.
It is more work, but it will pay back very fast, and many times over.
ViewState only scope within one page. It's useful for postback problem, but not for cross-page problems. Session can handle both, but it has some limitations of security, lifetime, transmittion time... Depend on particular sittuation you can pick your right choice.