I have the following parameter for SqlCommand. How do I make it to both in and out the paramter value for the Stored Procedure.
SqlCommand mySqlCommand = new SqlCommand("aspInsertZipCode", mySqlConnection);
mySqlCommand.CommandType = CommandType.StoredProcedure;
mySqlCommand.Parameters.Add("#DataRows", dataStringToProcess.ToString());
var pInOut = mySqlCommand.Parameters.Add("#DataRows", dataStringToProcess.ToString());
pInOut.Direction = ParameterDirection.InputOutput;
And then to read the output value after you've executed the command:
// assumes that the parameter is a string and that it could possibly be null
string value = Convert.IsDBNull(pInOut.Value) ? null : (string)pInOut.Value;
SqlParameter has a Direction enumeration. Set this value.
Then use the SqlCommand.Parameters.Add that takes a SqlParameter.
Parameter direction:
http://msdn.microsoft.com/en-us/library/system.data.parameterdirection.aspx
You then pull the value out after having called ExecuteNonQuery (for example), by getting the Value from the parameter out of the command collection:
myCommand.Parameters["#paramName"].Value
Can't remember, but I think there is a string indexer on that.
Alternatively, there is this one liner:
myCommand.Parameters.AddWithValue("#paramName", value).Direction = ParameterDirection.InputOutput;
One of the attributes of a SQL Command Parameter is the Direction. You would want to use (going off of memory)
SqlCommand mySqlCommand = new SqlCommand("aspInsertZipCode", mySqlConnection);
mySqlCommand.CommandType = CommandType.StoredProcedure;
mySqlCommand.Parameters.Add("#DataRows", dataStringToProcess.ToString());
mySqlCommand.Parameters("#DataRows").Direction = ParameterDirection.InputOutput;
SqlParameter DataRows = new SqlParameter("#DataRows", SqlDbType.Text)
{ Value = dataStringToProcess.ToString(), Direction = ParameterDirection.InputOutput};
mySqlCommand.Parameters.Add(DataRows);
Related
Im trying to do a COUNT using C# 'Prepared Statements' but I get error: :
SqlCommand.Prepare method requires all variable length parameters to
have an explicitly set non-zero Size
Below is my code:
public int PreparedCheck(String email)
{
SqlCommand cmd = new SqlCommand();
cmd = new SqlCommand("SELECT COUNT(*) FROM [dbo].[AspNetUsers] WHERE Email=#val1", conn);
cmd.Parameters.Add("#val1", SqlDbType.VarChar);
cmd.Prepare();
Int32 count = (Int32) cmd.ExecuteScalar();
return count;
}
Looks like you didn't specify your parameter value.
cmd.Parameters.Add("#val1", SqlDbType.VarChar).Value = email;
I write email as a value because you pass it as a parameter to your PreparedCheck method but you never use it.
Two errors. You don't set the value for the parameter, but you also don't set the size of the parameter as required by the Prepare method
SqlParameter p1 = cmd.Parameters.Add("#val1", SqlDbType.VarChar);
p1.Value = email;
p1.Size = 255; // This should be the size of the column EMail on the datatable
cmd.Prepare();
Simply setting the value of the parameter seems not to be enough for the Prepare method.
While the property size is changed to the string length passed as value, the error is still there and doesn't go away until you explicitly set the Size property to valid value.
This is from the REMARKS section on SqlCommand.Prepare
Before you call Prepare, specify the data type of each parameter in
the statement to be prepared. For each parameter that has a variable
length data type, you must set the Size property to the maximum size
needed. Prepare returns an error if these conditions are not met.
You need to set the value for your parameter:
cmd.Parameters.Add("#val1", SqlDbType.VarChar).Value = email;
I'm using output parameters to return values from a stored procedure.
Declaration in stored procedure is : #GrandTtl DECIMAL(19,3) OUT
The SELECT query is:
SET #GrandTtl = (SELECT TOP 1 Bill_Amount
FROM Tbl_Restaurant_Kitchen_Order_Bill_Details
WHERE Bill_Number = #billno)
For example, the select query returns the value 4087.67 then the output parameter value is returned as 4088 from SQL Server to C#.
Here is the C# code calling the stored procedure:
SqlCommand cmd = new SqlCommand("Sp_RestCC_BillDetails", con);
cmd.CommandType = CommandType.StoredProcedure;
SqlParameter OutParam26 = cmd.Parameters.Add("#GrandTtl", SqlDbType.Decimal,19);
da = new SqlDataAdapter(cmd);
con.Open();
da.Fill(ds, "dtRestCC_Items");
con.Close();
objRCCBEL.GrandTtlOut = Convert.ToDecimal(cmd.Parameters["#GrandTtl"].Value);
You need to set up the C# parameter as
output -- obviously you've done this
decimal type, with a correct/compatible scale
SqlParameter parm = new SqlParameter("#GrandTtl", SqlDbType.Decimal);
parm.Precision = 19;
parm.Scale = 3;
parm.Direction = ParameterDirection.Output;
cmd.Parameters.Add(parm);
If you do not set the scale, the default is 0. Ref: SqlParameter.Scale Property
The number of decimal places to which Value is resolved. The default is 0.
According to Microsoft decimal(p,s) should work for you. The money and smallmoneyt types are just a subset of decimal with precision of 4 places. So i think your problem comes from the type of the variable that is bound to the OUT parameter in C#.
In my testing project, I have a static class called FixtureSetup which I use to setup my integration testing data for validation.
I use the same SqlCommand and SqlParameter variable (not the object itself) within that class, repeatedly, using the same variable references over and over, assigning new SqlCommand and SqlParameter objects each time. My connection itself is created once and passed into the methods performing the setup, so each setup uses it's own distinct connection reference, and while the same conn is used multiple times, it's always in a linear sequence.
In one such method, I ran into a very odd situation, where my SqlCommand variable simply appears to have gotten tired.
cmd = new SqlCommand("INSERT INTO Subscription (User_ID, Name, Active) VALUES (#User_ID, #Name, #Active)", conn);
parameter = new SqlParameter("#User_ID", TestUserID); cmd.Parameters.Add(parameter);
parameter = new SqlParameter("#Name", "TestSubscription"); cmd.Parameters.Add(parameter);
parameter = new SqlParameter("#Active", true); cmd.Parameters.Add(parameter);
cmd.ExecuteNonQuery();
cmd = new SqlCommand("SELECT Subscription_ID FROM [Subscription] WHERE Name = 'TestSubscription'", conn);
parameter = new SqlParameter("#User_ID", TestUserID);
cmd.Parameters.Add(parameter);
using (dr = cmd.ExecuteReader())
{
while (dr.Read())
{
TestSubscriptionID = dr.GetInt32(dr.GetOrdinal("Subscription_ID"));
}
}
cmd = new SqlCommand("INSERT INTO SubscriptionCompany (Subscription_ID, Company_ID) VALUES (#Subscription_ID, #Company_ID)", conn);
parameter = new SqlParameter("#Subscription_ID", TestSubscriptionID); cmd.Parameters.Add(parameter);
parameter = new SqlParameter("#Company_ID", KnownCompanyId); cmd.Parameters.Add(parameter);
cmd.ExecuteNonQuery();
In the above, at the last line shown, doing the same thing I've done quite literally in dozens of other places (insert data, read the ID column and capture it), I get the following:
SetUp : System.InvalidOperationException : ExecuteNonQuery requires an
open and available Connection. The connection's current state is
closed. at
System.Data.SqlClient.SqlConnection.GetOpenConnection(String method)
BUT - replace cmd with new variable myCmd, and everything works swimmingly!
SqlCommand myCmd;
myCmd = new SqlCommand("INSERT INTO Subscription (User_ID, Name, Active) VALUES (#User_ID, #Name, #Active)", conn);
parameter = new SqlParameter("#User_ID", TestUserID); myCmd.Parameters.Add(parameter);
parameter = new SqlParameter("#Name", "TestSubscription"); myCmd.Parameters.Add(parameter);
parameter = new SqlParameter("#Active", true); myCmd.Parameters.Add(parameter);
myCmd.ExecuteNonQuery();
myCmd = new SqlCommand("SELECT Subscription_ID FROM [Subscription] WHERE Name = 'TestSubscription'", conn);
parameter = new SqlParameter("#User_ID", TestUserID);
myCmd.Parameters.Add(parameter);
using (dr = myCmd.ExecuteReader())
{
while (dr.Read())
{
TestSubscriptionID = dr.GetInt32(dr.GetOrdinal("Subscription_ID"));
}
}
myCmd = new SqlCommand("INSERT INTO SubscriptionCompany (Subscription_ID, Company_ID) VALUES (#Subscription_ID, #Company_ID)", conn);
parameter = new SqlParameter("#Subscription_ID", TestSubscriptionID); myCmd.Parameters.Add(parameter);
parameter = new SqlParameter("#Company_ID", KnownCompanyId); myCmd.Parameters.Add(parameter);
myCmd.ExecuteNonQuery();
What the heck is going on here? Did my command var just get tired???
What clued me to the "fix" was I noticed in my tracing that in my "read the id" block, my cmd.Parameters block had only ONE parameter in it, the 2nd one added, and when I forced the first cmd.Parameters.Add line to execute again, the number of parameters in the list dropped to 0. That's what prompted me to try a method level SqlCommand...cause I had the crazy idea that my cmd was tired... Imagine my shock when I apparently turned out to be right!
Edit: I'm not recycling any objects here - just the variable reference itself (static SqlCommand at the class level). My apologies for the earlier confusion in my wording of the question.
use one command per query and call dispose (or better yet, wrap in a using statement). you don't want to be "reusing" ado.net components.
Big Edit:
From: http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqldatareader.close.aspx
You must explicitly call the Close method when you are through using
the SqlDataReader to use the associated SqlConnection for any other
purpose.
The Close method fills in the values for output parameters, return
values and RecordsAffected, increasing the time that it takes to close
a SqlDataReader that was used to process a large or complex query.
When the return values and the number of records affected by a query
are not significant, the time that it takes to close the SqlDataReader
can be reduced by calling the Cancel method of the associated
SqlCommand object before calling the Close method.
so try:
using (dr = cmd.ExecuteReader())
{
while (dr.Read())
{
TestSubscriptionID = dr.GetInt32(dr.GetOrdinal("Subscription_ID"));
}
dr.Close();
}
Check that you haven't set the DataReader to CommandBehavior.CloseConnection since you mentioned that you're re-using the connection for your test initialization.
Also, the DataReader does take resources, so utilize Dispose
Do you really need to do make a new connection object after each try?
myCmd = new SqlCommand(...)
//your code
myCmd = new SqlCommand(...)
//etc
You can just say:
myCmd.CommandText = "INSERT INTO SubscriptionCompany (Subscription_ID, Company_ID) VALUES (#Subscription_ID, #Company_ID)";
so that you may re-use your command object. Additionally you can reset your parameters as well after each call. Just call myCmd.Parameters.Clear().
Also, make sure you wrap your SqlCommand in a using statement so they will be properly cleaned up.
using (SqlCommand cmd = new SqlCommand())
{
cmd.CommandText = "some proc"
cmd.Connection = conn;
//set params, get data, etc
cmd.CommandText = "another proc"
cmd.Parameters.Clear();
//set params, get date, etc.
}
I have a stored procedure that goes like this:
ALTER PROCEDURE [dbo].[AuthenticateUser]
#AzUserName varchar(20),
#Hash varchar(32),
#UserId bigint output,
#Authorized bit output
...
and runs just fine fine in Management Studio.
Here's my C# code:
SqlConnection scon = new SqlConnection(connectionString);
SqlCommand authCmd = new SqlCommand("AuthenticateUser", scon);
authCmd.CommandType = System.Data.CommandType.StoredProcedure;
SqlParameter userNameParam = authCmd.Parameters.Add("#AzUserName", System.Data.SqlDbType.VarChar, 20);
userNameParam.Value = username;
string hashed = Md5Hash.ComputeHash(username);
SqlParameter hashedParam = authCmd.Parameters.Add("#Hash", System.Data.SqlDbType.VarChar, 32);
hashedParam.Value = hashed;
SqlParameter userIdParam = authCmd.Parameters.Add("#UserId", System.Data.SqlDbType.Int);
userIdParam.Direction = System.Data.ParameterDirection.ReturnValue;
SqlParameter authorizedParam = authCmd.Parameters.Add("#Authorized", System.Data.SqlDbType.Bit);
authorizedParam.Direction = System.Data.ParameterDirection.ReturnValue;
scon.Open();
authCmd.ExecuteNonQuery();
scon.Close();
When I run it I am getting the following error:
{"Procedure or function 'AuthenticateUser' expects parameter '#UserId', which was not supplied."} System.Exception {System.Data.SqlClient.SqlException}
When I replace ParameterDirection.ReturnValue with ParameterDirection.Output I am not getting the error but never get the value of the procedure.
UPDATE:
Thank you All for your help. The error was more trivial than you would have thought and I described in the question. I have been changing back and forth ReturnValue to Output for quite a while today with no result. Then I had to post my question on SO just to realize that I am taking the hash value of ... username..Going outdoor to get some oxygen now.
You will have to use ParameterDirection.Output on every parameter, that has been marked with output in T-SQL. You can access the values, after the call to
authCmd.ExecuteNonQuery();
by getting the values of the parametes like this:
authCmd.Parametes["#UserId"].Value
You're confusing the concepts of OUTPUT and RETURN values.
A RETURN value from a stored procedure is a single integer value per stored procedure that is defined within your proc by using the RETURN statement eg
RETURN 1
A stored procedure can have zero to many parameters of which zero to many can be defined as OUTPUT.
In your case you're not showing any use of the RETURN statement but you are using OUTPUT parameters. In SQL Server these are more like input/output parameters and you need to provide a value.
You can access the resulting value of an OUTPUT parameter by looking at the parameters collection after calling the stored procedure and look at the value eg
authCmd.Parameters[2].Value
Or
userIdParam.Value
As per other answers, you need to use the output parameter direction to achieve this
You can access the values of authorizedParam.Value and userIdParam.Value after executing the command.
SqlConnection scon = new SqlConnection(connectionString);
SqlCommand authCmd = new SqlCommand("AuthenticateUser", scon);
authCmd.CommandType = System.Data.CommandType.StoredProcedure;
SqlParameter userNameParam = authCmd.Parameters.Add("#AzUserName", System.Data.SqlDbType.VarChar, 20);
userNameParam.Value = username;
string hashed = Zonal.Pie.Core.Common.Utils.Md5Hash.ComputeHash(username);
SqlParameter hashedParam = authCmd.Parameters.Add("#Hash", System.Data.SqlDbType.VarChar, 32);
hashedParam.Value = hashed;
SqlParameter userIdParam = authCmd.Parameters.Add("#UserId", System.Data.SqlDbType.Int);
userIdParam.Direction = System.Data.ParameterDirection.Output;
SqlParameter authorizedParam = authCmd.Parameters.Add("#Authorized", System.Data.SqlDbType.Bit);
authorizedParam.Direction = System.Data.ParameterDirection.Output;
scon.Open();
authCmd.ExecuteNonQuery();
//Access authorizedParam.Value and userIdParam.Value here
scon.Close();
I am trying to use Npgsql to invoke a function (stored procedure) that takes a CHARACTER as parameter, but it doesn't work. If I declare the same function without parameters, or with an INTEGER parameter, I get the result sets that I want. When I declare the parameter as CHARACTER, it stops working. What is wrong?
Here is the code of my function:
CREATE OR REPLACE FUNCTION testrefcursor1(in xxx character varying(10)) RETURNS SETOF refcursor AS
$$
DECLARE
ref1 refcursor;
ref2 refcursor;
BEGIN
OPEN ref1 FOR
SELECT * FROM accounts;
RETURN NEXT ref1;
OPEN ref2 FOR
SELECT * FROM accounts;
RETURN NEXT ref2;
RETURN;
END;
$$
LANGUAGE plpgsql;
And here is the C# code that I am using:
var connection = new Npgsql.NpgsqlConnection(connectionString.ConnectionString);
connection.Open();
var trans = connection.BeginTransaction();
var command = new Npgsql.NpgsqlCommand("testrefcursor1", connection);
command.CommandType = System.Data.CommandType.StoredProcedure;
var parameter = command.CreateParameter();
parameter.ParameterName = "xxx";
parameter.DbType = System.Data.DbType.String;
parameter.Value = "10";
command.Parameters.Add(parameter);
var da = new Npgsql.NpgsqlDataAdapter(command);
var ds = new System.Data.DataSet();
da.Fill(ds);
trans.Commit();
connection.Close();
I already tried declaring the parameter as CHARACTER, CHARACTER(10), CHARACTER VARYING and CHARACTER VARYING(10)...
EDIT: I don't get any error message, but instead of getting the expected result set, I get an empty result set with a single column that has the same name as the function I am trying to call.
You're passing a Unicode argument to an ASCII parameter.
Change this line:
parameter.DbType = System.Data.DbType.String;
To:
parameter.DbType = System.Data.DbType.AnsiString;
Generally, Postgres's varchar columns are in Unicode, provided that the Unicode option on the database is enabled (see here). My guess is that it's not, and your parameter is unable to convert itself into the correct type to be passed through the function.
Which Npgsql version are you using?
Also, can you specify the parameter type using NpgsqlDbType? Sometimes the mapping isn't exactly and Npgsql can't find the function you are trying to use and can't make it work.
Npgsql tries to find an exact match of function name and parameter types. DbString matches text parameter types. Would you mind to give it a try and change your parameter type to text?
I hope it helps.
Not sure, if this has to do with your problem, but yesterday I stumbled across the fact that PostgreSQL has a "single-byte internal type" char that is different from the type char(1). Maybe there is some confusion about these?
I tried with the below approach (which is similar to yours):
using (NpgsqlConnection connection = new NpgsqlConnection(< connectionString >))
{
DataSet ds = new DataSet();
DataTable dt = new DataTable();
connection.Open();
NpgsqlCommand cmd = new NpgsqlCommand(< name of your SP >, connection);
cmd.CommandType = CommandType.StoredProcedure;
var param = cmd.CreateParameter();
param.ParameterName = < exact parameterName you used in your SP>;
param.DbType = System.Data.DbType.AnsiString;
param.Value = < parameter value >;
cmd.Parameters.Add(param);
NpgsqlDataAdapter adapter = new NpgsqlDataAdapter(cmd);
adapter.Fill(ds);
dt = ds.Tables[0];
}
This is working fine for me and I'm getting proper DataTable.