I'm working on a web form that validates data that the user uploads in the form of an Excel file. The code iterates through each row in the spreadsheet and checks against various rules, one of which is that the reference must be a unique value. I have a stored procedure that takes the userID and referenceNum as parameters:
BEGIN
SET NOCOUNT ON;
DECLARE #Status AS BIT
IF EXISTS (SELECT [TransactionMstID]
FROM [dbo].[tbl_TransactionMst]
WHERE [TransactionRef] = #DocumentNumber
AND [SupplierID] = #SupplierID)
BEGIN
SET #Status = 0
END
ELSE
BEGIN
SET #Status = 1
END
SELECT #Status AS [Status]
END
When I try different scenarios in SQL Server Management Studio (SSMS), I get the desired outputs e.g. a "0" when the reference exists and a "1" when it doesn't.
The problem arises in my C# code, it executes the stored procedure, but in my testing I get the same result irrespective of whether the data exists or not.
Here's the core of the C#:
bool returnValue = true;
if (Docnumber != null)
{
SqlConnection con = new SqlConnection(GlobalSettings.connection);
con.Open();
SqlCommand Cmd = new SqlCommand("p_ValRefNumber", con);
Cmd.CommandType = System.Data.CommandType.StoredProcedure;
Cmd.Parameters.AddWithValue("#DocumentNumber", Docnumber);
Cmd.Parameters.AddWithValue("#SupplierID", SupplierID);
Cmd.ExecuteNonQuery();
SqlDataReader dr = Cmd.ExecuteReader();
while (dr.Read())
{
bool Status = convertor.ConvertToBool(dr["Status"]);
string test = dr["Status"].ToString();
int testint = convertor.ConvertToInt(dr["Status"].ToString());
if (Status == false)
{
//throw new System.Exception(CEObj.GetErrorDesc(101));
returnValue = false;
}
}
dr.Close();
con.Close();
}
return returnValue;
}
No matter what the value of docnumber is in testing, it always shows as True. I've added a breakpoint so that I can check each time and then test in SSMS and I get conflicting results.
Is my logic wrong? Does Visual Studio treat the values differently? How is the result not consistent when converting it to a string - at the very least? It always seems to read a value of "1" in VS but varies in SSMS
Edit: here's my converter method's code:
public static bool ConvertToBool(object value)
{
bool result = false;
if (value != null)
{
bool.TryParse(value.ToString(), out result);
}
return result;
}
bool.TryParse isn't doing what the convertor (sic) code thinks it does.
bool.TryParse returns true if the value parameter equals bool.TrueString, which is the literal string "True". It returns false for any other value, which means it returns false for both 0 and 1.
Also, T-SQL bit values are numbers. The converter code isn't really necessary - just convert the return value to an Int32 and do a comparison.
using (var con = new SqlConnection(GlobalSettings.connection))
{
con.Open();
using (var cmd = new SqlCommand() { Connection = con, CommandType = CommandType.StoredProcedure, CommandText = "p_ValRefNumber" })
{
/* Assuming both parameters are integers.
Change SqlDbType if necessary. */
cmd.Parameters.Add(new SqlParameter() { ParameterName = "#DocumentNumber", SqlDbType = SqlDbType.Int, Value = Docnumber });
cmd.Parameters.Add(new SqlParameter() { ParameterName = "#SupplierID", SqlDbType = SqlDbType.Int, Value = SupplierID });
using (var reader = cmd.ExecuteReader(CommandBehavior.CloseConnection))
{
return dr.Read() && (Convert.ToInt32(dr["Status"]) == 1)
}
}
}
Related
I am trying to insert data into a SQL table. The data types I am having issues with are nullable floats. When the NULL values are inserted they change to 0. How can I keep them NULL.
private void InsertStatisticsData(DataTable dt)
{
//check isin periodicity and As of Date
foreach(DataRow row in dt.Rows)
{
DataTable queryResultTable = SQL.Query($#"SELECT * FROM Statistics
WHERE [CodeID] = '{row["CodeID"]}'
AND [Periodicity] = '{row["Periodicity"]}'
AND [As of Date] = '{row["As of Date"]}'");
if(queryResultTable.Rows.Count == 0)
{
//Check for Null Values
for(int i = 0; i < row.ItemArray.Count(); i++)
{
if (Convert.ToString(row[i]) == "")
row[i] = (object)DBNull.Value;
}
//Insert Data Into DataBase
SQL.NonQuery($#"INSERT INTO Statistics
VALUES ('{row["CodeID"]}' ,
'{row["Volatility"]}',
'{row["Beta"]}',
'{row["Info Ratio"]}',
'{row["Tracking"]}',
'{row["Drawdown"]}',
'{row["Periodicity"]}',
'{row["As of Date"]}')");
}
}
}
Nonquery Function:
public static void NonQuery(string query, string databaseName = "Database", string serverAddress = "server-name", int commandTimeout = 30)
{
string connString = $"Server = {serverAddress}; Database = {databaseName}; Trusted_Connection = True";
using (SqlConnection sqlConn = new SqlConnection(connString))
using (SqlCommand cmd = new SqlCommand(query, sqlConn))
{
sqlConn.Open();
cmd.CommandTimeout = commandTimeout;
cmd.ExecuteNonQuery();
}
}
You need to make sure your database column structure contains NULL types where you actually need them.
Also make sure you don't have any default constraints set, which automatically values the columns to 0 when null is assigned.
if(Convert.ToString(null) == "")
will be evaluated as false.
so below code won't get executed
row[i] = (object)DBNull.Value;
on a side note, you should use SqlParameters instead of appending values in a string.
This may seem a little heavy handed and bloaty, but if you use parameters (and you really, truly should), I have an extention method I use in my project to take any command object and loop through the parameters to turn a .NET null into a DbNull:
private static void ProcessNullParameters(this DbCommand command)
{
foreach (DbParameter param in command.Parameters)
{
if (param.Value == null)
param.Value = DBNull.Value;
}
}
This way, if your native object returns a null value, you can call the extention method against the command object. I couldn't tell what your SQL object was in your example (a framework of some type?), but presumably, somewhere behind the scenes something like this would be going on:
SqlCommand cmd = new SqlCommand("insert into Statistics values (#Code, #Volatility)", conn);
cmd.Parameters.Add("#Code", SqlDbType.VarChar);
cmd.Parameters.Add("#Volatility", SqlDbType.Decimal);
foreach (DataRow dr in dt.Rows)
{
cmd.Parameters[0].Value = dr["Code"];
cmd.Parameters[1].Value = dr["Volatility"];
// And here you convert your nulls to DbNull
cmd.ProcessNullParameters();
cmd.ExecuteNonQuery();
}
The alternative would be to do this on every value declaration that is nullable.
cmd.Parameters[0].Value = dr["Code"] ?? DbNull.Value
cmd.Parameters[1].Value = dr["Volatility"] ?? DbNull.Value;
I'm asking about only sync methods of the SqlCommand class.
There are three methods (as everyone know) - ExecuteReader(), ExecuteScalar() and ExecuteNonQuery().
Which kind of this methods is more suitable for stored procedure likes this :
CREATE PROCEDURE [dbo].[pr_test]
#partherId UNIQUEIDENTIFIER,
#lowerBound SMALLINT = -1 out,
#upperBound SMALLINT = -1 out
AS
BEGIN
SET NOCOUNT ON;
SELECT
#lowerBound = ISNULL(MIN(SrartDayNumber), -1)
,#upperBound = ISNULL(MAX(EndDayNumber), -1)
FROM [CpsOther].[dbo].[FinDocument] f
WHERE f.partherId = #partherId
END
I need only out params and nothing else. I don't know which method of the SqlCommand is more suitable in this situation? Or it's doesn't matter. (The results are same)
int lowerBound = -1;
int upperBound = -1;
using (SqlConnection connection = new SqlConnection(_connectionString))
{
using (SqlCommand command = new SqlCommand())
{
command.Connection = connection;
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "[dbo].[pr_test]";
SqlParameter lowerBoundParam = new SqlParameter
{
ParameterName = "#lowerBound",
Value = lowerBound,
Direction = ParameterDirection.Output
};
SqlParameter upperBoundParam = new SqlParameter
{
ParameterName = "#upperBound",
Value = upperBound,
Direction = ParameterDirection.Output
};
command.Parameters.AddWithValue("#partnerId", Guid.Empty);
command.Parameters.Add(lowerBoundParam);
command.Parameters.Add(upperBoundParam);
connection.Open();
object result = command.ExecuteScalar();
//or object result = command.ExecuteNonQuery();
lowerBound = lowerBoundParam.Value as int? ?? -1;
lowerBound = lowerBoundParam.Value as int? ?? -1;
}
}
ExecuteNonQuery is the better solution for this. The other two are for commands that return a rowset.
To elaborate:
ExecuteReader is for situations where you want to iterate over a set of rows being returned by the command.
ExecuteScalar is for situations where you want to receive the first column of the first row being returned. It will automatically discard all other row data.
ExecuteNonQuery is for commands that do not return rowsets directly.
They all have the same abilities as regards parameters with directions of Output, InputOutput or ReturnValue. The only difference is how they deal with rowsets.
I am trying to determine if a specific value exists in a Oracle database table.
I used a query with "select count(*)", "select count(1)" and select count(<col_name>)" but keep getting the wrong result. When I use SQL Developer and run the query I get zero for the count. However, in the DAL, I get 1. I am guessing it is returning the number of row rather than the count itself. I tried both executeScalar() and ExecuteReader().
public override bool zipExists(string sZipCode)
{
OracleConnection conn = new OracleConnection(this.OraDataConnectionString);
OracleCommand oraCmd = new OracleCommand();
decimal iNumEntries = 0;
string sQuery = "select count(ZIPCODEID) as ZipCount from ZIPCODE where ZIPCODE = :ZipCode";
SetOraCommandType(oraCmd, CommandType.Text, sQuery);
conn.Open();
oraCmd.Connection = conn;
oraCmd.BindByName = true;
AddParamToOraCmd(oraCmd, "ZipCode", OracleDbType.Varchar2, 11, ParameterDirection.Input, sZipCode);
using (OracleConnection cn = new OracleConnection(this.OraDataConnectionString))
{
oraCmd.Connection = cn;
cn.Open();
iNumEntries = (decimal)oraCmd.ExecuteScalar();
}
return iNumEntries > 0;
also tried:
OracleDataReader sqlReader = oraCmd.ExecuteReader();
try
{
if (sqlReader.Read())
{
if (sqlReader["ZipCount"] != DBNull.Value)
iNumEntries = Convert.ToInt16(sqlReader["ZipCount"]);
}
}
}
return iNumEntries > 0;
I try you code on my table but pointing to some column and giving a select count(EN_Qty) as ZipCount from PSLAT.FSDEV.dbo.PS_EN_GEN_INTFC_BI where EN_Qty = '2600' works on my end so where in the code exactly are you experiencing an issue..? fyi I replaced my table with your query and assigned a value to the where clause.. so you query looks right.. however I would do an order by query to see if you perhaps are missing a zipcode..also oraCmd.ExecuteScalar() returns only 1 row so make sure you are not returning more than one row..oracle is funny like that
change your code to the following
object bExists = oraCmd.ExecuteScalar();
var bexists = bExists != DBNull.Value && result != null;
or change your code to check for row.count > 0 if true then you know the zipcode was found. Remember when making changes to Sql scripts or stored procedures in Oracle, you need to Compile the changes otherwise the changes will be visible to you when looking at the code but not to the caller trying to execute the stored proc.
I have this procedure in SQL Server and I tried it in SQL Server Management Studio, it works good with my database but in c# it does not work:
Create Procedure BorrowIsCorrect
#ProductName nvarchar(100),
#PersonId nvarchar(50),
#HBD bit,
#count int output
as
Begin
select #count = count(*)
from BorrowTable
where Person_Identity = #PersonId
and Product_Name = #ProductName
and H_B_D = #HBD
return #count
end
This is my code in c#:
internal bool BorrowIsCorrect(Borrow borrow)//checks if some person has some specific product and has not Delivered it yet
{
using (SqlConnection conn2 = new SqlConnection(_ConnectionString))
{
using (SqlCommand cmd = new SqlCommand())
{
conn2.Open();
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "BorrowIsCorrect";
cmd.Connection = conn2;
SqlParameter Output = new SqlParameter("#count", SqlDbType.Int);
Output.Direction = ParameterDirection.Output;
cmd.Parameters.Add(Output);
cmd.Parameters.Add("#ProductName", SqlDbType.NVarChar).Value = borrow.Product.Productname;
cmd.Parameters.Add("#PersonId", SqlDbType.NVarChar).Value = borrow.Person.Id;
cmd.Parameters.Add("#HBD", SqlDbType.Bit).Value = borrow.Has_been_received;
//int result = (int)(cmd.ExecuteScalar());
return ((int)(cmd.ExecuteScalar()) == 1) ? true : false;
}
}
}
But this code in C# throws an exception
Object reference was not set to an instance of an object
How can I fix it ?
Thanks
When I debug it, all is fine till this line:
return ((int)(cmd.ExecuteScalar()) == 1) ? true : false;
or I tried this line of code instead of the above one:
object result = (int)(cmd.ExecuteScalar());
The result variable is set to no value because exception occurs just when executing cmd.ExecuteScalar()
Edited:
I figure out that the result value is calculated correctly but not returned into an object as you see here:
Here after executing cmd.ExecuteScalar() in cmd.parameters we see four variables (correct) now if we expand count (the result and returned value form proc) as you see below its value is 1 (then result is correct) but the object that cmd.ExecuteScalar() returns is null.
finally i found a tricky way for solving my problem(i think it is not a standard way):
cmd.ExecuteScalar();
int result=cmd.Parameters[0].Value;
I am trying to check if a user is in my database, my return value from my function is either a 't' or an 'f'. as below:
CREATE OR REPLACE FUNCTION LOGIN
(p_Naam in varchar2
,p_Wachtwoord in varchar2)
return varchar2
is
v_count number;
BEGIN
select count(*) into v_count
from Lid
where Naam = p_Naam
and Wachtwoord = p_Wachtwoord;
if v_count > 0 then
return 't';
end if;
return 'f';
END LOGIN;
now i call this function with my C# code as below:
public bool LogIn(string gebruikersnaam, string wachtwoord)
{
string s;
var cmd = new OracleCommand
{
Connection = conn,
CommandText = "Login",
CommandType = CommandType.StoredProcedure
};
cmd.Parameters.Add("p_Naam", OracleDbType.Varchar2).Value = gebruikersnaam;
cmd.Parameters.Add("p_Wachtwoord", OracleDbType.Varchar2).Value = wachtwoord;
cmd.Parameters.Add("return_value", OracleDbType.Varchar2, ParameterDirection.ReturnValue);
try
{
conn.Open();
cmd.ExecuteNonQuery();
s = cmd.Parameters["return_value"].Value.ToString();
}
finally
{
conn.Close();
}
return s == "t";
}
when i try this funcion within my oracle developer i get an output.
only in my C# code, s always comes out as ""
in my sql developer the following gives me 't'
BEGIN
dbms_output.put_line(LOGIN('Willem Koonings', 'willem'));
END;
I haven't been working with OracleCommands for a while, but i would suggest this change:
CommandText = "Login(:p_Naam, :p_Wachtwoord)";
The long solution which I bets works (change type to text though):
CommandText = "select Login(:p_Naam, :p_Wachtwoord) return_value from dual";
When you have this in your code:
s = cmd.Parameters["return_value"].Value.ToString();
It implies, to me, that your command had somethink akin to this:
insert into test (user_name, create_date)
values (:USERNAME, sysdate)
returning user_id into :return_value
I've never seen a stored procedure use a "returning" as such. That doesn't automatically mean it can't be done, but it doesn't seem consistent with how I've used it and seen it used.
Since you are running a stored proc that returns a scalar value, this would probably suit you better:
string result = (string)cmd.ExecuteScalar();
You need to place the "return value" parameter first in the index, as far as i can tell from working with Oracle it doesn't pass the names of the parameters to the Oracle call, it is absolute placing.
Hope this solves your problems :)
Dom
ParameterDirection.ReturnValue could return only a numeric value, not a string or single char.
(At least in Sql Server). Try to change your function to return an integer and your parameters collection to receive an integer. (I don't have Oracle to test, please correct me if there are some syntax errors)
See this reference (for Sql Server)
CREATE OR REPLACE FUNCTION LOGIN
(p_Naam in varchar2
,p_Wachtwoord in varchar2)
return INTEGER
is
v_count number;
BEGIN
select count(*) into v_count
from Lid
where Naam = p_Naam
and Wachtwoord = p_Wachtwoord;
if v_count > 0 then
return 1;
end if;
return 0;
END LOGIN;
....
cmd.Parameters.Add("p_Naam", OracleDbType.Varchar2).Value = gebruikersnaam;
cmd.Parameters.Add("p_Wachtwoord", OracleDbType.Varchar2).Value = wachtwoord;
cmd.Parameters.Add("return_value", OracleDbType.Int32, ParameterDirection.ReturnValue);
....
int result = Convert.ToInt32(cmd.Parameters["return_value"].Value);
if(result == 0)
// fail
else
// success...
Instead of 'Return' statement why don't you declare a out parameter .Let me know if iam not clear
Just found out this and tested with VS2017 Community edition + 11g.
OracleCommand chkCmd = null;
try
{
chkCmd = new OracleCommand();
chkCmd.CommandText = "login";
chkCmd.CommandType = CommandType.StoredProcedure;
chkCmd.Connection = conn;
OracleParameter mobParam1 = new OracleParameter("p_Naam", OracleDbType.Varchar2, 2000);
mobParam1.Direction = ParameterDirection.Input;
mobParam1.Value = gebruikersnaam;
OracleParameter mobParam2 = new OracleParameter("p_Wachtwoord", OracleDbType.Varchar2, 2000);
mobParam2.Direction = ParameterDirection.Input;
mobParam2.Value = wachtwoord;
OracleParameter retValue = new OracleParameter("returnVal", OracleDbType.Varchar2, 2000);
retValue.Direction = ParameterDirection.ReturnValue;
chkCmd.Parameters.Clear();
chkCmd.Parameters.Add(retValue);
chkCmd.Parameters.Add(mobParam1);
chkCmd.Parameters.Add(mobParam2);
con.Open();
chkCmd.ExecuteScalar();
string retmsg = Convert.ToString(retValue.Value);
return retmsg=="t";
}
finally
{
con.Close();
chkCmd.Dispose();
con.Dispose();
}
Make Return_Value your first parameter:
cmd.Parameters.Add("Return_Value", OracleDbType.Boolean, ParameterDirection.ReturnValue);