I've created an asp.net page for work that allows customers to look up users in AD, and then request to have them added as delegates to Rightfax numbers, which can also be searched for. Rightnow I'm storing public variables used by the back end and front end of the project in a CurrentSession class inherited by the pages thats supposed to be unique to each user, but I'm still seeing occasional issues where variables will 'bleed' from one session to another. Sometimes I'll go to the page and the list of AD users is already populated with users from another session\user.
I'm wondering what the best method is for storing variables in this scenario. Should I be using cookies rather than a current session class? Are there any good guides/tutorials that go over variable management for asp.net pages? I'm typically a desktop developer so I'm not particularly familiar with this kind of issue.
the whole jump to the web, and that of session managment, or varible managment is a huge topic. And it is often a challenge, since the concpets are very different then desktop.
So, I mean, when a user clicks a button, the web page is posted up to the server. The page is found, loaded, variables start from scratch. Code behind runs, page is sent back down to client AND THE PAGE SERVER SIDE IS TOSSED out!
So, the above is a new challenge, due to that so called "state-less" nature of web pages.
As for session bleeing to other users? Hum, that should not occur. However, session() can be VERY fragle. Due to time outs, due to some execution error in code, the app pool can and will often re-start.
So, I strong recommend that you run the script and turn on SQL servre based sesison management. Once done, then session() becomes bullet proof - and always works.
As for values bleeding to other users? No way - I not seen that.
(of course, you did not mention or note what authentication provider you are using (or even if users have to logon).
However, while I OFTEN use session, and even to pass values from one page to the next? (absolute hate parameters in the URL - messy and often security risk if things like PK id etc. are included)).
However, some big sites say like amazon use parameters and values in the URL a lot. They do this since then the server side does not get over-loaded and have to keep track or hold those values. However, unless you building the next facebook, or huge + high volume web site, then session() is quite much the standard approch to keep values active for your code.
However, lets assume we toss up a grid, and the user selects that product?
We set in session() say that PK id, and hten jump to the next page say to buy that house?
Well, now if you open a new tab - even a different browser, launch that grid, select a house and jump to the page to display that information? You can't use session since as noted it will overwrite the values in the other page.
So, you can try and build a bunch of fancy classes and all kinds of handstands, but I just simply transfer the session() values to ViewState.
ViewState is per page, and session() is global to that one user.
So, if I need 10 variables and value for a given part of the application, say like this:
<Serializable>
Public Class clsProjectInfo
Public ContactID As Integer = 0
Public ContactGeneralID As Integer = 0
Public PortalComp As String = ""
Public ProjectHeaderID As Integer = 0
Public QuoteNum As Integer = 0
Public UploadGroup As Integer = 0
Public txtNotes As String = ""
Public ProofID As Integer = 0
End Class
So say in the page that we select the project - setup a whole buch of values?
Then the above var will be in session(), but ONLY for pasing to the next page.
So the project view page that needs all of the aobve values to work?
The page load code will look like this:
Dim cPinfo As New clsProjectInfo
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If IsPostBack = False Then
cPinfo = Session("Pinfo")
ViewState("Pinfo") = cPinfo
Else
cPinfo = ViewState("Pinfo")
End If
So we only EVER use session to pass that "bunch of values" to the next page, but always first thing we do is transfer from session() to viewstate. That way if the user opens another tab, or selects a different house in that 2nd browser, jumps to the view details, we only ever used session() to pass the values to the next page, but from that point on, always used ViewState.
Now the above simple idea might not work for all cases but it does for most.
So, don't adopt huge number of session() values, and as always even in desktop, global vars and values should not be required.
So session() for a given user will most certainly often "stomp" on top of other parts of the application. If session() is spilling over between different users? That should not occur, never occur and means as noted something else is going wrong here.
So even for desktop software? Each form, or page or part of the application tend to have and need a set of values. So, I build plane jane simple class as per above. And then you can with great ease pass ONE class thing with the 5-10 variables in it. that way I don't wind up with 50 variables in session() - which is a nightmare from coding point of view (let alone to remember the varaiables). But with above passing the one class, then you just passing ONE thing and you get intel-sense too boot.
And no doubt that group of variables often has to be passed to routine. So beofre above, I often like had to pass like 5-6 values to some function or sub - and what a pan.
So old way:
Call SaveAfterUpload(AjaxFileUpload1, session("QuoteNum", session("ContactID",
session("UploadGroup"), strCleanFile, session("txtNotes"), session("PortalComp")
but now we can go:
Call SaveAfterUpload(AjaxFileUpload1, cPinfo.QuoteNum, cPinfo.ContactID,
cPinfo.UploadGroup, strCleanFile, cPinfo.txtNotes, cPinfo.PortalComp)
But, then again, since we have that class, then above now becomes
Call SaveAfterUpload(AjaxFileUpload1, cPinfo)
So, don't put a truck load of values into session().
Create "groupings" of the values.
And what is even SUPER great?
often some of the client side JavaScript code needs those values.
So, you THEN wind up dropping in a boatload of hidden fields or hidden cnotrols for those values.
But, with the above class? I can pass + have the whole mess client side like this:
cPinfo = ViewState("Pinfo") ' this no doubt occured on page load
' copy Pinfo to browser side
MyUpLoadInfo.Value = JsonConvert.SerializeObject(cPinfo)
MyUpLoadInfo is just a simple asp.net hidden field like this:
<asp:HiddenField ID="MyUpLoadInfo" runat="server" ClientIDMode="Static" />
But, now in place of those 5-6 hidden fields, I have the above cPinfo now for use in the client side.
eg this:
MyUpLoadInfo = document.getElementById("MyUpLoadInfo")
Pinfo = JSON.parse(MyUpLoadInfo.value)
// now I have all values such as
Pinfo.txtNotes
Pinfo.QuoteNum
So, by building that class or set of variables, I can now pass down the WHOLE mess in one shot to the client side, and now my JavaScript code can with great ease use all those variables client side!!
And it turns out that for each section of a typical application?
about 5-10 variables are only required
often they are required client side - and with above we can
Most if not ALL of the routines in that application part need those vars
(including subs and functions, so now we can pass 5-10 values, and we don't
have huge long list of messy parameters in all of those subs and functions).
We can modify the class - add more variables, and the dozen routines now all have that extra variable - yet we don't change the code, or even change the sub/function calls to have that extra new variable in that code.
(and this applies to client side js code if we need/require that group of values for the browser code.).
So we thus don't have huge numbers of global vars.
we don't wind up with a gazillion number of separate session values.
we vast improve the ability to pass those values to subs/functions.
And we even can pass that set of values to the client side with great ease.
So any global var can be in session, but those global vars are NEVER to be used for passing value from page to page and code calls for a given part of your application.
And if you want to support more then one page or browser by the user? Then adopt the standard that on first page load you transfer that session class to viewstate.
Related
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.
If I use a protected variable, does the variable exist for the whole web application or does it get removed when the user move to other page by get or post? I do know that it is not accessible in other pages unless I use static variable, but I am curious as to if it exists for the whole application. Please let me know!
when you move to other page and return, a new instance of your page class will be created and so all non static variables will be reset.
The value will be valid in a one request process life time (starts with request start and ends with request end)
making a variable protected, just means that this variable is access-able in inherited class. for example in asp.net you can use it in inherited class like inside your markup (because it inherits code behind class)
this is the meaning of protected variable
if you want to keep a value saved between pages you can use one of these items depending on your requirement :
Cookie
Query String
Session States
Application States
Cache
and ViewState keeps state variable between postback in a same page or control while it is not redirected to another page.
protected keyword does not determine how long a variable exists nor does it determine whether it will be available in the next post back.
What you are probably looking for is state management.
Take a look at this webpage to see how you can maintain state between post backs, different pages etc.
Also take a look at this page to determine which state management feature to use in which situation.
In general, "page" variables only live through the duration of the request. If your variable is static, there will only be one instance of the variable for all the requests until the app domain unloads.
If your variable is private or protected, no other classes will have access to it.
Your question, however, seems a little strange. What's your concern?
Good afternoon ladies and gents --
I've been tasked with finding and fixing a bug in an unfamiliar legacy application that had some recent changes made to it, but I don't have an easy way (that I know of) to test my theory. I'm hoping your collective knowledge will verify the test for me.
This application lazy loads lookup lists (tongue-twister?) into DataTables from a database and stores them as an object in HttpContext.Current.Application (an HttpApplicationState).
Before the changes were made, one of the lookup tables was bound to a DropDownList in the following manner (contrived):
Me._lookupList = TheSession.LookupCache.SomeLookupListName.DefaultView
...
ddl.DataSource = Me._lookupList
where 'SomeLookupListName' is a read-only property that returns a DataTable from HttpContext.Current.Application. The changes added some code that filters the private Me._lookupList (DataView) before being bound to the DropDownList:
Me._lookupList.RowFilter = "SomeTableIDColumn <> " & ...
What's happening, if you haven't guessed it already, is that that DataView is now filtered for every user of the application. I looked around the code and found that most other lookup lists are copied to local members in this fashion:
Me._lookupList = New DataView(TheSession.LookupCache.SomeLookupListName)
Since I don't know how to attack my local debug session pretending to be multiple users, will changing the code to use the latter method actually be any different than the former? Does filtering the result of DataTable.DefaultView actually apply the filter to the underlying DataTable differently than if wrapping the table with a New DataView(...)?
Would it make sense to simply clear the row filter after the DropDownList is bound (seems like a bad solution)? I'd like to stick to the ugly conventions this application uses so that I don't surprise another developer down the road who gets a similar task, otherwise I'd just bypass the application state and grab the items right out of the data repository.
I appreciate your feedback.
Does filtering the result of
DataTable.DefaultView actually apply
the filter to the underlying DataTable
differently than if wrapping the table
with a New DataView(...)?
Yes. It creates a new view which the filter is applied to. The filter is not applied to the table directly. Following the pattern to use the new view will work.
BTW, easy to test multiple sessions against your debugger. Just open up two different browsers (IE and FF) and point to the same app. User login might be the same, but the sessions will be unique.
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.
Just trying to gather thoughts on what works/doesn't work for manipulating Business/Domain objects through an ASP.NET (2.0+) UI/Presentation layer. Specifically in classic ASP.NET LOB application situations where the ASP.NET code talks directly to the business layer. I come across this type of design quite often and wondering what is the ideal solution (i.e. implementing a specific pattern) and what is the best pragmatic solution that won't require a complete rewrite where no "pattern" is implemented.
Here is a sample scenario.
A single ASP.NET page that is the "Edit/New" page for a particular Business/Domain object, let's use "Person" as an example. We want to edit Name and Address information from within this page. As the user is making edits or entering data, there are some situations where the form should postback to refresh itself. For example, when editing their Address, they select a "Country". After which a State/Region dropdown becomes enabled and refreshed with relevant information for the selected country. This is essentially business logic (restricting available selections based on some dependent field) and this logic is handled by the business layer (remember this is just one example, there are lots of business situations where the logic is more complex during the post back - for example insurance industry when selecting certain things dictates what other data is needed/required).
Ideally this logic is stored only in the Business/Domain object (i.e. not having the logic duplicated in the ASP.NET code). To accomplish this, I believe the Business/Domain object would need to be reinitialized and have it's state set based on current UI values on each postback.
For example:
private Person person = null;
protected void Page_Load()
{
person = PersonRepository.Load(Request.QueryString["id"]);
if (Page.IsPostBack)
SetPersonStateFromUI(person);
else
SetUIStateFromPerson(person);
}
protected void CountryDropDownList_OnChange()
{
this.StateRegionDropDownList.Enabled = true;
this.StateRegionDropDownList.Items.Clear();
this.StateRegionDropDownList.DataSource = person.AvailableStateRegions;
this.StateRegionDropDownList.DataBind();
}
Other options I have seen are storing the Business object in SessionState rather than loading it from the repository (aka database) each time the page loads back up.
Thoughts?
I'd put your example in my 'UI Enhancement' bucket rather than BL, verifying that the entries are correct is BL but easing data entry is UI in my opinion.
For very simple things I wouldn't bother with a regular post back but would use an ajax approach. For example if I need to get a list of Cities, I might have a Page Method (Or web service) that given a state gives me a list of cities.
If your options depends on a wide variety of parameters, what your doing would work well. As for storing things in Session there are benefits. Are your entities visible to multiple at the same time? If so what happens when User A and User B both edit the same. Also if your loading each time are you savign to the database each time? What happens if I am editing my name, and then select country, but now my browser crashes. Did you update the name in the DB?
This is the line I disagree with slightly:
this.StateRegionDropDownList.DataSource = person.AvailableStateRegions;
Person is a business/domain object, but it's not the object that should be handling state/region mapping (for example), even if that's where the information to make the decision lives.
In more complicated examples where multiple variables are needed to make a decision, what you want to do in general is start from the domain object you're trying to end up with, and call a function on that object that can be given all the required information to make a business decision.
So maybe (using a static function on the State class):
this.StateRegionDropDownList.DataSource = State.GetAvailableStateRegions(person, ipAddress);
As a consequence of separating out UI helper concerns from the Person domain object, this style of programming tends to be much "more testable".