Should I store a database ID field in ViewState? - asp.net

I need to retrieve a record from a database, display it on a web page (I'm using ASP.NET) but store the ID (primary key) from that record somewhere so I can go back to the database later with that ID (perhaps to do an update).
I know there are probably a few ways to do this, such as storing the ID in ViewState or a hidden field, but what is the best method and what are the reasons I might choose this method over any others?

It depends.
Do you care if anyone sees the record id? If you do then both hidden fields and viewstate are not suitable; you need to store it in session state, or encrypt viewstate.
Do you care if someone submits the form with a bogus id? If you do then you can't use a hidden field (and you need to look at CSRF protection as a bonus)
Do you want it unchangable but don't care about it being open to viewing (with some work)? Use viewstate and set enableViewStateMac="true" on your page (or globally)
Want it hidden and protected but can't use session state? Encrypt your viewstate by setting the following web.config entries
<pages enableViewState="true" enableViewStateMac="true" />
<machineKey ... validation="3DES" />

Do you want the end user to know the ID? For example if the id value is a standard 1,1 seed from the database I could look at the number and see how many customers you have. If you encrypt the value (as the viewstate can) I would find it much harder to decypher the key (but not impossible).
The alternative is to store it in the session, this will put a (very small if its just an integer) performance hit on your application but mean that I as a user never see that primary key. It also exposes the object to other parts of your application, that you may or may not want it to be exposed to (session objects remain until cleared, a set time (like 5 mins) passes or the browser window is closed - whichever happens sooner.
View state values cause extra load on the client after every post back, because the viewstate not only saves objects for the page, but remembers objects if you use the back button. That means after every post back it viewstate gets slightly bigger and harder to use. They will only exist on he page until the browser goes to another page.
Whenever I store an ID in the page like this, I always create a property
public int CustomerID {
get { return ViewState("CustomerID"); }
set { ViewState("CustomerID") = value; }
}
or
Public Property CustomerID() As Integer
Get
Return ViewState("CustomerID")
End Get
Set(ByVal value As Integer)
ViewState("CustomerID") = value
End Set
End Property
That way if you decide to change it from Viewstate to a session variable or a hidden form field, it's just a case of changing it in the property reference, the rest of the page can access the variable using "Page.CustomerID".

ViewState is an option. It is only valid for the page that you are on. It does not carry across requests to other resources like the Session object.
Hidden fields work too, but you are leaking and little bit of information about your application to anyone smart enough to view the source of your page.
You could also store your entire record in ViewState and maybe avoid another round trip to th server.

I personally am very leery about putting anything in the session. Too many times our worker processes have cycled and we lost our session state.
As you described your problem, I would put it in a hidden field or in the viewstate of the page.
Also, when determining where to put data like this, always look at the scope of the data. Is it scoped to a single page, or to the entire session? If the answer is 'session' for us, we put it in a cookie. (Disclaimer: We write intranet apps where we know cookies are enabled.)

If its a simple id will choose to pass it in querystring, that way you do not need to do postbacks and page is more accessible for users and search engines.

Session["MyId"]=myval;
It would be a little safer and essentially offers the same mechanics as putting it in the viewstate

I tend to stick things like that in hidden fields just do a little
<asp:label runat=server id=lblThingID visible=false />

Related

Using a class in PostBack

I am totally new to classes and OOP, so please bear with me.
I am creating a large scale web app which I'm trying to keep tidy by creating my own classes.
For instance I have a Public Class Product which has several properties. One way I am using it is on page load a product ID is assigned to the ID property which in turn gets the details for that product and assigns the various data to the other properties. So within my code I can used for example product.price or product.description and get the appropriate values. This has worked fine, but I found that because the class was initiated on page load it was getting the data from the DB each time that the page refreshed. I stopped this by using an If Not IsPostback to initiate the class. This meant that the data was pulled in only on the initial page load. So far so good.
I then needed to compare a value in a textbox with a property of the product. I have a textchanged event with
If textbox1.Text <> product.description Then....
but here I get a wavy line under product.description and VS2010 is saying that the object is not defined. Its Dim'd in the page.load so I moved the Dim statement outside the page class so that it will be accessible to all events on the page.
The dim statement is Dim product as New product
In my not ispostback chunk of code I have for example product.ID = 1 which will get all the product properties for product 1
The wavy line has gone but when I run the page all works fine on page load. Data is displayed so my product class is working fine. As soon as I make a change in textbox1 and the event triggers product.description is nothing. It got reinitalised.
How do I stop this from happening...
Your "Product" is not persisted between postbacks.
Only control objects in aspx page are persisted/restored automatically.
To remedy this there are multiple approaches.
If Product is loaded via setting "Product.id=1" then what I woudl do is have a hiddenfield that receives the value of the product.id during prerender event (to save it in the page) and in an init event I would restore the "Product.id=hiddenfield.value" but only when it is a postback to reload your object.
EDIT
Thanks for picking my answer. I'll elaborate a little on the various ways to deal with this and why I suggested my answer.
Store Key in HiddenField Reload from DB:
PROS: Product is always Fresh/Correct/Current values. Corresponding to the database. Databases are very efficient to return a record based on a primary key. Very little data is sent to and posted back from the client browser. Low complexity. Each page opened by the client is safely isolated.
CONS: Multiple database transactions. If the DB is already strained or extremely massive you may need to consider even the smallest efficiency gain, but this is not common or likely on a primary key based record
Session State (store entire object):
PROS: Lowest time to "load" object since it's available in memory already once loaded. Less DB Transactions. No data piggy backed to the client and back again.
CONS: Object can become "out-of-date" if altered in the DB. Users who open multiple pages of your application can end up getting the wrong object if both require a different "Product", so instead to be totally safe you need a more complex structure in place to store more then one product or store them based on some kind of key (such as the product ID). Server Memory is used, if serving thousands of users or your product data is large it can become an issue, especially if you do this in many pages with many objects.
Serialization (store the entire object in the page in a field, similar to event state):
PROS: Once loaded, the Database is accessed only once for a specific product, then the product is held, in it's entirety inside the page, it is recreated by the server from the data in the field or via viewstate. Each page opened by the client is safely isolated. Is fairly easy to implement storing in ViewState of the Page.
CONS: Object can become "out-of-date" if altered in the DB. ALLOT more data is added to your page responce and the users next page request. Is more complex to implement because the object needs to be designed to be serialized correctly. Complex objects require allot of manual code to be serialized successfully.
again, there are many other ways to deal with this, such as storing items in a synclocked dictionary style object global to the application, but is considerablby more and more complex as you go.
This is likely the standard ASP.NET page life cycle problem.
After you initialize the page, it gets sent to the user's browser. When the user clicks on something, the browser sends a postback request back to your application. The view state allows the textbox1 object to remember what was in its Text property. However, your Page_Load ran from scratch, and, yes, everything including your product object got recreated from scratch.
If you want your product object to "remember" what it knew before the postback, you'll have to remind it. One way would be to store the initialized value in Session state, and then refresh your product object during the postback section of the Page_Load method.
Every time you do a postback, you're working with a new instance of your page class. The prior copy of your class was thrown away and probably disposed before your browser even rendered the page to the screen.
If you want to persist a value across http requests (of which postbacks are just one type), then you need to put it somewhere like the Session.

Retain Dynamic dropdown values

I have 3 dyanmically generated dropdowns in this aspx page. The 2nd and 3rd one are populated as per the selected value of the first one (I've the code for creating the 2nd and 3rd dropdown in 1st one's selectedindexchanged event)
How do I write the code in a such a way that when I traverse back to the page, the dynamic dropdowns retain their selected values?
I'm assuming that what you mean when you say that you "traverse back" to the page is that you navigate to a different page on the site and come back to this page that it's dropdown values will be filled in with what the user selected.
Remember that HTTP is an inherintly statless protocol in that it won't remember data in between postback to the servers. In order to overcome this limitation ASP.NET and other web frameworks use various ways of saving data between request. Currently you are relying on "ViewState" that is stored within the page as a hidden variable called __VIEWSTATE (look at the page source sometime to get an idea of what field looks like) this scope of this hidden variable is when the page first gets loaded and everytime you do a postback to the same page. From your description you probably need a longer term persistance called SessionState or Cookies that will store values for a particular Session.
Here is a link from MSDN that contains interesting information regarding all the possible ways of saving state in an ASP.NET application. Let me know if you've got any other questions.
http://msdn.microsoft.com/en-us/library/75x4ha6s.aspx
--EDIT--
Here's a link to the MSDN article on Session State. My recommendation is to be careful with Session state and only store things that are absolutely required. Also I'd recommend you have a Class that contains the a bunch of constant for the Session Keys. It's easier to manage
http://msdn.microsoft.com/en-us/library/ms178581.aspx
ie instead of
string value = Session["Key"];
//Create a class SessionKeys
Class SessionKeys{
public const string SESSION_KEY = "Key"
}
//Now that string is strongly typed and you don't have to worry about misspelling it
string value = Sesssion[SessionKeys.SESSION_KEY];

How to save value across postbacks for a composite control without using viewstate

I have a composite control that has a couple of private fields that reference values in the cache and these private fields are called during the constructor method. Since a string key is used to identify the value in the cache, I must have a way of storing that string key in such a way that it is available at the time the control is instantiated, and I have to be able to reference it on postbacks without it changing.
In addition, this key is generated the first time the control is loaded, but it should not be changed again after that first time.
How can I accomplish this?
I have already tried saving it to viewstate, but that doesn't work because viewstate is not yet available at the time the control is instantiated.
I have tried using a private field and then checking against Page.IsPostback in the constructor and if it isn't postback, I assign a value to the private field, but on subsequent postbacks it looses it's value, and I can't reassign it in the Page.IsPostBack again because it is an autogenerated GUID.
This has got to be something folks have had to do before....
There isn't a lot of state info available during control construction at all, so this could be difficult. Is there some reason you can't move your code which accesses the Cache'ed info into the control's Init event?
I assume you can't use Session because the information stored is related to that specific request/postback. If it's not specific to that request, using Session could be a possibility - but I think you may encounter other problems trying to deal with control state so early in the lifetime.
After seeing your comment to the other answer; you should be able to move your code that checks for the cached datasource into the control's Init or even Load event, so the state will be available.
Also, incidentally; are you sure you really need to cache this data? That could end up taking up a lot of server memory.
Have you tried Session?
You can store anything you like in the session object for one particular user, maintaining the value / object between postbacks.
If you want to store on a global basis and not per ser basis, try Application
Although this isn't the best solution (rearranging your logic to fit the lifecycle model generally is), have you tried accessing the Request directly? I once really wanted to get the selected value off a DropDownList very early in the lifecycle so I could adjust some elements in the building, and I did it like this:
myDropDownList.SelectedValue = Page.Request.Form[myDropDownList.UniqueID];
So instead of waiting for the viewstate to load the server-side proxie's values, I just got it myself from the client-side control value that was passed in on the post. I probably would do things differently if I redesigned that page, but it seems to have worked out alright for now and it solved the problem I was having.

building dynamic controls on postback (asp.net)

I'm sure this has been answered but I cannot find it...perhaps because the terms are used for so many differing questions...anyway.
how is the best way to store information about building dynamic controls on postback etc in the init event...
e.g. take the classic "questionnaire" scenario...question.aspx?id=1
get the id from the querystring
load the questions from where ever and build controls dynamically,
or dynamically add each question
user control for how ever many
questions etc
page posts back (user presses submit I guess!)
in the init event...where should you have already stored the id to
recreate the question controls?
things i've tried/thought of so far:
I initially stored the value in
viewstate as it's relevant to the
page, but the viewstate value isn't
available in page init (or is it??)
a hidden variable and then accessing the value in the form collection
session - I don't want to use session, seems problematic, e.g. what if the user views two questionnaires?
the querystring again? but that seems too easy to be changed and the dynamic controls just wouldn't make sense...
in the controls themselves? e.g. disregard the questionnaire id all together and simply go off the question id perhaps?? assuming that control ids would be in some format like controlQ# e.g. controlQ1, controlQ2. this means that each question id must be unique
any help/guidance etc much appreciated!!!
The query string will still be available during a postback, so you could take the ID from there.
If you are concerned about users manipulating the query string parameters, then maybe you should add some additional parameters which will allow you to verify the correctness of the query string during a postback. As a simple example, you could add a MD5 hash of the ID parameter and the current session's ID, and recreate/compare that checksum during a postback.
try to create/load the usercontrols in the page OnLoad.
if you want to load inside a usercontrol other usercontrols use the OnInit event.
use the viewstate for the persistence of the information you will need to re-instantiate the usercontrols during the postback. to map the postback data to the controltree you have to ensure that you have the same control-tree. after that mapping you could change your controltree (load some other controls e.g.)
contols-id: yes, you have to give them unique ids, otherwise it will not work. if you store your questions in a database, there should not be any problem to use the database-primary key for this, so name them e.g. question-1, question-2 (the controls)

What is the best alternative for QueryString

We heard a lot about the vulnerabilities of using QueryStrings and the possible attacks.
Aside from that, yesterday, an error irritated me so much that i just decide to stop using QueryStrings, i was passing something like:
Dim url As String = "pageName.aspx?type=3&st=34&am=87&m=9"
I tried to
Response.Write(url)
in the redirecting page, it printed the "type" as 3, then i tried it in the target page, it printed 3,0....i know this can be easily dealt with, but why? i mean why should i pass 3 and have to check for 3.0 in the next page's load to take my action accordingly???
So what should we use? what is the safest way to pass variables, parameters...etc to the next page?
You could use Cross-Page Postbacks.
Check also this article:
How to: Pass Values Between ASP.NET Web Pages
There are many options you can use, most of them requires you to build a strategy to pass variables between pages.
In most projects I use this strategy, I create a formVariables class to hold currently active items. it has properties which you will need to pass by querystring. and I store this class at session. and in my base page I read it from session. so in every page I get values over this object. the only negative thing about this method is to clean up items when you finished your work on it..
hope this helps.
I would sugest you avoid using Session to pass variables between pages as this breaks the stateless model of the web.
if you have just stored some values in session that relate to a certain page then the user uses their browsers back button to go back to the same page whcih should have a different state then you are not going to know about it.
It leads to the possibility of reading session values that are not relevant to the page the user is currently viewing - Which is potentially very confusing for the end user.
You will also run into issues with session expiration if you rely on it too much.
I personally try to avoid using session where possible in preference of hidden form values + query strings that can be read on postback + navigation.
The best / most secure way to pass info between pages is to use the session.
// On page 1:
this.Session["type"] = 3;
// On Page 2:
int type = (int)this.Session["type"];
You can store any kind of object in the session and it is stored on the server side, so the user can't manipulate it like a query string, viewstate, or hidden field
You said:
it printed 3,0....i know this can be easily dealt with, but why? i mean why should i pass 3 and have to check for 3.0
There's a difference between "3,0" (three comma oh) and "3.0" (three point oh). You also said that you were "passing something like".
In a query string, if you pass multiple values in the same key, they will be seperated with commas.
As all values are passed as strings there's no way that an int "3" is going to magically become decimal "3.0" unless you parse it as such when you request it.
I'd go back and double check what you are passing into your URL, if it ends up as something like:
pageName.aspx?type=3&st=34&am=87&m=9&type=0
Then when you read back
Request.QueryString["type"]
You'll get "3,0" back as the comma seperated list of values in that key.
First, in asp .net you can use several strategys to pass values between pages. You have viewstate too, however the viewstate store the value and the use is in different scenarios , you can use it too. Sessions instead, and of course by post in a form.
If your problem is the security, I recommended you to create 2 users for accesing the data. One user with read only access, this for accessing the pages ( Sql Inyection prevent ) and validate the data throw the querystring. And One with write access for your private zone.
Sorry, for my unreadeable English.
I like to use query string as I like users to be able to bookmark things like common searches and the like. E.g. if a page can work stand-alone then I like to it to be able to work stand-alone.
Using session/cross-page postbacks is cool if you needed to come from another page for the page you're on to make sense, but otherwise I generally find querystrings to be the better solution.
Just remember that query strings are unvalidated input and treat them with the caution you would treat any unvalidated input.
If you do proper security checks on each page load then the querystring is fine and most flexible IMHO.
They provide the most flexibility as the entry poitn to a page is not dependant on the sender as in some other options. You can call a page from any point within your own app or externally if needed via querystrings. They can also be bookmarked and manually modified for testing or direct manipulation.
Again the key is adding proper security and validation to the querystring, and not processing it blindly. Keep in mind that the seucirty goes beyond having edit or read access, depending on the data and user, they may not have access to the data with thos paranters at all, in cases where data is owned and private to specific users.
We have tried various methods, in an attempt to hide the querystring but in the end have gone back to it, as it is easier to do, debug, and manage.

Resources