DbDataReader returns null - c#

I created a stored procedure to get 1 row out of a table.
spGetDepartment
#ID int
AS
BEGIN
SELECT [ID],[Name],[Description]
FROM tblDepartments
WHERE [ID] = #ID
END
When stored procedure is tested in SQL I get the desired result.
When I try to call the stored procedure from code (C#) and debug the page I can see the reader gets populated correctly but when trying to read it, it changes to null value.
Here is the code calling the stored procedure.
public static Department GetDepartment(string ID)
{
Department Dept = new Department();
DbCommand comm = DataAccess.CreateCommand();
try
{
comm.CommandText = "spGetDepartment";
// create a new parameter
DbParameter _ID = comm.CreateParameter();
_ID.ParameterName = "#ID";
_ID.Value = ID;
_ID.DbType = DbType.Int32;
comm.Parameters.Add(_ID);
comm.Connection.Open();
DbDataReader rdr = comm.ExecuteReader();
while (rdr.Read())
{
Dept.DepartmentID = Convert.ToInt32(rdr["ID"].ToString());
Dept.DepartmentName = rdr["Name"].ToString();
Dept.Description = rdr["Description"].ToString();
}
rdr.Close();
}
catch (Exception ex)
{
Utilities.LogError(ex);
}
finally
{
comm.Dispose();
comm.Connection.Close();
}
return Dept;
}

Try this:
SqlDataReader rdr = comm.ExecuteReader();
if (rdr.HasRows)
{
reader.Read();
Dept.DepartmentID = Convert.ToInt32(rdr["ID"].ToString());
Dept.DepartmentName = rdr["Name"].ToString();
Dept.Description = rdr["Description"].ToString();
reader.Close();
}
else
{ //No data
}

Related

C# call stored MYSQL procedure with input parameter

I'm calling stored procedure finish_record with 1 input parameter.
I read all related questions here for this topic but I didn't see mistake on my side...
But somewhere is it :-)
here procedure:
CREATE DEFINER=`root`#`localhost` PROCEDURE `finish_record`(
IN recordId INT
)
BEGIN
UPDATE bnirolovani.record r INNER JOIN(
select Sum(SumError) as Total, count(RollID) as Rolls, sum(Lenght) as Lenght, sum(ExtraMeter) as ExtraMeter from bnirolovani.roll where RecordID=recordId)
i on r.RecordID = recordId SET r.SumError = i.Total, r.SumReels=i.Rolls,r.SumProduced=i.Lenght, r.SumExtraMeter=ExtraMeter;
UPDATE record r
INNER JOIN
(SELECT
SUM(Lenght) AS Total
FROM
bnirolovani.roll
WHERE
RecordID = recordId AND Quality = 0) i ON r.RecordID = recordId
SET
r.QualityE = i.Total;
UPDATE record r
INNER JOIN
(SELECT
SUM(Lenght) AS Total
FROM
bnirolovani.roll
WHERE
RecordID = recordId AND Quality = 1) i ON r.RecordID = recordId
SET
r.QualityII = i.Total;
UPDATE record r
SET
DateProducedF = NOW()
WHERE
r.RecordID = recordID;
END
and here my C# code
private void FinishRecord(int recordID)
{
try
{
string con = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
using (MySqlConnection conection = new MySqlConnection(con))
{
using (MySqlCommand cmd = new MySqlCommand("finish_record", conection))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("recordId", MySqlDbType.Int32).Value=recordID;
conection.Open();
cmd.ExecuteNonQuery();
conection.Close();
}
}
}
catch (Exception ex)
{
throw;
}
Could someone help me where is mistake? I didn't see it :-(
THX
Solved
I replace C# code:
Add procedure call as simple CommandType=Command.Text instead of CommandType.Procedure
and works
private void FinishRecord(int recordID)
{
try
{
string con = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
using (MySqlConnection conection = new MySqlConnection(con))
{
using (MySqlCommand cmd = new MySqlCommand())
{
cmd.Connection = conection;
cmd.CommandType = CommandType.Text;
cmd.CommandText = String.Format("CALL finish_record({0})",recordID);
conection.Open();
cmd.ExecuteNonQuery();
conection.Close();
}
}
}
catch (Exception ex)
{
throw;
}
}

How to return data using sqCommand.ExecuteReader();?

I have Sqlite method that makes SELECT query:
try {
myConn.Open();
using(SQLiteCommand sqCommand = new SQLiteCommand(sql, myConn)) {
sqCommand.CommandText = sql;
SQLiteDataReader reader = sqCommand.ExecuteReader();
return reader.GetString(0);
}
} catch (Exception e) {
// do exception handling
}
I tried to get last inserted id:
sql = 'SELECT id FROM Pacients ORDER BY id DESC LIMIT 1';
I tried to di that like:
return reader.GetString(0);
it's throwing me down on exception "No current row"
After calling ExecuteReader you need to call Read to position on the first record of the dataset. Read returns true/false to inform you if there are records to read. So your code changes to
try {
myConn.Open();
using(SQLiteCommand sqCommand = new SQLiteCommand(sql, myConn)) {
sqCommand.CommandText = sql;
SQLiteDataReader reader = sqCommand.ExecuteReader();
if(reader.Read())
return reader.GetString(0);
else
return ""; // or whatever you want to return if no records are present
}
} catch (Exception e) {
// do exception handling
}
Said that, remember that if you want to retrieve just one column from a single row like you have in your query then it is better to use ExecuteScalar instead of ExecuteReader
try {
myConn.Open();
using(SQLiteCommand sqCommand = new SQLiteCommand(sql, myConn)) {
sqCommand.CommandText = sql;
object result = sqCommand.ExecuteScalar();
return result != null ? result.ToString() : "";
}
} catch (Exception e) {
// do exception handling
}

Exit out of datareader before else, in if-else statement

I would like to find a way to exit out of datareader after the if statement so that I can execute the insert query in else statement. Is there a way to do it?
I am getting the error that dr is still open and hence cannot perform the below query.
sVendorDetails.VendorID = insertcmd.ExecuteNonQuery();
Here is the code:
public class VendorDetails
{
int _VendorID;
string _VendorName;
public int VendorID
{
set { _VendorID = value; }
get { return _VendorID; }
}
public string VendorName
{
set { _VendorName = value; }
get { return _VendorName; }
}
}
public VendorDetails VendorCheck(string sVendorName)
{
SqlCommand cmd = new SqlCommand("dbo.usp_GetVendorByVendorName", myConnection);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#VendorName", SqlDbType.VarChar));
cmd.Parameters["#VendorName"].Value = sVendorName;
VendorDetails sVendorDetails = null;
try
{
myConnection.Open();
SqlDataReader dr = cmd.ExecuteReader();
if (dr.HasRows)
{
while (dr.Read())
{
sVendorDetails = new VendorDetails();
sVendorDetails.VendorID = ((int)dr["VendorID"]);
sVendorDetails.VendorName = ((string)dr["VendorName"]).ToUpper().Trim();
}
}
else if (dr.HasRows!= true)
{
ClientScript.RegisterStartupScript(this.GetType(), "alert", "alert('VendorName:" + sVendorName + " not found. Inserting Vendor details into Vendor and Invoice table.')", true);
SqlCommand insertcmd = new SqlCommand("dbo.InsertVendorName", myConnection);
insertcmd.CommandType = CommandType.StoredProcedure;
insertcmd.Parameters.Add(new SqlParameter("#VendorName", SqlDbType.VarChar));
insertcmd.Parameters["#VendorName"].Value = sVendorName;
sVendorDetails = new VendorDetails();
sVendorDetails.VendorID = insertcmd.ExecuteNonQuery();
sVendorDetails.VendorName = sVendorName;
}
dr.Close();
return sVendorDetails;
}
catch (SqlException err)
{
throw new ApplicationException("DB usp_GetVendorByVendorName Error: " + err.Message);
}
finally
{
myConnection.Close();
}
}
You will need to close/dispose of your DataReader prior to reusing the connection, as it's still being used.
Maybe something like this?
var readerHasRows = false;
using (var dr = cmd.ExecuteReader())
{
readerHasRows = dr.HasRows;
if(readerHasRows)
{
while (dr.Read())
{
sVendorDetails = new VendorDetails();
sVendorDetails.VendorID = ((int)dr["VendorID"]);
sVendorDetails.VendorName = ((string)dr["VendorName"]).ToUpper().Trim();
}
}
}
if(!readerHasRows)
{
ClientScript.RegisterStartupScript(this.GetType(), "alert", "alert('VendorName:" + sVendorName + " not found. Inserting Vendor details into Vendor and Invoice table.')", true);
SqlCommand insertcmd = new SqlCommand("dbo.InsertVendorName", myConnection);
insertcmd.CommandType = CommandType.StoredProcedure;
insertcmd.Parameters.Add(new SqlParameter("#VendorName", SqlDbType.VarChar));
insertcmd.Parameters["#VendorName"].Value = sVendorName;
sVendorDetails = new VendorDetails();
VendorDetails.VendorID = insertcmd.ExecuteNonQuery();
sVendorDetails.VendorName = sVendorName;
}
There are a few things I would like to mention
Your main issue is that you are not closing your DataReader. You can use the using statement for it
You don't need to explicitly open and close the SqlConnection. The SqlCommand object will do it as needed.
You don't need to check with if (dr.HasRows) and then check again in while (dr.Read()). Also, you don't need to loop to pick up only one row of data.
Ideally, I would put the "Fetch" part in a separate function and the "insert" in a separate function, so the functions stay small and reusable.
Your pattern is superfluous if (flag) {TakeAction();} else if (!flag) {TakeAction2();}. Every time the code hits theelse, it will also hit theif (!flag)`
sVendorDetails.VendorID = insertcmd.ExecuteNonQuery(); line looks fishy. If your Stored Procedure returns the VendorId, then you should use ExecuteScalar. Currently it is just storing 1 in all case since you are presumably inserting one row.
Don't discard the original SqlException when creating a custom ApplicationException. Upstream system might want to know more details than you are passing. Pass it along as the InnerException
I have also changed some stylistic aspects:
The variable names changed to the more commonly used camelCase, instead of the incorrectly used Hungarian Notation (sVendorDetails instead of oVendorDetails)
Brace in K&R style
Used var when the right side is a new statement
Use Object Initializers instead of creation+assignment
Below is the code
public VendorDetails VendorCheck(string vendorName, SqlConnection myConnection) {
try {
return GetVendor(vendorName, myConnection) ?? InsertVendor(vendorName, myConnection);
} catch (SqlException err) {
throw new ApplicationException("DB usp_GetVendorByVendorName Error: " + err.Message, err);
}
}
VendorDetails GetVendor(string vendorName, SqlConnection myConnection) {
using (var cmd = new SqlCommand("dbo.usp_GetVendorByVendorName", myConnection)) {
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#VendorName", SqlDbType.VarChar));
cmd.Parameters["#VendorName"].Value = vendorName;
using (SqlDataReader dr = cmd.ExecuteReader()) {
ClientScript.RegisterStartupScript(this.GetType(), "alert", "alert('VendorName:" + vendorName + " not found. Inserting Vendor details into Vendor and Invoice table.')", true); // TODO: Does this really belong here!?!?
if (dr.Read()) {
return new VendorDetails {
VendorID = ((int)dr["VendorID"]),
VendorName = ((string)dr["VendorName"]).ToUpper().Trim()
};
}
}
}
return null;
}
VendorDetails InsertVendor(string vendorName, SqlConnection myConnection) {
using (var insertcmd = new SqlCommand("dbo.InsertVendorName", myConnection)) {
insertcmd.CommandType = CommandType.StoredProcedure;
insertcmd.Parameters.Add(new SqlParameter("#VendorName", SqlDbType.VarChar));
insertcmd.Parameters["#VendorName"].Value = vendorName;
return new VendorDetails {
VendorID = (int)insertcmd.ExecuteScalar(),
VendorName = vendorName
};
}
}

Trying to auto fill textboxes with by selecting an item in combobox. Gets an error when form opens.

I have this stored procedure that gets the product table with provided parameter
CREATE PROCEDURE DisplayProductParameter #id nvarchar(100)
AS
BEGIN
SET NOCOUNT ON;
SELECT P.product_id, P.product_name, P.product_price, T.[type_name], T.[type_fee], T.[type_id]
FROM Product P
INNER JOIN [Product Type] T ON P.[type_id] = T.[type_id]
WHERE P.product_id = #id
END;
GO
I call it with this function in C#
public SqlCommand InitSqlCommand(string query, CommandType commandType)
{
var Sqlcommand = new SqlCommand(query, con);
Sqlcommand.CommandType = commandType;
return Sqlcommand;
}
Then I store it in a DataTable
public DataTable GetData(SqlCommand command)
{
var dataTable = new DataTable();
var dataSet = new DataSet();
var dataAdapter = new SqlDataAdapter { SelectCommand = command };
dataAdapter.Fill(dataTable);
return dataTable;
}
Then this is how I get the DataTable
public DataTable DisplayProductParameter()
{
string getProductIdParam = "DisplayProductParameter";
var command = Connection.InitSqlCommand(getProductIdParam, CommandType.StoredProcedure);
command.Parameters.AddWithValue("#id", P.Id);
return Connection.GetData(command);
}
This is how I should autofill textboxes whenever I click on the combobox
private void cmbProductId_SelectedIndexChanged(object sender, EventArgs e)
{
try
{
p.Id = cmbProductId.SelectedItem.ToString();
dtbProduct = po.DisplayProductParameter();
for (int i = 0; i < dtbProduct.Rows.Count; i++)
{
txtProductType.Text = dtbProduct.Rows[i]["type_name"].ToString();
txtPrice.Text = dtbProduct.Rows[i]["product_price"].ToString();
txtProductName.Text = dtbProduct.Rows[i]["product_name"].ToString();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
But I get this error message at the start of the form
Procedure or function 'DisplayProductParameter' expects parameter
'#id', which was not supplied.
Logically your code seems correct.
In order to get more information on where and why this is happening, could you add a breakpoint on this line:
public DataTable DisplayProductParameter()
{
string getProductIdParam = "DisplayProductParameter";
var command = Connection.InitSqlCommand(getProductIdParam, CommandType.StoredProcedure);
-->command.Parameters.AddWithValue("#id", P.Id);
return Connection.GetData(command);
}
and run in debugging mode to see what the value of P.Id is. It could be passing a null or empty string value into the procedure.

What is the return value of ExecuteNonQuery statement?

I have written a program to verify username and password using 3 tier architecture in Visual Studio 10. In the DAL, ExecuteNonQuery statement returns '-1'. But I want it to return '1' if username and password are correct or '0'if not correct.
Code snipped for DAL:
public class LoginDataAccess
{
SqlConnection con;
string constr = ConfigurationManager.ConnectionStrings["localhostakash"].ToString();
public int LoginData(LoginEntity elOj)
{
try
{
con = new SqlConnection(constr);
int result;
if(ConnectionState.Closed==con.State)
con.Open();
SqlCommand cmd = new SqlCommand("uspuserlogin", con);
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#Username", elOj.Username);
cmd.Parameters.AddWithValue("#Password", elOj.Password);
result = Convert.ToInt32(cmd.ExecuteNonQuery());
return result;
}
catch (Exception ex)
{
throw ex;
}
finally
{
con.Close();
}
}
}
Code snippet for BLL:
public class LoginLogic
{
LoginDataAccess lda = new LoginDataAccess();
public int userValidate(LoginEntity le)
{
int result = 0;
try
{
result = Convert.ToInt32(lda.LoginData(le));
}
catch (Exception ex)
{
//response.write(ex.Message);
}
return result;
}
}
Code snippet for button function:
protected void Button1_Click(object sender, EventArgs e)
{
LoginLogic ll = new LoginLogic();
LoginEntity le = new LoginEntity();
int v;
le.Username = TextBox1.Text;
le.Password = TextBox2.Text;
v = Convert.ToInt32(ll.userValidate(le));
if (v == 1)
{
Label1.Text = "LOGGED IN SUCCESSFULLY!";
}
else
{
Label1.Text = "TRY AGAIN...";
}
}
Here is the documentation:
For UPDATE, INSERT, and DELETE statements, the return value is the number of rows affected by the command. When a trigger exists on a table being inserted or updated, the return value includes the number of rows affected by both the insert or update operation and the number of rows affected by the trigger or triggers. For all other types of statements, the return value is -1. If a rollback occurs, the return value is also -1.
Read more here: https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.executenonquery.aspx
You are calling a stored procedure "uspuserlogin". That's why ExecuteNonQuery returns -1.
You can return value as row if you need to know result of operation.
ALTER PROCEDURE [dbo].[uspuserlogin]
#username nvarchar(255),
#password nvarchar(255)
AS
BEGIN
SELECT COUNT(*) AS Found
FROM [Users]
WHERE [Username] = #username AND [Password] = #password
END
In code:
var obj = cmd.ExecuteScalar();
return (int)obj;
// Somewhere in code
if (loginDataAccess.LoginData(loginEntity) == 1)
// Authorize
Of course, you can transform it to bool for your convenience:
public bool LoginData(LoginEntity elOj)
{
try
{
con = new SqlConnection(constr);
int result;
if(ConnectionState.Closed==con.State)
con.Open();
SqlCommand cmd = new SqlCommand("uspuserlogin", con);
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#Username", elOj.Username);
cmd.Parameters.AddWithValue("#Password", elOj.Password);
var obj = cmd.ExecuteScalar();
return ((int)obj > 0);
}
catch (Exception ex)
{
throw ex;
}
finally
{
con.Close();
}
}

Categories