how to pass an optional parameter to sql function from C# - c#

I have an issue. please help to solve my problem
I have a SQL function
function [dbo].[fnKudishikaAmt]
(#ParishName nvarchar(100), #Hno int, #dateto datetime = Null)
Returns Decimal(15,2)
This function shows proper result by using the execute command
Select dbo.fnKudishikaAmt('St.George Malankara Catholic Church', 29, default)
My requirement is this function should be called from C#
I am getting the error
Conversion failed when converting datetime from character string
Code:
public double kudishikatotal(string ParishName, Int32 HouseNo)
{
String SQLText = "select ChurchDB.dbo.fnKudishikaAmt(#ParishName, #Hno, #dateto) as fnresult";
SqlCommand cmd = new SqlCommand(SQLText);
cmd.CommandType = CommandType.Text;
cmd.Parameters.AddWithValue("#ParishName", ParishName);
cmd.Parameters.AddWithValue("#Hno", HouseNo);
cmd.Parameters.AddWithValue("#dateto", "default");
string rval = GetSingleValue(cmd);
double kudiamt = 0;
if (rval != null)
{
kudiamt = Convert.ToDouble(rval);
}
return kudiamt;
}
private static string GetSingleValue(SqlCommand cmd)
{
string ConString = connectionstring();
string returnvalue = "";
using (SqlConnection con = new SqlConnection(ConString))
{
cmd.Connection = con;
con.Open();
returnvalue = cmd.ExecuteScalar().ToString();
con.Close();
}
return returnvalue;
}

If you've declared default value for parameter in stored procedure - then you can just not pass this parameter from c# code at all, and in this case it will have default value.
In your case exception thrown because it's impossible to convert string "default" to SqlDateTime which is your parameter type.

YOu can use if condition while sending the datetime parameter.
if(some condition)
{
cmd.Parameters.AddWithValue("#dateto", dateTimeValue);
}
Here datetimeValue is the value you want to pass. So you will be passing dateTimeValue only if required.
The error is due to the string "default" you passed.

Related

Input string was not in a correct format error not getting resolved for class function

I am getting the error as
Input string was not in a correct format.
newRow["col_frm_bin_id"] = CF.ExecuteScaler("Select location_name from wms_storage_bin where mkey = " + e.Record["from_bin"] + "");
public string ExecuteScaler(string StrQuery)
{
DB.EConnection();
cmd = new SqlCommand(StrQuery, DB.conn);
cmd.Connection = DB.conn;
int val=Convert.ToInt32(cmd.ExecuteScalar());
DB.conn.Close();
string ret = val.ToString();
return ret;
}
I tried with converting but still it didn't worked
Your return column name sounds like its a string variable, Change it with int type column, or remove Convert.ToInt32 from code side
public string ExecuteScaler(string StrQuery)
{
DB.EConnection();
cmd = new SqlCommand(StrQuery, DB.conn);
cmd.Connection = DB.conn;
string ret=cmd.ExecuteScalar().ToString();
DB.conn.Close();
return ret;
}
i think you should do like this but this is not good practice and also not safe
your mkey value should be in between quotes
mkey = '" + e.Record["from_bin"] + "'
newRow["col_frm_bin_id"] = CF.ExecuteScaler("Select location_name from wms_storage_bin where mkey = '" + e.Record["from_bin"] + "'");
public string ExecuteScaler(string StrQuery)
{
DB.EConnection();
cmd = new SqlCommand(StrQuery, DB.conn);
cmd.Connection = DB.conn;
int val=Convert.ToInt32(cmd.ExecuteScalar());
DB.conn.Close();
string ret = val.ToString();
return ret;
}
but sending parameters is best practice
I'll try summarize various pieces of information from other answers and comments.
First, your existing code is open to Sql injections. This is a very bad thing. To avoid the risk of Sql injection you shoul use Parametrized queries. See for instance here.
That means your ExecuteScaler method should not take a string as its argument, but instead a SqlCommand (I have corrected the spelling of scalar):
public string ExecuteScalar(SqlCommand query) { ... }
Your current implementation of ExecuteScaler is also at risk of leaking SqlConnetions. If an exception is thrown in this method before the DB.conn.Close() line, the connection will not be closed. For instance, in the case you described in the question, the following line is the prime suspect:
int val = Convert.ToInt32(cmd.ExecuteScalar());
With your current call to the method, you seem to be fetching something that is a string from the database. Unless that string is convertible to Int32, this line will throw an exception, and the connection will not be closed. To fix this, you should at the minimum add a try { ... } finally { ... } block:
public string ExecuteScalar(SqlCommand query)
{
try
{
DB.EConnection();
query.Connection = DB.conn;
string ret = query.ExecuteScalar().ToString();
return ret;
}
finally
{
if(DB.conn.State == ConnectionState.Open)
DB.conn.Close();
}
}
I would also suggest that you create separate versions of ExecuteScalar for different expected return types. Perhaps:
public string GetStringScalar(SqlCommand query)
public int GetInt32Scalar(SqlCommand query)
etc.
The calling code then needs to be changed:
string locName = null;
using (SqlCommand locNameCommand = new SqlCommand(#"
select location_name
from wms_storage_bin
where mkey = #mkey
"))
{
locNameCommand.Parameters.AddWithValue("mkey", e.Record["from_bin"]);
locName = GetStringScalar(locNameCommand);
}
newRow["col_frm_bin_id"] = locName;

Method Returning only " "

I need return string of DateTime. More this method is returning only null
receives an input string, convert to DateTime and i need put DateTime on valor.retorna_date_time string variable.
Code:
public void Search_DATE(string param_date)
{
SqlDataReader objReader;
SqlCommand objcmd = null;
vsql = "SELECT [IDCADASTRO],[RGP],[PEIXE],[PESO],[QUANTIDADE],[DATA_REGISTRO] FROM cadastro WHERE DATA_REGISTRO LIKE #DATA_REGISTRO";
if (this.Conectar())
{
try
{
DateTime dtParam = DateTime.Parse(param_date);
objcmd = new SqlCommand(vsql, objCon);
objcmd.Parameters.Add(new SqlParameter("#DATA_REGISTRO", dtParam));
objReader = objcmd.ExecuteReader();
if (objReader.Read())
{
valor.retorna_date_time = objReader.GetString(6);
}
}
catch (SqlException erro)
{
throw erro;
}
finally
{
this.Desconectar();
}
}
}
Input Parameters:
DateTime myDateTime = DateTime.Now;
string DateTimesql = myDateTime.ToString("dd-MM-yyyy");
objSQL.Search_DATE(DateTimesql);
valor.retorna_date_time is a global string variable.
Your SELECT statement returns 6 columns:
[IDCADASTRO],[RGP],[PEIXE],[PESO],[QUANTIDADE],[DATA_REGISTRO]
But IDatareader's GetString(n) method is 0-based, so GetString(6) returns the 7th column, which there isn't.
Change it to GetString(5).
Without seeing your table structure, I would try GetOrdinal.
Change
valor.retorna_date_time = objReader.GetString(6)
to
valor.retorna_date_time = objReader.GetOrdinal("DATA_REGISTRO");
Since you only seem to need just the last column, you can change your query to:
"SELECT [DATA_REGISTRO] FROM cadastro WHERE DATA_REGISTRO LIKE #DATA_REGISTRO";
And then read it using ExecuteScalar
objcmd = new SqlCommand(vsql, objCon);
objcmd.Parameters.AddWithValue("#DATA_REGISTRO", dtParam);
valor.retorna_date_time = objcmd.ExecuteScalar().ToString();
EDIT:
ExecuteScalar() will return a null reference if the query did not return anything.
You should check if it is null before converting it to string and passing it to valor.retorna_date_time. Do something like:
string returnValue = objcmd.ExecuteScalar() == null ?? String.Empty : objcmd.ExecuteScalar().ToString();

ASP.NET label is displaying "System.Data" message

I am having a strange message displayed on the asp:label when trying to display data from a database. During page_load the asp:label is meant to be populated from a datasource however is displays the following message/text "System.Data.SqlClient.SqlDataReader"
What could be causing this?
I have written a small method in the page load of the .aspx.cs page.
labelName is the one which is displaying this message:
public partial class edit_questionnaire : System.Web.UI.Page
{
OsqarSQL GetData;
protected void Page_Load(object sender, EventArgs e)
{
string questionnaireId = Session["qID"].ToString();
int qid = Convert.ToInt32(questionnaireId);
GetData = new OsqarSQL();
string name = GetData.GetQuestionnaireName(qid);
labelName.Text = name;
}
}
Which calls the following method:
public string GetQuestionnaireName(int questionnaireId)
{
string returnValue = string.Empty;
SqlCommand myCommand = new SqlCommand("GetQuestionnaireName", _productConn);
myCommand.CommandType = CommandType.StoredProcedure;
myCommand.Parameters.Add(new SqlParameter("#QUEST_ID", SqlDbType.Int));
myCommand.Parameters[0].Value = questionnaireId;
SqlDataReader qName = getData(myCommand);
while (qName.Read())
{
returnValue = qName.ToString();
}
_productConn.Close();
return returnValue;
}
And uses this stored procedure:
ALTER PROCEDURE [hgomez].[GetQuestionnaireName]
(
#QUEST_ID int
)
AS
/*SET NOCOUNT ON;*/
SELECT QuestionnaireName FROM [Questionnaires] WHERE QuestionnaireID = #QUEST_ID
RETURN
public string GetQuestionnaireName(int questionnaireId)
{
string returnValue = string.Empty;
SqlCommand myCommand = new SqlCommand("GetQuestionnaireName", _productConn);
myCommand.CommandType = CommandType.StoredProcedure;
myCommand.Parameters.Add(new SqlParameter("#QUEST_ID", SqlDbType.Int));
myCommand.Parameters[0].Value = questionnaireId;
SqlDataReader qName = getData(myCommand);
while (qName.Read())
{
returnValue = qName[0].ToString();
}
_productConn.Close();
return returnValue;
}
You were assigning the SqlDataReader to your returnValue rather than reading the value.
You need to use the GetValue method of SqlDataReader
SqlDataReader qName = getData(myCommand);
while (qName.Read())
{
returnValue = qName.GetValue(0).ToString();
}
The problem is in the line below - it is a data reader, not a string returned by the data reader:
returnValue = qName.ToString();
You should replace this with
returnValue = qName.GetString(0);
What you are getting is SqlDataReader type as string. To read a string out of SqlDataReader you will have to use GetString method. And as parameter you should pass the index of field. As you have only one field which you are trying to read pass zero here.
You should return value like this
returnValue = qName.GetString(0);
Alternatively you can do
returnValue = qName.GetString("QuestionnaireName");
//this is better to name fields
Or you can simply write
returnValue = qName[0].ToString();
That's because you are calling SqlDataReader.ToString(). That will return the string of the type, which is in fact "System.Data.SqlClient.SqlDataReader".
Use returnValue = qName.GetString(0);
You are converting qName.ToString(), you can try qName[0].ToString(); with [0] being the index of select column inside your stored procedure.

Handling ExecuteScalar() when no results are returned

I am using the following SQL query and the ExecuteScalar() method to fetch data from an Oracle database:
sql = "select username from usermst where userid=2"
string getusername = command.ExecuteScalar();
It is showing me this error message:
System.NullReferenceException: Object reference not set to an instance of an object
This error occurs when there is no row in the database table for userid=2.
How should I handle this situation?
According to MSDN documentation for DbCommand.ExecuteScalar:
If the first column of the first row in the result set is not found, a
null reference (Nothing in Visual Basic) is returned. If the value in
the database is null, the query returns DBNull.Value.
Consider the following snippet:
using (var conn = new OracleConnection(...)) {
conn.Open();
var command = conn.CreateCommand();
command.CommandText = "select username from usermst where userid=2";
string getusername = (string)command.ExecuteScalar();
}
At run-time (tested under ODP.NET but should be the same under any ADO.NET provider), it behaves like this:
If the row does not exist, the result of command.ExecuteScalar() is null, which is then casted to a null string and assigned to getusername.
If the row exists, but has NULL in username (is this even possible in your DB?), the result of command.ExecuteScalar() is DBNull.Value, resulting in an InvalidCastException.
In any case, the NullReferenceException should not be possible, so your problem probably lies elsewhere.
First you should ensure that your command object is not null. Then you should set the CommandText property of the command to your sql query. Finally you should store the return value in an object variable and check if it is null before using it:
command = new OracleCommand(connection)
command.CommandText = sql
object userNameObj = command.ExecuteScalar()
if (userNameObj != null)
string getUserName = userNameObj.ToString()
...
I'm not sure about the VB syntax but you get the idea.
I just used this:
int? ReadTerminalID()
{
int? terminalID = null;
using (FbConnection conn = connManager.CreateFbConnection())
{
conn.Open();
FbCommand fbCommand = conn.CreateCommand();
fbCommand.CommandText = "SPSYNCGETIDTERMINAL";
fbCommand.CommandType = CommandType.StoredProcedure;
object result = fbCommand.ExecuteScalar(); // ExecuteScalar fails on null
if (result.GetType() != typeof(DBNull))
{
terminalID = (int?)result;
}
}
return terminalID;
}
The following line:
string getusername = command.ExecuteScalar();
... will try to implicitly convert the result to string, like below:
string getusername = (string)command.ExecuteScalar();
The regular casting operator will fail if the object is null.
Try using the as-operator, like this:
string getusername = command.ExecuteScalar() as string;
sql = "select username from usermst where userid=2"
var _getusername = command.ExecuteScalar();
if(_getusername != DBNull.Value)
{
getusername = _getusername.ToString();
}
Check out the example below:
using System;
using System.Data;
using System.Data.SqlClient;
class ExecuteScalar
{
public static void Main()
{
SqlConnection mySqlConnection =new SqlConnection("server=(local)\\SQLEXPRESS;database=MyDatabase;Integrated Security=SSPI;");
SqlCommand mySqlCommand = mySqlConnection.CreateCommand();
mySqlCommand.CommandText ="SELECT COUNT(*) FROM Employee";
mySqlConnection.Open();
int returnValue = (int) mySqlCommand.ExecuteScalar();
Console.WriteLine("mySqlCommand.ExecuteScalar() = " + returnValue);
mySqlConnection.Close();
}
}
from this here
SQL NULL value
equivalent in C# is DBNull.Value
if a NULLABLE column has no value, this is what is returned
comparison in SQL: IF ( value IS NULL )
comparison in C#: if (obj == DBNull.Value)
visually represented in C# Quick-Watch as {}
Best practice when reading from a data reader:
var reader = cmd.ExecuteReader();
...
var result = (reader[i] == DBNull.Value ? "" : reader[i].ToString());
In my experience, there are some cases the returned value can be missing and thus execution fails by returning null. An example would be
select MAX(ID) from <table name> where <impossible condition>
The above script cannot find anything to find a MAX in. So it fails. In these such cases we must compare the old fashion way (compare with C# null)
var obj = cmd.ExecuteScalar();
var result = (obj == null ? -1 : Convert.ToInt32(obj));
If you either want the string or an empty string in case something is null, without anything can break:
using (var cmd = new OdbcCommand(cmdText, connection))
{
var result = string.Empty;
var scalar = cmd.ExecuteScalar();
if (scalar != DBNull.Value) // Case where the DB value is null
{
result = Convert.ToString(scalar); // Case where the query doesn't return any rows.
// Note: Convert.ToString() returns an empty string if the object is null.
// It doesn't break, like scalar.ToString() would have.
}
return result;
}
Always have a check before reading row.
if (SqlCommand.ExecuteScalar() == null)
{
}
This is the easiest way to do this...
sql = "select username from usermst where userid=2"
object getusername = command.ExecuteScalar();
if (getusername!=null)
{
//do whatever with the value here
//use getusername.toString() to get the value from the query
}
In your case either the record doesn't exist with the userid=2 or it may contain a null value in first column, because if no value is found for the query result used in SQL command, ExecuteScalar() returns null.
Alternatively, you can use DataTable to check if there's any row:
SqlCommand cmd = new SqlCommand("select username from usermst where userid=2", conn);
SqlDataAdapter adp = new SqlDataAdapter(cmd);
DataTable dt = new DataTable();
adp.Fill(dt);
string getusername = "";
// assuming userid is unique
if (dt.Rows.Count > 0)
getusername = dt.Rows[0]["username"].ToString();
private static string GetUserNameById(string sId, string connStr)
{
System.Data.SqlClient.SqlConnection conn = new System.Data.SqlClient.SqlConnection(connStr);
System.Data.SqlClient.SqlCommand command;
try
{
// To be Assigned with Return value from DB
object getusername;
command = new System.Data.SqlClient.SqlCommand();
command.CommandText = "Select userName from [User] where userid = #userid";
command.Parameters.AddWithValue("#userid", sId);
command.CommandType = CommandType.Text;
conn.Open();
command.Connection = conn;
//Execute
getusername = command.ExecuteScalar();
//check for null due to non existent value in db and return default empty string
string UserName = getusername == null ? string.Empty : getusername.ToString();
return UserName;
}
catch (Exception ex)
{
throw new Exception("Could not get username", ex);
}
finally
{
conn.Close();
}
}
Slight conjecture: if you check the stack for the exception, it is being thrown then the ADO.NET provider for Oracle is reading the underlying rowset to get the first value.
If there is no row, then there is no value to find.
To handle this case execute for a reader and handle Next() returning false for the case of no match.
I Use it Like This with Microsoft Application Block DLL (Its a help library for DAL operations)
public string getCopay(string PatientID)
{
string sqlStr = "select ISNULL(Copay,'') Copay from Test where patient_id=" + PatientID ;
string strCopay = (string)SqlHelper.ExecuteScalar(CommonCS.ConnectionString, CommandType.Text, sqlStr);
if (String.IsNullOrEmpty(strCopay))
return "";
else
return strCopay ;
}
I have seen in VS2010
string getusername = command.ExecuteScalar();
gives compilation error,
Cannot implicitly convert type object to string.
So you need to write
string getusername = command.ExecuteScalar().ToString();
when there is no record found in database it gives error
Object reference not set to an instance of an object
and when I comment '.ToString()', it is not give any error. So I can say ExecuteScalar not throw an exception. I think anserwer given by #Rune Grimstad is right.
I had this issue when the user connecting to the database had CONNECT permissions, but no permissions to read from the database. In my case, I could not even do something like this:
object userNameObj = command.ExecuteScalar()
Putting this in a try/catch (which you should probably be doing anyway) was the only way I could see to handle the insufficient permission issue.
object objUserName;
objUserName = command.ExecuteScalar();
if (objUserName == null) //if record not found ExecuteScalar returns null
{
return "";
}
else
{
if (objUserName == DBNull.Value) //if record found but value in record field is null
{
return "";
}
else
{
string getusername = objUserName.ToString();
return getusername;
}
}
/* Select some int which does not exist */
int x = ((int)(SQL_Cmd.ExecuteScalar() ?? 0));
I used this in my vb code for the return value of a function:
If obj <> Nothing Then
Return obj.ToString()
Else
Return ""
End If
Try this code, it appears to solve your problem.
Dim MaxID As Integer = Convert.ToInt32(IIf(IsDBNull(cmd.ExecuteScalar()), 1, cmd.ExecuteScalar()))
I'm using Oracle.
If your sql returns numeric value, which is int, you need to use Convert.ToInt32(object). Here is the example below:
public int GetUsersCount(int userId)
{
using (var conn = new OracleConnection(...)){
conn.Open();
using(var command = conn.CreateCommand()){
command.CommandText = "select count(*) from users where userid = :userId";
command.AddParameter(":userId", userId);
var rowCount = command.ExecuteScalar();
return rowCount == null ? 0 : Convert.ToInt32(rowCount);
}
}
}
Try this
sql = "select username from usermst where userid=2"
string getusername = Convert.ToString(command.ExecuteScalar());

Checking SQL Table Value / Using return value as parameter

I am a little new to using ASP.NET with SQL and I am having trouble with this. The function CheckStatus() basically calls a stored procedure to check a table column value which is only going to be 1 or 0. The data type in the table is a bit so my idea was to convert it into a string and do a check from there. After that, it returns 1 or 0.
The second function ChangeFileStatus() is supposed to change the value to either 1 or 0 depending on what it is.
However, my question is can I use my return value as a parameter in another function? I wanted to make an if condition checking for the return value. Please help.
private void ChangeFileStatus()
{
CheckStatus(); // i wanna call this here but use return value as the paramter
using (SqlConnection con = new SqlConnection(CS))
{
cmd = new SqlCommand("spEcovaFilesChangeJobStats", con); //call your stored procedure within the ""
cmd.CommandType = CommandType.StoredProcedure; // this is saying that the command type is a stored procedure
con.Open();
cmd.ExecuteNonQuery();
con.Close();
}
}
private int CheckStatus()
{
int status = 0;
using (SqlConnection con = new SqlConnection(CS))
{
cmd = new SqlCommand("spEcovaGetFilesJobStats", con); //call your stored procedure within the ""
cmd.CommandType = CommandType.StoredProcedure; // this is saying that the command type is a stored procedure
con.Open();
cmd.ExecuteNonQuery();
SqlDataReader rdr = cmd.ExecuteReader();
if (rdr.HasRows)
{
while (rdr.Read())
{
string active = rdr["IsActive"].ToString();
if (active == "1" )
{
status = 0 ;
}
else
{
status = 1;
}
}
}
con.Close();
return status;
}
So a couple of things that I think you might want to reconsider with your application. You said your table stores a bit, Why not use the C# equivalent of bool? no conversion needed. Also about your call to your stored procedure, are you ever expecting more than 1 value? if not why not do something as follows for check status, This provides the user a clear picture versus a magic number that you have to figure out/remember
private bool CheckStatus()
{
bool wasSuccessful = false;
using (SqlConnection con = new SqlConnection(CS))
{
cmd = new SqlCommand("spEcovaGetFilesJobStats", con);
cmd.CommandType = CommandType.StoredProcedure;
con.Open();
wasSuccessful = (bool)cmd.ExecuteScalar();
con.Close();
}
return status;
}
And if you wanted this to roll into the change status you would only have to change the signature of ChangeFileStatus like learningNew
mentioned. But if your status can only ever be a bit/boolean do you really need to pass it?
Change the method
private void ChangeFileStatus() {}
to
private void ChangeFileStatus(int Status)
{
//check staus value here
}
and you can call the method like
int Status=CheckStatus();
ChangeFileStatus(Status);
or
ChangeFileStatus(CheckStatus());

Categories

Resources