SqlDataSource and stored procedure call issue - asp.net

I've stumbled upon an issue and can't figure it out on my own. Hope someone could help me resolve it.
So, I have a simple stored procedure in a SQL Server 2005 database
CREATE PROCEDURE spTest
#pin varchar(128)
AS
BEGIN
SELECT #Pin as Param
END
and an asp.net page with a SqlDataSource and a GridView control in an application (VS2008)
<asp:SqlDataSource
ID="sds2"
runat="server"
ConnectionString="..."
SelectCommand="spTest"
SelectCommandType="StoredProcedure"
>
<SelectParameters>
<asp:QueryStringParameter Name="pin" QueryStringField="pin" DbType="String"/>
</SelectParameters>
</asp:SqlDataSource>
<asp:GridView ID="gv" runat="server" DataSourceID="sds2"></asp:GridView>
As you can see, the code is straightforward. Nevertheless, if I don't bother specify the pin on the url (.../Default.aspx instead of .../Default.aspx?pin=somevalue) or specify an empty line (.../Default.aspx?pin=) there is no any call to the stored procedure (I check it with SQL Server Profiler).
Moreover, if I replace the QueryStringParameter with a simple
<asp:Parameter Name="pin" DbType="String" />
and do not point out DefaultValue value, the situation repeats and no calls to the stored procedure are made. What is the reason of such a behaviour?
I'm quite a new to the asp.net and possibly overlook something, but I even tried to do the same in code-behind file programmatically instead of declaratively and the result is the same. The only thing I could find out is that in such case a Selecting event of the SqlDataSource is fired, but the Selected is not. Maybe some kind of an error happens?
Anyway, any kind of help would be greatly appreciated.

The SqlDataSource object has a property called CancelSelectOnNullParameter. Its default value is true, so I think the behavior you're seeing is expected, albeit not obvious. Try setting this property to false.
<asp:SqlDataSource
ID="sds2"
runat="server"
ConnectionString="..."
SelectCommand="spTest"
SelectCommandType="StoredProcedure"
CancelSelectOnNullParameter="false"
>
Additionally, you may find the ConvertEmptyStringToNull property of the Parameter class (QueryStringParameter extends this) to be of some use, depending on if/how your stored proc handles null values. Its default value is true as well.

Try this out.
Create a method which will return db null if parameter is not passed in the stored procedure
public static object GetDataValue(object o)
{
if (o == null || String.Empty.Equals(o))
return DBNull.Value;
else
return o;
}
Create a method which will called the stored procedure and fill the dataset.
public DataSet GetspTest(string pin)
{
try
{
DataSet oDS = new DataSet();
SqlParameter[] oParam = new SqlParameter[1];
oParam[0] = new SqlParameter("#Pin", GetDataValue(pin));
oDS = SqlHelper.ExecuteDataset(DataConnectionString, CommandType.StoredProcedure, "spTest", oParam);
return oDS;
}
catch (Exception e)
{
ErrorMessage = e.Message;
return null;
}
}
Now bind the dataset to gridview
private void GvTest()
{
DataSet oDsGvspTest = new DataSet();
string pin = Request.QueryString["Pin"];
oDsGvspTest = GetspTest(pin);
if (oDsGvspTest.Tables[0].Rows.Count > 0)
{
Gv.DataSource = oDsGvspTest;
Gv.DataBind();
}
}
Now called this method on page_load event
if(!IsPostBack)
{
GvTest();
}
If found it useful,please mark it as your answer else let me know...

Related

Data Source is not supported server side pagination

I am working on project which has hundred thousands of records coming from database. I have to show this in DevExpress grid.
Well default behavior of grid is to load all records at once and it applies pagination on client end.
The problem i am having is that the page takes lots of time while loading. To stop this i am going to use server side pagination at devExpress grid. But I am getting error : "The data source does not support server-side data paging"
My grid is "gvList" and i am setting its property as :
gvList.DataSourceForceStandardPaging = True
And then
Dim cmd As New SqlCommand
Dim ds As New DataSet
Dim da As SqlDataAdapter
Dim dbConn As New SqlConnection(conStr)
cmd.CommandType = CommandType.Text
cmd.CommandText = strSQL 'contains SQL string
cmd.Connection = dbConn 'contains connection object
da = New SqlDataAdapter(cmd)
da.Fill(ds, tbl)
gvList.DataSource = ds
gvList.DataBind()
Can any one please tell me where i am going wrong ?
Thanks..
Anjum Dhamial
ASPxGridView supports three different data binding modes:
1) common binding when all data is fetched to the web server and processed by the ASPxGridView itself;
2) server side sorting and paging. This functionality is turned on by activating the ASPxGridView's DataSourceForceStandardPaging property; In this case, you need to use ObjectDataSource since SQLDataSource does not support server side pagination.
3) real server mode when almost grid data related calculations (like grouping, summary) are implemented on the DB server. The link above contains some useful information regarding this mode.
So, the easiest solution to this problem is to use my second option. The third option is much more powerful but will require some additional work.
Use Custom Pagination aproach.
Hence, you should associate the grid to a datasource. It can be a objectdatasource or another.
In datasource, some parameters should be send to class which contains methods for selecting and counting.
An example:
Web form
<asp:ObjectDataSource ID="ds"
EnablePaging="True"
TypeName="Namespace.to.Service"
runat="server"
SelectMethod="FindAll"
SelectCountMethod="FindAllCount"
StartRowIndexParameterName="startRow">
<SelectParameters>
<asp:Parameter Name="maximumRows" Type="Int32" />
<asp:Parameter Name="startRow" Type="Int32" />
</SelectParameters>
</asp:ObjectDataSource>
<asp:GridView ID="grd" runat="server" AutoGenerateColumns="False"
DataSourceID="ds"
AllowPaging="True">
<%-- add columns here --%>
</asp:GridView>
If you need pass extra parameters in datasource from some control, you can add to SelectParameters
<asp:ControlParameter ControlID="txtID" Name="parameterName" PropertyName="Text" Type="String" />
In Namespace.to.Service class, put methods as below:
public IList<MyObject> FindAll(int maximumRows, int startRow) { ... }
public int FindAllCount(int maximumRows, int startRow) { ... }
If extra parameters in datasource was used, simply add them to methods too:
public IList<MyObject> FindAll(int maximumRows, int startRow, string parameterName)
{
/*
fetch result from database or something;
'maximumRows' is pageSize
'startRow' is first result to fetch 'maximumRows' records from database
*/
}
public int FindAllCount(int maximumRows, int startRow, string parameterName)
{
/*
take amount of data for. It will be used to create grid footer for pagination;
parameters are like above.
*/
}
I guess it's all you need.

C#/ASP.Net - Extract bit value of column in a gridview

I have a gridview that is SQL bound. In some of the columns there are bit values. When I use C# to get the values into the gridview, checkboxes are displayed. I need to extract the value of that column into text.
SqlConnection sConnection = new SqlConnection(MyConnectionString);
SqlCommand sCommand = new SqlCommand();
using (sConnection)
{
sCommand.Connection = sConnection;
sCommand.CommandText = "MyStoredProcedure";
sCommand.CommandType = CommandType.StoredProcedure;
sCommand.Connection.Open();
SqlDataReader reader = sCommand.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
gridView.DataSource = reader;
gridView.DataBind();
}
for (int i = 0; i < gridView.Rows.Count; i++)
{
ListBox1.Items.Add(gridView.Rows[i].Cells[3].Text);
}
}
}
The gridview column data type is 'bit'. I do not have access to the database or stored procedure to change anything there. I need to somehow extract the '0' or '1' value, but when I do it like above, the text is blank.
I also tried to use 'GetOrdinal'. It returned a True/False value from the database, but I could not figure out how to get the value for each item in the gridview.
if (!reader.IsDBNull(reader.GetOrdinal("MyColumn1")))
{
ListBox1.Items.Add(reader.GetOrdinal("MyColumn1").ToString());
}
General overview:
You need to be able to find the CheckBox that's generated and get the value of it's "Checked" property.
To do this, you need to be able to use the FindControl() method on the GridViewRow.
To use FindControl, the CheckBox needs a predictable name.
To get a predictable name, you need to have that column be a TemplateColumn so that you can specify the name of the CheckBox in the markup on the ASPX page.
There's a full working set of code here: http://www.codeproject.com/Articles/25056/The-RIGHT-Way-to-Use-Checkboxes-in-a-NET-Repeater
This shows the code for a Repeater, but it's the same principle and general code for any DataBound control.
The code below should work with modifications to match your DB names:
<asp:TemplateField>
<ItemTemplate >
<asp:checkbox id="MyColumnNameCheckbox" runat="server" />
</ItemTemplate>
</asp:TemplateField>
string defaultvalue = "0"; // To be used to display the value of the original bit field.
foreach (GridViewRow row in GridView1.Rows)
{
CheckBox chkBx = (CheckBox)row.FindControl("MyColumnNameCheckbox");
if (chkBx != null && chkBx.Checked)
{
defaultvalue = "1";
}
}
I was able to figure it out. Thanks, David Stratton, for pointing me in the right direction.
I did it by assigning an id to the dynamically created control first. then did the FindControl()...
Control ctrl = GridView1.SelectedRow.Cells[4].Control[0];
ctrl.ID = "ctrl";
Boolean result = Convert.ToBoolean(((Checkbox)GridView1.Rows[0].Cells[4].FindControl("ctrl")).Checked);
TextBox1.Text = result.ToString();
This returns a value of "True" or "False"...
Thanks again.
Another way to resolve it:
bool result = (GridView1.SelectedRow.Cells[4].Control[0] as Checkbox).Checked;
TextBox1.Text = result.ToString();
it resolve the problem with less code :)

Populate asp labels from SQL Query

I wrote the code to query the database, but now do not know how to get the text into my two labels 'txtTitle' & 'txtBody'
protected void GridView1_SelectedIndexChanged(object sender, EventArgs e)
{
SqlConnection thisConnection = new SqlConnection(ConfigurationManager.ConnectionStrings["blcDocumentationConnectionString"].ConnectionString);
// Create Command Object
SqlCommand nonqueryCommand = thisConnection.CreateCommand();
pnlNew.Visible = false;
pnlView.Visible = true;
try
{
// Open Connection
thisConnection.Open();
// Create SELECT statement with named parms
nonqueryCommand.CommandText = "SELECT DocumentTitle,DocumentBody FROM tblDocument WHERE DocumentID = #DocumentID";
// Add parms to Command parms collection
nonqueryCommand.Parameters.AddWithValue("#DocumentID", GridView1.SelectedValue);
// Execute query statement
nonqueryCommand.ExecuteNonQuery();
// Populate Labels
GridViewRow row = GridView1.SelectedRow;
lblTitle.Text = row.Cells[1].Text;
lblBody.Text = row.Cells[2].Text;
}
finally
{
// Close Connection
thisConnection.Close();
}
}
<asp:Panel ID="pnlView" runat="server" Visible="False" CssClass="pnlView">
<h1 style="background-color: #CCE6FF">
<asp:Label ID="lblTitle" runat="server" Text="Label"></asp:Label></h1>
<p>
<asp:Label ID="lblBody" runat="server" Text="Label"></asp:Label></p>
<p style="background-color: #EFEFEF">
<asp:Button ID="btnEdit" runat="server" Text="Edit This Document" OnClick="btnEdit_Click" /> or
cancel</p>
</asp:Panel>
Your code shows that you have a table filled with documents IDs (I'm assuming you already bound this correctly in some other part of your code)
You used the SelectedIndexChanged event correctly, but why are you executing another query inside? If executing the query is intentional, why are you binding your labels to old data? row.cells[].value holds old information, not the information you re-queried. If you want the information you re-queried for, get that info directly from the result set. Also, that result set will return nothing unless you change nonqueryCommand.ExecuteNonQuery(); to nonqueryCommand.ExecuteDataSet();
Reiterating what User:rkw mentioned above. ExecuteNonQuery will not get any data back from the database. You need to use either DataReader ( http://msdn.microsoft.com/en-us/library/haa3afyz.aspx ) or use the ExecuteDataset and put the information into a DataSet and then read from it. When you say ExecuteNonQuery you are basically telling the SQL server to execute commands but not expecting any data back from the SQL Server.

Error: SelectedValue which is invalid because it does not exist in the list of items

I have a Gridview which binds to an ObjectDataSource (objStudentDetails). In edit/insert mode of the Gridview one of the fields is a DropDownList that gets it's pick list options from a lookup table. I have this DropDownList binding to another ObjectDataSource control (objStateList) which represents the lookup table. It works fine as long as the value in the objStudentDetails ObjectDataSource matches one of the values in the objStateList ObjectDataSource, at least in the case of a non empty string value anyway.
The objStateList has these values (from the stored proc that loads it - ID#6 is an empty string ''):
StateId State
----------- -----
6
4 AL
1 GA
3 KY
2 TN
The objStudentDetails has these values (from the stored proc that loads it):
FirstName LastName State
----------- ---------- -----
tone smith TN
Or it could have this result set (State is an empty string - ''):
FirstName LastName State
----------- ---------- -----
jenny johnson
In the first objStudentDetails resultset the state DropDownList in the EditItemTemplate shows up fine. In the second resultset, however, I get this error:
'ddlEditState' has a SelectedValue which is invalid because it does not exist in the list of items.
Parameter name: value
I would think that since my lookup table has a value with an empty string, that the objStudentDetails value with an empty string for state would match, but something isn't working the way I am expecting it to.
Here is my EditItemTemplate code from the Gridview:
<EditItemTemplate>
<asp:Panel ID="panEditState" runat="server">
<asp:DropDownList ID="ddlEditState" runat="server" CssClass="GridviewDropdownlist"
DataSourceID="objStateList" DataTextField="State" DataValueField="State"
SelectedValue='<%# Bind("State") %>'
Width="50px">
</asp:DropDownList>
</asp:Panel>
</EditItemTemplate>
And the objStateList, which calls a method passing a parameter of which lookup table to query:
<asp:ObjectDataSource ID="objStateList" runat="server" SelectMethod="GetDropdownData" TypeName="AIMLibrary.BLL.DropdownData">
<SelectParameters>
<asp:Parameter Name="itemsToGet" DefaultValue="state" />
</SelectParameters>
</asp:ObjectDataSource>
Any ideas?
Start by setting both DropDownLists' AppendDataBoundItems property to true. Next, add the NULL ListItem by adding the following <asp:ListItem> element to each DropDownList so that the declarative markup looks like:
<asp:DropDownList ID="Categories" runat="server"
DataSourceID="CategoriesDataSource" DataTextField="CategoryName"
DataValueField="CategoryID" SelectedValue='<%# Bind("CategoryID") %>'
AppendDataBoundItems="True">
<asp:ListItem Value="">[nothing selected]</asp:ListItem>
</asp:DropDownList>
I suspect there are many different scenarios that can cause this error. In my case, I had a drop down placed in a template field. The drop down was bound to its own objectdatasource, and its selectedvalue property was bound to a field from the gridview's own (separate) datasource.
Now, with my specific scenario, the problem was a race condition. The gridview's datasource was being populated and bound BEFORE the dropdowns had their turn. This also meant that the dropdowns' selectedvalues were being set BEFORE the dropdowns' items had been created through their own bindings.
I'm sure there's got to be a better solution, but I didn't have much time for research. I disconnected the gridview and the dropdowns from their datasources (meaning, removing the assignments from the designer) and opted bind programmatically. That way, I can explicitly bind the dropdowns so that their items' values will be available when the gridview itself is bound.
So far, so good. Just a few extra lines of code in the Page_Load
AppendDataBoundItems="True"> works but not in all cases. Making dropdownlist inside GridView is still a mystery which Microsoft has to resolve. They say development is ASP is much quicker than PHP. Well this is my third day on this small problem and still have no solution.
OK, since this is a common problem I guess its worth to actually post an answer: After a lot of looking around I've found two solutions - well, one patch and one real one.
Patching:
Set the DDL setting AppendDataBoundItem=true anda add manually one element to the list (i.e. "Please Select" with null value):
< asp:DropDownList ID="DropDownList5 runat="server" AppendDataBoundItems="True" ... >
< asp:ListItem>Please Select< /asp:ListItem>
< /asp:DropDownList>
This seems to work in about 80% of cases. I had a weird situation when I had to upgrade existing (and working) query used by DDL to allow another value of parameter - Query was something similar to SELECT ID, Name from EMPLOYEES where Department =#Department and originally #Department could only be equal to "Planners" and "Workshop" - after adding "Logistics" DDL mysteriously stopped working ONLY for the new value of department.
Proper solution: Bind the DDL during the GridView_RowDataBound event (fount thanks to This article
My parameter is taken as a text from the label (set up somewhere else)
protected void GridView5_RowDataBound(object sender, GridViewRowEventArgs e)
{
//********** this is a workaround for the annoying problem with dropdownlist in gidview without adding new item ************
if (e.Row.RowType == DataControlRowType.DataRow && GridView5.EditIndex == e.Row.RowIndex)
{
DropDownList DropDownList5 = (DropDownList)e.Row.FindControl("DropDownList5");
string query = "SELECT gkey as empID, name FROM [employees] where department=#department";
SqlCommand command = new SqlCommand(query);
command.Parameters.AddWithValue("#department", lblDepartment.Text);
DropDownList5.DataSource = GetData(command);
DropDownList5.DataTextField = "name";
DropDownList5.DataValueField = "empID";
DropDownList5.DataBind();
}
And the GetData method:
private DataTable GetData (SqlCommand cmd)
{
string strConnString = ConfigurationManager.ConnectionStrings["MyConnectionString"].ConnectionString;
using (SqlConnection con = new SqlConnection(strConnString))
{
using (SqlDataAdapter sda = new SqlDataAdapter())
{
cmd.Connection = con;
sda.SelectCommand = cmd;
using (DataTable dt= new DataTable())
{
sda.Fill(dt);
return dt;
}
}
}
}

switch statement in linq

My code for sql connection using linq is:
var query1 = from u in dc.Usage_Computers
where u.DomainUser == s3
select u; // selects all feilds from table
GridView1.DataSource = query1;
GridView1.DataBind();
I have a field called "Operation" in the table "Domainuser" which has values like "1, 2, 3". When I populate these values to data grid I wanted to convert them to meaningful values like if the value of Operation is 1 then display in datagrid as "logon", if 2 then "logoff" etc...
How do i assign values for them after retrieving from database?
This technique does not seem particularly applicable to your problem, but here it is anyway.
You can create a SQL case statement in LinqToSql by using the C# ? : operator.
var query1 =
from u in dc.Usage_Computers
where u.DomainUser == s3
select new {usage = u,
operation =
u.DomainUser.Operation == 1 ? "login" :
u.DomainUser.Operation == 2 ? "logoff" :
"something else"
};
Use a template field in your gridview:
<asp:GridView ID="gvDomain" runat="server" OnRowDataBound="gvDomain_RowDataBound">
<Columns>
<asp:TemplateField>
<HeaderTemplate>
Operation
</HeaderTemplate>
<ItemTemplate>
<asp:Label id="lblLogon" runat="server" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
Then use the gridviews RowDataBound event to discover the label and assign its text:
Protected Sub gvDomain_RowDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs) Handles gvStates.RowDataBound
Dim lblLogon As Label = DirectCast(e.Row.FindControl("lblLogon"), Label)
Dim drv As DataRowView = DirectCast(e.Row.DataItem, DataRowView)
If lblLogon IsNot Nothing Then
Select Case drv("Operation").ToString()
Case "1"
lblLogon.Text = "Logon"
Break
Case "2"
lblLogon.Text = "Logoff"
Break
//etc...
End Select
End If
End Sub
static Func<int?, string> MapSqlIntToArbitraryLabel = (i =>
{
// for performance, abstract this reference
// dictionary out to a static property
Dictionary<int, string> labels = new Dictionary<int, string>();
labels.Add(1, "logon");
labels.Add(2, "logoff");
labels.Add(...);
if (i == null) throw new ArgumentNullException();
if (i < 1 || i > labels.Count) throw new ArgumentOutOfRangeException();
return labels.Where(x => x.Key == i.Value)
.Select(x.Value)
.Single();
}
that return statement can also be expressed as:
return (from kvp in labels
where kvp.Key == i.Value
select kvp.Value).Single();
Then you can use call that function from your linq query like so:
var query1 = from u in dc.Usage_Computers
where u.DomainUser == s3
select {
Operation = MapSqlIntToArbitraryLabel(u.Operation)
// add other properties to this anonymous type as needed
};
I've tried every suggested method of fooling Linq2Sql into running my code and this method is the only one that i've found that allows me to run code as part of a deferred-execution projection.
I've done something similar using TemplateFields. Using an ASP:Label bound to the property and adding an OnPreRender event handler for the control. In the event handler for the control I translate the text based on it's current value and set the new value:
protected void label_OnPreRender( object sender, EventArgs e )
{
Label l = (Label)sender;
switch (l.Text) {
case "1":
l.Text = "Logon";
break;
...
default:
break;
}
}
If the form is in edit mode, you'll need to handle it differently. You'll also probably need to add handlers for Inserting and Updating to the View control you are using to translate the data supplied by the page into its database representation.

Resources