PagedDataSource not working with previous/next pages - asp.net

I'm having a hard time with this scenario and hoping someone can help me clarify where I'm missing a step/logic. I tried searching online but I couldn't find an example that addressed this issue.
We are setting up a search page that when first loads shows a bunch of options (e.g., textboxes, checkboxes, etc). The user will fill out the form and submit the form back to itself. Once posted back, the page will build and run a SQL query (e.g., SELECT ID FROM Customers WHERE Company = 'Acme' AND AmtDue = 3) against the database with the user's options and then the results will show up. This part works ok.
The part that breaks down is when I am trying to add pagination. The result set is a DataTable bound to a Repeater. I am using a PagedDataSource to add pagination. Pagination works great for the first page but for subsequent pages, it does not work. Basically, instead of returning the next page of the result requested (e.g., SELECT ID FROM Customers WHERE Company = 'Acme' AND AmtDue = 3), the results returned is the SQL query before appending the user's search options (e.g., SELECT ID FROM Customers).
I think my basic problem is that I'm not sure how to distinguish a normal Page.IsPostBack and paginating through results. The reason why this is causing problems is because I don't want to be regathering form data, rebuilding query, and requerying the DB.
The examples that I found online involved pagination of a DataTable that is rebuilt every time the page loads (e.g., Not Page.IsPostBack).
Here is a rough outline of our code:
Public dtCustomers As DataTable = New DataTable()
Sub Page_Load(ByVal Sender As Object, ByVal E As EventArgs) Handles Me.Load
' When the page first loads, show the search form with search options.
If Not Page.IsPostBack Then
ShowSearchForm()
' Once the form is submitted, show the search results.
Else
' ----------------------------------------
' This is the part that I'm having trouble with. How do I skip the following two steps (GatherFormData() and BuildDT()) when the user requests a subsequent page?
GatherFormData()
dtCustomers = BuildDT()
' ----------------------------------------
BindData()
End If
End Sub
Sub BindData()
Dim objPDS As PagedDataSource = New PagedDataSource()
objPDS.DataSource = dtCustomers.DefaultView
objPDS.AllowPaging = True
objPDS.PageSize = intNumPerPage
objPDS.CurrentPageIndex = Me.ViewState("_CurrentPage")
rptSearchResults.DataSource = objPDS
End Sub
' Subroutine called when the previous page button is pressed.
Sub GoToPrevPage()
' Set viewstate variable to the previous page.
Me.ViewState("_CurrentPage") -= 1
BindData()
End Sub
' Subroutine called when the next page button is pressed.
Sub GoToNextPage()
' Set viewstate variable to the next page.
Me.ViewState("_CurrentPage") += 1
BindData()
End Sub
Note: I understand that the DataTable will have to be put into cache or a session variable, but haven't decided the best method.
Please excuse the code outline but the actual code is massive so the simplification is to make it easier to get to the heart of the issue.
Let me know if any of that was unclear. Thanks in advance!

I am assuming that you are going to store your data in session/cache (I would prefer the later but there can be use case for storing it in session) - you can store the key in the view-state and presence of key could be use to determine if post-back is for pagination or not. For example,
if (ViewState["dataKey"] == null)
{
// first form submittal, do the search
GatherFormData();
dtCustomers = BuildDT();
// store it in cache
ViewState["dataKey"] = Guid.NewGuid().ToString(); // create a key
Cache.[ViewState["dataKey"].ToString()] = dtCustomers; // TODO use Add method to control expiration etc
}
Its important that you clear the view-state when the search is reset (or similar condition)
Lastly, it is also possible to do the paging from database/data-store (for example, using ranking functions in SQL Server) so that you need not have to store the search results into the session/cache but rather do a database trip at each post-back. Such approach is useful when the complete result set size can be huge.

Related

populate text boxes based on drop down box selection

Not done any programming for about 5 years so im a little rusty on this one.
I am building an Asset management system to make my job easier but am struggling on a few things.
What i have so far is the DB setup and populated with a small amount of data, The main site itself is built and the basic select statements to a gridview of any data currently in the system for that particular page/search and i also have an insert statement that is populating the relevant tables based on data filed in on a form on that page. All these functions are working seamlessly.
The problem i have is that one field that needs to be populated for the insert statement needs to populate based upon the selection that is being made from a drop down box in the same form.
i.e.
An Asset that is being registered into the system has an Asset Type (the type details are contained within a parent table Asset_Type) the form has 3 fields for display purposes (one of these fields will need to return an entry to the Asset table in the database from the form, this is working) that are taken from the Asset_Type table, the other fields only relate to the Asset table itself.
What i am trying to achieve is that a user goes to the page and sees a list of Assets registered (this is working) they then need to add a new asset by filling in a form lower down on the page (for is there and writes to DB) on this form is a drop down menu that queries the Asset_Type table and allows the user to select the Type by name (this works)
What i now need to get working is for the 2 other fields to populate based on what Asset type is selected.
These fields are currently textboxes in the form but can be changed if required.
The code i have behind the page is below:
Protected Sub Name_Model_SelectedIndexChanged(sender As Object, e As EventArgs) Handles Name_Model.SelectedIndexChanged
Using conn As New SqlConnection(ConfigurationManager.ConnectionStrings("AssetManagementConnectionString").ToString())
Dim cmd As New SqlCommand()
cmd.Connection = conn
cmd.CommandText = "SELECT [ID], [Name_Model], [Description_Spec] FROM [Asset_Type] WHERE ([ID] = #ID)"
cmd.CommandType = CommandType.Text
conn.Open()
Dim reader As SqlDataReader = cmd.ExecuteReader()
If reader.HasRows Then
While (reader.Read())
{
Type_ID.text = (reader["ID"].tostring())
Name_Model.text = (reader["Name_Model"].tostring())
Description_Spec.text = reader["Description_Spec"].tostring
}
End While
End If
End Using
End Sub
So I'm not sure about Steve's comment, but I know he is a lot more familiar with VB based on questions of my own he has answered. However I am going to suggest something.
So "Drop downs" typically have 2 pieces of data associated with them.
A DisplayMember and a ValueMember (also a SelectedValue if it's a ComboBoxColumn)
You can reference either, I believe.
Then, you can do what you please with that value using an If Then or however you plan on using it.
assetValue = YourComboBox.DisplayMember.ToString
YourTextBox.Text = assetValue
or
If ComboBox.ValueMember = YourIndex Then
assetValue = Whatever
ElseIf
....
End If
Give that a try and let me know what you get. I am just throwing ideas out there.

How to avoid using Session

i have an interface that contains data-grid. When i add element to data-grid, i add it also into List property that is the data-source of my Data-grid. Here the declaration of my list in code-behind:
Public Property listeSpecialite() As List(Of RECSPECIALITECONCOURS)
Get
Return Session("specialite")
End Get
Set(ByVal value As List(Of RECSPECIALITECONCOURS))
Session("specialite") = value
End Set
End Property
and here is the code when i add element to Data Grid :
Protected Sub gridsecialite_ItemCommand(source As Object, e As System.Web.UI.WebControls.DataGridCommandEventArgs) Handles gridsecialite.ItemCommand
If e.CommandName = "Insert" Then
Dim dropSpecialite As DropDownList = CType(e.Item.FindControl("txtSpecialite_Footer"), DropDownList)
Dim specialite As New RECSPECIALITECONCOURS
specialite.CODESPECIALITE = IGS.ChercherParIdInt(Of GENSPECIALITE)(CInt(dropSpecialite.SelectedValue))
listeSpecialite.Add(specialite)
gridsecialite.DataSource = listeSpecialite
gridsecialite.DataBind()
End If
End Sub
and in user click save, i save all element in my list to database.
how can i save element of the list without the use of session. (my boss say that it's not good to store list of element in session-performance reason)
You can use ViewState to store listeSpecialite. Just make sure to understand how it works to ensure that match what you need.
Viewstate has its own drawbacks. More process time for serialization/deserialization, encoding/deocding in B64 and for the page lifecycle in load/restore viewstate of the page, etc. Also, by default, viewstate is sent to the client in a hidden field. This increase bandwidth.
Anyway, if your list has not to be persisted out of your current view I would use Viewstate.
EDIT:
With list of 1000 items viewstate will be hurge. How about read from data base persisted items and keep in viewstate just the added items?. You could retrieve items of database, retrieve items from viewstate, combine and bind to datagrid. It is another strategy to avoid session and hurge viewstate at the cost of read database every postback.
As I said, there is no silver bullet... :-P

PageIndexChanging Without Binding

I have datagridview linked with dataSource, this data source get data from table has 9 Million record and I'm searching in this table by name, after Binding data when need to change PageIndex this requires to research in 9 million records by this code:
Any method to change PageIndex without binding it again?
Protected Sub GVCenteralSearch_PageIndexChanging(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewPageEventArgs) Handles GVCenteralSearch.PageIndexChanging
GVCenteralSearch.PageIndex = e.NewPageIndex
GVCenteralSearch.DataSource = CentralSearch_DS
GVCenteralSearch.DataBind()
End Sub
Notes : This Is Based on WebApp (ASP.net)
Not that I know of. You do have a few options though, you can cache the data so you don't have to get it from the database again. You could also look into javascript alternative to paging the data (jQuery DataTable plugin). Alternatively you can re-write the query to only grab the records you need. This would still take the round trip to get the data displayed, but it won't be as significant.

Reading a DataSet in JavaScript

' USED TO REFRESH THE PAGE WHIN IT IS POSTED BACK
If (IsPostBack = False) Then
' USED TO DISPLAY DEFAULT FIRST ITEM IN THE DROPDOWN
Dim Li1 As New ListItem()
Li1.Text = "ALL"
Li1.Value = ""
cboStudy.Items.Add(Li1)
' USED TO COUNT THE STUDIES IN THE DROPDOWN
If (objDS.Tables(0).Rows.Count <> 0) Then
' USED TO CIRCULATE LOOP UPTO THE RECORD COUNT
Dim i As Integer
For i = 0 To objDS.Tables(0).Rows.Count - 1
' USED TO CREATE NEW ITEM IN THE DROPDOWN
Dim Li As New ListItem
Li.Text = objDS.Tables(0).Rows(i)("Study_Desc").ToString()
Li.Value = objDS.Tables(0).Rows(i)("Study_ID").ToString()
'USED TO ADD ITEMS IN THE DROPDOWN
cboStudy.Items.Add(Li)
Next
End If
'USED TO SAVE THE CHANGES IN DATASET
objDS.AcceptChanges()
' USED TO CLOSE THE DATABASE CONNECTION
objDS.Dispose()
End If
End If
I have to read dataset in javascript. So that I have to bind Study_Desc in DropDownList.
How can I do that?
I believe you might find it useful to review how an ASP.NET page works and how it renders. In your particular case you are setting the contents of a dropdownlist to your dataset. This will then render a 'select' object to the user with the appropriate entries without the need for Javascript. This all occurs on the server-side, which is processed on the server before a HTML response to given back to the user.
With Javascript, this code runs on the client-side, i.e. the user's computer. Here it is possible to retreive your dataset (by this, the dataset will get serialised to be passed over the wire and read into a format that Javascript can read) and have it interact on the client-side. The question is, in your case, is why bother as you're rendering the dropdown on the server-side. If you are interested in pushing your dataset to Javascript, check out the links on this post for a selection of approaches you can take.
Minor notes:
In your code you're using the 'AcceptChanges' method when there is absolutely no reason to use this unless you're making a change(s) to the dataset which I'm guessing you're not in the PageLoad...

when using datasource and databind to set items in a dropdownlist - index is always the value of first item

I am trying to use datasource and bind in my application to set a dropdown list to the results of a query. The dropdownlist populates correctly (shows each different type_name in the set) but later when I use ddltype.selectedvalue - I am always getting the value from the first item in the dataset. Here is the code.
if ds.hasRows = true
ddlType.DataValueField = "type_id"
ddlType.DataTextField = "type_name"
ddlType.DataSource = ds
ddlType.DataBind()
end if
Then later on I use the following code to try to get teh selected value
Dim typeID = ddlType.SelectedValue
I have ran the query to get the dataset on my SQL server and get the below results - but every time I use the above dim statement, the value is set to 812 even when type 2 is selected. Below is the results from the query that fills the sqldatareader named ds.
type_id : type_name
___________________
812 : type one
813 : type two
Thanks in advance.
Based on the information in your question, I don't see anything wrong.
But based on common mistakes people make with this pattern, I am guessing you are probably running ddlType.DataBind() on every postback. So the user clicks a button or something similar to fire the postback, and the DDL is re-bound before your click handler checks the value. Re-binding sets the selectedvalue back to default.
Edit: Your databinding code in Page_Load should only run once. One way to do this is to wrap the databinding code...
If Not IsPostBack Then
If ds.hasRows = True Then
ddlType.DataValueField = "type_id"
ddlType.DataTextField = "type_name"
ddlType.DataSource = ds
ddlType.DataBind()
End If
End If
This will cause the databinding to occur only the first time the page is requested, but not after a button click or other postback action.

Resources