DropDownList Data Items Shared With All Web Users? - asp.net

I'm designing a custom ASP.NET drop down list for a Web application to display a list of a lot of organizations.
Btw: I reviewed every other SO question related to dynamically creating a drop-down-list and I couldn't find an answer.
Since there's a lot of organizations I want to build the item list as fast as possible.
Here's my ideas:
I'd share the list like this:
Imports System.Web.UI.WebControls
Public Class Organizations
Inherits DropDownList
Private Shared m_Organizations As List(Of Organization) = Nothing
Public Sub CreateChildControls()
' If list not populated get list from database.
If m_Organizations Is Nothing Then
m_Organizations = Get_Organizations()
End If
For Each Org As Organization in Organizations
Item_Obj = New ListItem(Org.Name, Org.OrgId)
Items.Add(Item_Obj)
Next
MyBase.CreateChildControls()
End Sub
End Class
I did a test. I created a single web page with two copies the drop down list. I ran the web page.
On initial startup of the web page I see m_Organization Is Nothing and Get_Organizations is called.
When I select an organization I see m_Organizations IsNot Nothing so Get_Organizations is not called. That's what I wanted.
When I click on a Submit button on the test page I see m_Organizations IsNot Nothing so Get_Organizations is not called. That's what I wanted.
So Organizations is persisted across page postbacks.
Now my question.
Suppose 3 users are using the web application at the same time and they display the web page with the Organizations drop down list.
Will m_Organizations be shared with all three web applications so it is loaded only once for all three users?
Or, is each user in their own process and therefore GET_Organizations will be called a total of three times?
Thanks, Ed

Each user has their own web page, controls, and code values. They each get their own code copy and their code runs just FOR each user.
So, to load up the dropdown list? It not clear why you pulling into a list?
The base .net objects such as row, and dateable run a lot faster then lists. And these base types can be directly binded to the drop down list (no need for a loop).
So, your on load would be something like:
So, in page load event, you have this:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If IsPostBack = False Then
Me.Instutions.DataSource = Myrst("select ID,Institution from Institutions")
Me.Instutions.DataBind()
End If
End Sub
So, only on the page load do we fill up the combobox (drop down list). You check the "isPostback" value.
Now it possible that you using datasets (or the newer EF) for the data. But even in that case, you can get/grab the data table from that, and shove it directly into the dropdown list data source. You as a general rule will NOT run loops to fill these types of controls (gridview, listview, dropdownlist). They can be bound directly to the result of the data source without loops. And since each users will get their own copy of the data, and your code runs for each user? Then you do want to avoid things like loops - since that code will run separate for each user and each page post back.
So yes, each user gets their own web page, gets their own variables, and runs their own code 100% separate from each user. And that includes a copy of the datasets you have.
When the user selects a value from the drop down list? (yes, we have auto postback = true), and thus the code for that wlll look like:
Protected Sub Instutions_SelectedIndexChanged(sender As Object,
e As EventArgs) Handles Instutions.SelectedIndexChanged
' pk id from drop list = Instutions.SelectedValue
' text value from drop list = Instutions.DataTextField
End Sub
So, it only takes two lines of code to load up the dropdown list - even when you don't have a dataset, or are using EF.
Now, I did use a helper routine called myrst(), and it can be used to fill dropdown lists, datagrids, listviews etc. and once again only two lines of code, and no looping.
that "handy" routine is this:
Public Function Myrst(strSQL As String) As DataTable
Dim mycon As New SqlConnection(My.Settings.Test4)
Dim oReader As New SqlDataAdapter
Dim rstData As New DataTable
oReader.SelectCommand = New SqlCommand(strSQL, mycon)
Try
oReader.Fill(rstData)
Return rstData
Catch
Debug.Print(Err.Description)
Return Nothing
End Try
End Function
So when they select a value in the drop down list? You do get a full page post back, and your on-load event will run. And yes, EACH user has their own web page, and own copy of that code running - they are not relegated to each other in ANY way, and all local, global variables and code is run 100% in a separate session for each user. And each user also has their own Session() variables - and again they are 100% separate and not shared between all users.
So yes, all 3 users will each call and each load up their own copy of that data. (yes, it called 3 times). if that was not the case, then what would occur if you wanted to filter only companies or records belonging to the one user? so you can consider all code and even loading of data a 100% separate thing/concept and process for each user.

Related

How do I separate data in browser sessions when my form is using a gridview in asp.net

I am not sure if my question is clear but let me explain. First off I am not a professional programmer and I know that I am doing things wrong. I have created a form to collect contact information which has a gridview. My page does a few postbacks while I am collecting info. When there is one user and one browser everything works great. When I have multiple users trying to input data my app doesn't work as I want. So the first problem which I know is a problem is that I am using some static variables to store info. Point me in the direction I need to go in order to program correctly. The second issue and probably related; the gridview will display all information across browsers. Meaning if user A inputs info it will show in the gridview of user B across the globe. How do I make each person to have their own instance of the app? This is my real question. Any resources or guidance would be greatly appreciated.
Well I was trying to localize my variables so that they are not static but I am not sure if this is the only problem. I am thinking about viewstate but I am just not sure.
As you found out, using static variables will not work. Not only will it mess up your code with other users, those static variables "sort of" and "hope on a wing and prayer" will persist, but sometimes they do not!!!
so, static varabiles not only don't work with multi-user, but they can un-expected go out of scope.
The only use of a static class in .net and webforms?
For a shared code module. So in vb.net that means my "genreal" set and hodge podge of routines, or in c# we use a static class, and this is 100% fine as long as you NEVER use class varibles in those static classes (or code modules in vb.net), then you are fine.
So, for all kinds of general shared code, then a vb code module, or a c# static class is 100% fine. (Those routines can be passed values, and have local vars, but NEVER class scoped variables (since you never have to create an instance of such classes, then take off variables scoped to that given class, and you are just fine.
Ok, next up, what about persisting values in each page?
Well, first up TOP GOAL is to reduce the amount of persisting you have, and only persist things that you absolute must.
Now, most controls, including the grid view have automatic persistance built in.
However, this introduces our first design rule and pattern:
In the web form's page load, you are 100% free to load up the gridview, listbox(s), combo boxes (dropdown list) etc., but you ONLY want to load these things up ONE time and ONLY on first page load.
That means and results in the last 200 web pages I have built, that EVERY page has a "real" first load code stub.
That means this:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not IsPostBack Then
LoadGrid()
End If
End Sub
Sub LoadGrid()
Dim strSQL = "SELECT * FROM tblHotelsA ORDER BY HotelName"
GridView1.DataSource = Myrst(strSQL)
GridView1.DataBind()
End Sub
So, in above examle, I wanted to load up the GV with data.
Note the if not IsPostBack stub. EVERY one of my pages has such a code stub.
So, what this means is the page can survive post-backs, and a dropdown list etc will correctly save/persist their values.
However, so far, that does not cover variables and things say like a PK value.
So, the next two choices that are the bread and button choices are:
Session() - server side - per user.
ViewState() - client side browser - per web page.
Grasping when to use session() vs ViewState is quite much the Rossetta stone that un-locks how you can persist values, but not have values over write or clash with other web pages that a SINGLE user might have open.
So, I tend to follow this design pattern
Session() - use that to pass values to another page, but on the above first page load, transfer those values to ViewState().
Why?
Because session is global to teh one user.
lets take a typical + super simple example.
I have a list of hotels, user will click on a row - say to book a room in that hotel. When they select the one row from the GV, then we will jump to the next (differnt) web page that shows details about the hotel room etc.
So, I can use session() to PASS the hotel PK id to the next page, but I cannot use session() for that page to "operate" based on that PK session value.
Why?
Because what happens if they have two copies of the browser open, or have two tabs open on the list of hotels page, and then click on a row.
if they have two browsers (or 2 tabs - same case in regards to session(), then I can now have two web pages open on the hotel details/information.
But, if the code behind uses/assumes/operatates based on that session("HotelPK") value, then I have two pages open running that same code!!
Now, if you buying a house, you may well wind up buying the wrong house!!
Or you have two web pages open, code assumed each has HotelID, but session() is global to the one user.
So, session is great for persiting values global to the one user, but NOT for code that needs some values persited for the ONE page.
So, I will (and do) use sesison all the time to pass values to the next web page, but FIRST task is to then transfer that value to viewstate(), and hten all code on that page operates/uses/consumes ViewState to function.
So, say I have this GV of hotels:
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
DataKeyNames="ID" Width="100%" CssClass="table table-hover" >
<Columns>
<asp:BoundField DataField="FirstName" HeaderText="First Name" />
<asp:BoundField DataField="LastName" HeaderText="Last Name" />
<asp:BoundField DataField="City" HeaderText="City" />
<asp:BoundField DataField="HotelName" HeaderText="Hotel Name" />
<asp:BoundField DataField="Description" HeaderText="Description" />
<asp:TemplateField>
<ItemTemplate>
<asp:Button ID="cmdEdit" runat="server" Text="Edit" CssClass="btn myshadow"
OnClick="cmdEdit_Click" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
Above page load code is used, and I now have this:
Ok, so now we click on that View button, and we will grab the PK value
(Which by the way is NEVER exposed to the client-side browser for reasons of security).
Now, when you hit view, we have to store/save/have use of/pass the Hotel PK id selected from above. So, we will "need" to persist that value in the code that operates on that ONE hotel we selected.
As noted, we have several options:
HiddenField - this actually uses viewstate, but is OFTEN handy for persisting values, and nice is client-side JavaScript (js) can use (or even set) such values, they persist and the values in a hidden field can be used by both client-side code, and server-side code. This of course is a "per page" persisting. This choice can't (should not) be used for things like row PK values, but for "low" security values and variables, a HiddenField can be a great choice.
The other way to persist values is to place/put them in the URL as paramaters. I think that looks ugly as can be, but worse, once again, this only works for values that of "low" value.
So, to pass a value to the next page, we can use several approaches, but session() is really good, per user, but ONCE we have that new page loaded, then we use ViewState.
So, the code for above "view" button can/will look like this:
Protected Sub cmdEdit_Click(sender As Object, e As EventArgs)
Dim btn As Button = sender
Dim gRow As GridViewRow = btn.NamingContainer
Dim HotelPK As Integer = GridView1.DataKeys(gRow.RowIndex).Item("ID")
' Now jump to our hotels edit page:
Session("HotelPK") = HotelPK
Response.Redirect("EditHotel")
End Sub
So, we can use session() to pass the HotelPK.
But, as noted, we ONLY use session for passing the value, NOT persisting.
So, now on the hotel edit page (hotel.aspx), then our all important load event will have this:
Dim intHotelPK As Integer = 0
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not IsPostBack Then
intHotelPK = Session("HotelPK")
ViewState("HotelPK") = intHotelPK
' Load up our form to edit hotel
Dim strSQL As String = "SELECT * FROM tblHotelsA WHERE ID = " & intHotelPK
Dim rstHotel As DataTable = Myrst(strSQL).Rows(0)
Call fLoader(Me.EditHotelDiv, rstHotel)
Else
intHotelPK = ViewState("HotelPK")
End If
End Sub
Now, for any button click, any code that runs in this page?
Any variable, including any post-back button can use intHotelPK.
so, my save button code would/could be this:
Protected Sub cmdSave_ServerClick(sender As Object, e As EventArgs)
Dim strSQL As String = "SELECT * FROM tblHotelsA WHERE ID = " & intHotelPK
Dim rstData As DataTable = Myrst(strSQL)
Call fWriterW(EditRecord, rstData.Rows(0))
Call MyRstSave(rstData, "tblHotelsA")
Response.Redirect("ChooseHotel")
End Sub
Note how any button click, and any code now has a persisted value of HotelPK, and any code, post-back code etc. is now free to use that persisted value.
And EVEN if the user will have two browser tabs open, or two copies of the browser?
The code will still work fine, since they can't really click on two open web pages at the same time. So, we can (safe) pass the pk id, shove into session, and now that page will operate correctly, have a persisted "hotel PK id", and it will not matter even if they have 2 or 5 pages open (more then one time). the code will work, since ViewState is per page.
However, use caution with ViewState. ViewState is client side storage, and each button click (post-back) means that ViewState travels and makes the round trip. (thus increasing the size of the post back). So, don't go shoving in some large dataset or datatable - since it will cost dearly in terms of response time.
So hopefully the above gives you some ideas here.
This also means in some cases, you will "often" re-pull data from the database, and in 99% of cases, a re-pull of some data for code behind is BETTER than attempting to persist a dataset/datatable in code behind.
And in fact, unless only a few rows of data, I don't recommend using ViewState for persisting datasets/datatables. Session() is much better if you must do this, but while session() is 100% server side, it also per user, and not per page - so as noted, use session () for passing values to the next page.
And if you need to pass 5 variables to the next page? Build a little class, since then you can "group" sets of values required for a given page operation, and they also facilitate passing of values to the next page with great ease.

VB.Net: call sub from shared sub

I have some Ajax on a web page that feeds some data to a server-side VB.Net method. Once that data is in the server-side method, I need to call another server-side method to use the data I just collected. Here is a really simplified example:
' This method gets the input from the Ajax code on the web page.
<System.Web.Services.WebMethod> _
Public Shared Sub GetAwesome(VBInputText As String)
Dim strTest As String = VBInputText
' Now that we have collected input from the user,
' we need to run a method that does a ton of other stuff.
DisplayAwesome(VBInputText)
End Sub
Protected Sub DisplayAwesome(AwesomeIn As String)
' The real app does a lot more than this. For this example, it
' just sets the text of a literal.
litAwesomeResult.Text = AwesomeIn
End Sub
Of course, in the above example DisplayAwesome(VBInputText) gives me the 'Cannot refer to an instance member...' error. So, is it possible now to call Protected Sub DisplayAwesome from Public Shared Sub GetAwesome? I'm hoping to stay close to this sort of solution because it would play very well with the app as it is already written by another coworker.
unfortunately you cannot do this, Since the page method DisplayAwesome is defined as Protected and you requires an instance of the class to access the Protected method. But changes in another instance will not reflect in the current UI. another thing you can do is Make DisplayAwesome as Shared, but this time you cannot access the UI elements inside the shared function.
The thing you can do in this situation is, return data to the called method(in front end) and handle the litAwesomeResult.Text there
Call sub with name of Form Class like this:
FormName.DisplayAwesome(VBInputText)
In VB.Net, you can call the method not shared from a shared method with Name of Form Class by default instance, because The default instance is an object Form type that the VB application framework create and manage it, when the form is added to the project.
For more info see this :
VB.NET Default Form Instances

vb global variable to be accesses by different classes

I am new to VB and am working on an asp.net VB web application.
I have a partial class called s_new.aspx.vb which is the vb page behind the page s_new.aspx.
In this page there are a series of select dropdowns, which when selected lead to different options becoming available eg different select boxes appearing and or with different options.
One particular select box accesses a method in a different class (webcontrols).
My problem is that I need the web controls class to change its behavior based on a certain selection made in the s_new.aspx.vb page.
If I was using C# I would create a global variable and use that as a decision maker, but I cant seem to to this in VB.
below is a snippet of the code I am using:
Partial Class s_new
Protected Sub fldSourceofFunds_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles fldSourceofFunds.SelectedIndexChanged
Select Case fldSourceofFunds.SelectedIndex
Case 1
####
Case 2
###
Case 3
do something and set the variable
End Select
End Sub
Partial Class Webcontrols
Private Sub ReloadChildren()
If (variable from s_new has certain value) Then
ResetChildren()
Select Case PaymentMethodValue.ToLower.Trim
Case "payment via provider/platform"
Case "payment direct from client"
Case "payment via provider/platform and payment direct from client"
End Select
End if
End Sub
End Class
This is an existing project and I need to minimize the changes I make to the project, eg I am not able to make large changes to the structure to accommodate this change. As I said above, this would be a simple solution in C# and I assume it is simple here to.

Why is my session variable being shared by multiple users in asp.net application

I have a session variable with a datatable assigned to it. For some reason the results from the datatable (display to user in a GridView) are being shared accross multiple users who are logged in. I thought each session was independent? So when one user makes changes then the other users see those results added to their results. Not sure why. I am not using Application variables.
I initialize the Session variable in Global_asax, then populate it on a button command after the user has filled out the required entries.
Imports System.Web.SessionState
Public Class Global_asax
Inherits System.Web.HttpApplication
Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs)
Session("RDDT") = New DataTable
End Sub
End Class
As it stands, I did overlook something. There was a usercontrol that someone else had created that had Public variables. The application was treating them as static. All I did was remove them and changed where in use to Session varibles. The Application works as intended now.

SqlDataSource reuse on different pages

I am using ASP.NET. I have a ReportPage1 and ReportOutputPage1. These are different aspx files and has different MasterPages. However, I need the same SqlDataSource object to use on both pages. On ReportPage I need SqlDataSource to call StoredProcedure and import data to CSV file, but on ReportOutputPage I need to use SqlDataSource to call the same StoredProcedure and populate GridView.
ReportPage1 is "main" page - a button click from this page opens ReportOutputPage1 and displays it in new window. ReportPage is PreviousPage for ReportOutputPage1.
Above is example for Report1. The same idea is for Report2 (with SqlDataSource2) and for Report3 (SqlDataSource3) etc. - 10 different reports.
How to reuse SqlDataSource for every two pages (ReportPage & ReportOutputPage)?
First suggestion I found in web - using masterpage for both ReportPage and ReportOutputPage. This doesn't work, as I have already have different masterpages for ReportPage and ReportOutputPage, as well as then I need to create 10 different MasterPages for each Report.
Second suggestion was to define SqlDataSource on ReportPage and then reuse it using PrevousePage on ReportOutputPage, but this doesn't work for my special case (I am using Ajax staff and Partial page postbacks and I am loosing PreviousPage, also SqlDataSource could not be serialized to save it in ViewState or similar).
Create UserControl. Probably this could work, but it is time consuming to create UserControl every time for new Report (10 reports - 10 usercontrols?).
Simply Copy & Paste SqlDataSource (I did it for one Report) could work, but I would like something more like code reuse. Someone could simply forget to modify SqlDataSource in two different places if necessary.
Can you, please, give me some suggestions how to reuse code (particularly, SqlDataSource) for Report & ReportOutput pages?
Could you define the need for using the same SqlDataSource? If they are two different pages and it sounds like two different uses why not use two different SqlDataSource? The pages are separate anyhow, your not going to be able to share an object on one with the other.
I would suggest you look at adding a data layer to your application for database interaction and binding your data to the datagrid at request time. That way you build your database interaction once and reuse that over different pages.
The other option is you simply use two SqlDataSources and copy/paste them to both the pages. If your trying to make a selection or some sort of criteria apply to your second page then consider using query strings and QueryStringParameters in your SqlDataSource.
Hope this helps.
Edit: Pop this in App_Code somewhere, pass in your specific usage requirements.
Public Shared Function GetData(connString As String, command As String, Optional params As SqlParameter() = Nothing) As DataTable
Dim conn As New SqlConnection(connString)
Dim da As New SqlDataAdapter(command, conn)
Dim dt As New DataTable
da.SelectCommand.CommandType = CommandType.StoredProcedure
da.SelectCommand.CommandTimeout = 6000 '6 seconds.
If Not IsNothing(params) Then
For Each p As SqlParameter In params
da.SelectCommand.Parameters.Add(p)
Next
End If
Try
conn.Open()
da.Fill(dt)
Return dt
Catch ex As Exception
Throw ex
Finally
conn.Close()
End Try
End Function
Then bind the datatable to your gridview, not sure how your outputing to file.
Private Sub BindData(gridview As GridView, data As DataTable)
gridview.DataSource = data
End Sub
You can now re-use the database interaction from the code behind:
Protected Sub Page_Load(sender As Object, e As System.EventArgs) Handles Me.Load
BindData(MyGridView,GetData(ConnectionStrings(connName).ConnectionString, _
"dbo.SomeSprocOrOther", _
New SqlParameter(){New SqlParameter("paramName","paramValue")})
End Sub

Resources