ASP.NET - Trying to implement SortParameterName property to SqlDataSource - asp.net

I have several Gridviews and Repeaters bound to SqlDataSources using stored procedures. I am trying to implement sorting functionality into some of these but am having a hard time finding concrete instructions and/or examples of what is required on the SqlDataSource side to generate the ORDER BY's needed. Particularly, I do not understand the point of having a SortParameterName property in the SqlDataSource if all it does is manually connect to an ORDER BY clause in the stored procedure. Why define it as such if it is just another parameter in the SelectParameters list like any other, but just so happens to be connected to the ORDER BY clause? When I run the code example below, I am told there are too many arguments specified (obviously, the extra SortParams argument). Do I really need to alter my stored procedures and add "ORDER BY #SortParams" clauses to the end of the existing queries to make this work? I feel like I am missing something.
SqlDataSourceInLine.SelectParameters.Clear()
SqlDataSourceInLine.SelectCommandType = SqlDataSourceCommandType.StoredProcedure
SqlDataSourceInLine.SelectCommand = "ApproverGetApproved"
SqlDataSourceInLine.SelectParameters.Add("CompanyID", ConfigurationManager.AppSettings("Temp_CompanyID"))
SqlDataSourceInLine.SelectParameters.Add("SortParams", "EmpName DESC")
SqlDataSourceInLine.DataSourceMode = SqlDataSourceMode.DataSet
SqlDataSourceInLine.SortParameterName = "SortParams"
Dim dv As DataView = SqlDataSourceInLine.Select(DataSourceSelectArguments.Empty
Any clarification would be appreciated!

I was just trying to figure out how to use the SortParameterName and found this question. After doing some more search I think I have now found the correct answer.
Microsofts page Sorting Data with Data Source Controls says as follows (my emphasis):
The parameter identified by the SortParameterName property is passed to the ObjectDataSource control's SelectMethod or passed as part of the parameter collection to the SqlDataSource control's SelectCommand. The ObjectDataSource control can use the information passed to it in the sort parameter to return the data in sorted order. For the SqlDataSource control, you must supply the name of a stored procedure that can take the sort parameter and return the sorted data, because you cannot pass a parameter as part of an ORDER BY clause.
This indicates that the answer given by Icarus is not correct.
My conclusion is that when the SortParameterName property is set (in combination with an appropriate stored procedure) the Gridview will not do the sorting itself, but will let the datasource do a so called Custom Sorting, which for example would be the necessary way to sort if Custom Paging is used.
Update:
I have now used it in my own programming and confirmed that my conclusion was correct.

I've never used the SortParamter in the past, but what I gather from the documentation is that the purpose of this parameter is to allow you to get the results sorted in the way you want them in case the stored procedure does not do it already. Other than that, you don't need to use it for anything. A GridView whose datasource is of type SqlDataSource already implements sorting out of the box. You simply need to set the AllowSorting property to True and the SortExpression on every column.
Example:
<asp:GridView ID=" productsGridView" Runat="server"
DataSourceID="SqlproductDataSource" AutoGenerateColumns="False"
AllowSorting="True" >
<Columns>
<asp:BoundField HeaderText="Product"
DataField="ProductName" SortExpression="ProductName">
</asp:BoundField>
...

Nothing is required on the SqlDataSource. You need to implement the Sorting event for the Gridview and something else for the repeaters since they don't have sorting built in. What you could do (if you have small datasets coming back) is to use ViewState and store your DataTable of results and then utilize the DataView and the sorting capability of that, then bind the Repeaters/GridViews to the sorted DataView. You still have to keep track of the SortDirection and SortParameter within ViewState, regardless.

Related

How can I increase the performance of this FormView / SqlDataSource

I have a simple asp FormView on my page, it's working fine and everything, but I noticed the page is somewhat slow to load up (2-3 seconds), when there's really no reason for it, it's such a simple page.
The problem is that the FormView is using a SqlDataSource, which is using a 'heavy' select statement, basically grabbing all the fields and all rows of a profile table (name, address, phone, etc.). I'm assuming the FormView needs to get all the records so the Paging works correctly, but is there a way to code it or an attribute I can use so it doesn't grab every row and column but just all the columns of the current NameId?
asp:
<asp:FormView ... DataSourceId="sdsNames" AllowPaging="True" DataKeyNames="NameId">
<EditItemTemplate>....</EditItemTemplate>
<InsertItemTemplate>...</InsertItemTemplate>
<ItemTemplate>
<asp:Label Text='<%# Eval("Name") %>'.../>
...
</ItemTemplate>
</asp:FormView>
<SqlDataSource ID="sdsNames" ... SelectCommand="SELECT * from tblNames">
I tried changing the SelectCommand to
SELECT * from tblNames where NameId=1
since the initial page should show the first entry. But then I lose the pagination, basically no way to view the next record.
What the Page looks like:
There is not a lot you can do to improve performance here. You're correct, the SqlDataSource does slow things down because it retrieves all of the rows from your table.
There are a couple of different approaches that come to my mind:
Remove fields from your query.
This is the simplest solution, though it might not apply to your situation. If there are fields you don't need to display / modify, remove them. So, rather than SELECT * FROM..., you have SELECT LName, FName, Mname FROM...
You're still getting every row, but at least there is less data in each row.
You could switch to the ObjectDataSource control.
I haven't used this control much, but MSDN says that it performs a little better, because it doesn't always have to retrieve every row:
Some data sources, such as the ObjectDataSource control, offer more
advanced paging capabilities. In these cases the FormView control
takes advantage of the data source's more advanced capabilities to
gain better performance and flexibility while paging. The number of
rows requested may vary depending on whether the data source supports
retrieving the total row count.
You could move away from the datasource controls.
This probably involves more complexity than what you're looking for, but it is the most flexible approach. You can retrieve just the columns and rows you want. You can create a PagerTemplate, and set the values when the FormView is databound. You would have to handle the PageIndexChanging and PageIndexChanged events to change the paging values yourself.

Trouble with user selection of records in ASP.NET GridView

Goal
What I need is to display a SQL DataTable to the user on a webpage through ASP.NET and allow them to select a number of rows very quickly and easily, and then hit any of a number of different buttons to signify different operations. The selected rows are to be sent off to be processed (generally sent to a webservice or similar through codebehind). This is a somewhat generic solution, as I will be using this same setup in many places - thus the data, and the available operations will be different.
The goal is to be able to know basically nothing about the data - that is for the user to understand. If this concept is already impossible (though I believe it is not), please let me know.
Pulling from T-SQL (SQL Server 2008 R2 or later).
Using ASP.NET to make aspx webpage. Codebehind is done in VB.NET, in both cases we're using 4.0
I'm fluent in C# and VC++, so if you can't easily translate your code, don't worry about that.
The problem
My problem has been creating a row selection process for the user which persists while sorting or paging the table, is quick for the user and relatively quick in response time, and which is reflected in the GridView's bound data (as this allows me to use a filter on a DataView to produce a DataTable for passing). If there's another way to know the selected rows then I'm all game so long as it is persistant and quick for the user.
I'm not debugging and I'm not having syntax issues (I do not believe) - I don't know how to proceed.
I don't necessarily need code as a solution - I need to understand if my metaphor is either bad practice, or simply uncommon and thus not well supported. In either case, what is an appropriate way to proceed? If nothing else, where do I look for a solution besides endless API and Tutorials?
My original plan
I wanted to use asp:GridView for binding the DataTable pulled from TSQL since I can auto-generate columns. This allows me to display data without needing to know what it is. I planned to add a specific boolean Column (left most) for storing the User's row selection. Then I can simply run a filter on the DataTable to produce a DataView, get the resulting DataTable from that DataView, and pass it down the chain.
All the columns except our specific selection one would be Read-Only. The user isn't editing data - their selecting records and view their selection side-by-side with the viewable relevant data.
Of note, I planned to check for name collision so our added Column doesn't collide with any existing Column in the DataTable - I'd rename it as needed using 'Select' followed by an integer produced by looping through, comparing my name to other columns, incrementing as needed.
I presumed that once I set this up, it would just handle itself: the user could click the resulting checkbox column cell's and it would change the data on the fly. The point of saving the selection was their selection persisted through sorting and paging for convenience (I save the GridView's DataSource and re-bind when necessary). It didn't really matter if the user refreshed the page and completely and lost their checkboxes - they would need to review any changes/new data, and they wouldn't need more then a brief moment to check off whatever they wanted.
This did not happen.
I found that AutoGenerateColumns does not produce checkboxes for boolean DataColumns. It produces text, so I end up with the word 'True' or 'False'.
I began looking for a way to get check boxes in the cells for a boolean DataColumn. I found that for the added boolean column, I could bind an asp:CheckBoxField to it using the attribute DataField. Of course, I have to figure out how to point it at a variable DataField...
<Columns>
<asp:CheckBoxField DataField="Select" HeaderText="Select"
ReadOnly="False" SortExpression="Select">
<ItemStyle HorizontalAlign="Center" />
</asp:CheckBoxField>
</Columns>
However, because a GridView is designed for Row-By-Row editing, all the resulting CheckBoxes are disabled and cannot be checked. This is because their containing row is not in Edit Mdoe. I do not want to enable Row-By-Row editing using a button or similar metaphor as the user often needs to check off several rows. More clicks is bad, and annoying. They should be focused soley on their data and making their selection, not worrying about remembering to enter and end edit mode. Also, the metaphor seems poorly placed here since the checkboxes are the -ONLY- editable data - and are not part of the represented data.
I could, of course, make a new class which inherits from GridView, overload the method which generates columns to produce checkboxes instead of text fields - but I felt there had to be a more straightforward path. Maybe this is the way to go? But maybe it would have the same editing issue as above - I'm not sure.
So, next I tried looking at using an asp:TemplateField as a column in my GridView. This TemplateField contains an asp:CheckBox who's Checked state is based on the underlying bound data value. The issue here is trying to make changing the check-state update that bound value. I would need some way of looking up a GridView value I could then use to find the same row in the DataTable. I've seen great examples using a Primary Key. While I could assume everyone keeps a Primary Key for any possible table, this might not be the case! I could further add a Primary Key myself, but now it looks like I would be adding and removing two columns before I pass a DataTable off to a WebService instead of just one. Again, I also would need to be able to assign a Column name which is dynamic to avoid collision.
<asp:TemplateField HeaderText="Select">
<ItemTemplate>
<asp:CheckBox runat="server" ID="CheckBox"
Checked="<%# DataBinder.Eval(Container.DataItem, "Select") %>"
AutoPostBack="true" OnCheckedChanged="SelectCheckBox_CheckChanged" />
</ItemTemplate>
</asp:TemplateField>
I stopped myself - This path is also fairly indirect. Surely there is a much cleaner, simpler way to do this. Is it uncommon for someone to display Read-Only data where records are selected to be submitted, in one fashion or another, for processing/updating/alteration?
Is my choice of GridView poor? I haven't yet found another good way to represent Table data.

Is it possible to have a SQLDataSource with a parameter that is based only upon the GridView that is binding to it?

I have a scenario where I want to put four identical Gridviews on the same page. (They will be on different tabs in an Ajax TabControl.) They show the same source data, but there are four corresponding groups of source data in a common underlying table. So I want to show Group 1 on Tab 1, Group 2 on Tab 2, etc. These Gridviews contain complicated controls, so I would prefer to use the same data source for all of them to avoid unnecessary repetition. The Insert and Update commands are completely identical.
So in theory I could build the Select command in such a way that I could filter the data based on the GridView that is binding to the SQLDataSource. The problem is that if I use the same SQLDataSource for all the Gridviews, I cannot find a way to have each GridView tell the SQLDataSource which one is calling it. I am thinking maybe this is not possible, because the SQLDataSource binds first before it knows what is binding to it, but I'm not sure. Can anyone think of a way to do this?
You can change the parameter value dynamically using OnSelecting event of SQLDataSource. This can be done in server side code.
Create a property which holds your current gridview unique key, which is causing SQLDataSource to fetch data from SQL database.
Assign this property unique gridview key on DataBinding event of gridview.
Based on this property change the parameter in OnSelecting event of SQLDataSource.
Let me know if I am missing something.

Iteratively Reference Controls by Name Pattern (VB.NET)

Is there a way to iteratively reference an ASP.NET control based on it's name pattern? Perhaps by a "pointer" or something?
What I have are a large set of comboboxes (each with an associated DropDownList and TextBox control). The DropDownList always has an item selected by default, but the use may wish to submit a NULL value. I come up with the following code to handle three cases: NULL, Existing Item, New Item.
'TextBoxControl & DropDownListControl should iteratively reference their
' respective TextBox & DropDownList controls by the actual control name
If StrComp(TextBoxControl.Text, DropDownListControl.SelectedItem.ToString) = 0 Then
'When an Existing Item is selected, Do Something
ElseIf Not TextBoxControl.Text = "" Then
'When a New Item is entered, Validate & Do Something
Else
'When NULL, Do Something
End If
The problem is, with so many comboboxes, I could easily end up with hundreds of lines of code. I wish to handle this in a more dynamic way, but I do not know if it is possible to reference controls in this way. Say I wanted to reference all the TextBox Controls and DropDownList Controls, respectively.
I can do string formatting with a given naming pattern to generate a name ID for any of the controls because they are all named with the same pattern. For example by attaching a specific suffix, say "_tb" for TextBox Controls and "_ddl" for DropDownList Controls:
Left(item.SelectedItem.ToString, item.SelectedItem.ToString.Length - 3) + "_tb"
Can this sort of thing be done in VB? Ultimately, my goal is to take the value entered/selected by the user, if any, and send it to a stored procedure on SQL Server for insertion into the database.
Yes. Write your code inside of a function having parameters for the textbox and the dropdown, and then write the function in terms of those two parameters. Then you simply call that function for every set instead of copy/pasting code everywhere.
Don't attempt choosing things by name. That's fragile and imposes machine requirements on a field that'd designed for human consumption.
I am not sure if the samething that applies VBA will apply to your situation, but I had the same issue and was able to use:
Me.MultiPage1.Pages(1).Controls("ref" & i + 1).Value
where controls encompasses all controls on the userform ("Me"). So if the controls in your program conform to a consecutive naming structure it is easy handle if the above applies vb.net

DataKeys dataList

This is my bare DataList definition :-
<asp:DataList runat="server" ID="dtltrial"
ondeletecommand="dtltrial_DeleteCommand" DataKeyField="PhotoId" >
</asp:DataList>
where PhotoId is my primary key of table and so i have given it as datakeyField. However i also want AlbumId along with DataKeyField. How do i specify it in DataKeyField and later on retrieve it in ondeletecommand event of DataList?
Thanks in advance :)
You can specify:
DataKeyNames="PhotoId,AlbumId"
Update:
It looks like the DataList doesn't allow you to specify multiple keys, and then retreive them as normal. From this question, the accepted answer deals with a GridView. Unfortunately the DataList doesn't function the same.
You'll likely have to create a unique identifier yourself at the datasource, or create a hybrid on a databinding event. For example, "PhotoId_AlbumId", which would turn into "837_7826". On the delete event, you'd then have to extract out the two IDs, separated by the underscore.
Consider creating a new single primary key on the datasource instead.

Resources