Executing query with parameters - c#

I want to execute a .sql script from C#. Basically the script inserts a row into few different tables.
The point is I have values in C# code that I need to pass to the .sql query. These values will be collected during program execution.
Here is the query that I want to execute from C# code:
INSERT INTO [DB].[dbo].[User]
           ([Id]
           ,[AccountId]
           ,[FirstName]
           ,[LastName]
           ,[JobTitle]
           ,[PhoneNumber]
          )
     VALUES
           ('00A640BD-1A0D-499D-9155-BA2B626D7B68'
           ,'DCBA241B-2B06-48D7-9AC1-6E277FBB1C2A'
           ,'Mark'
           ,'Wahlberg'
           ,'Actor'
,'9889898989'])
GO
The values will vary from time to time i.e., they are captured in C# code and need to be passed.
Can anyone please help me do this..I am learning both C# and SQL. Thanks a lot.

You could open yourself up to SQL injection attacks here, so best practice is to use parameters:
using (SqlConnection dbConn = new SqlConnection(connectionString))
{
dbConn.Open();
using (SqlTransaction dbTrans = dbConn.BeginTransaction())
{
try
{
using (SqlCommand dbCommand = new SqlCommand("insert into [DB].[dbo].[User] ( [Id], [AccountId], [FirstName], [LastName], [JobTitle], [PhoneNumber] ) values ( #id, #accountid, #firstname, #lastname, #jobtitle, #phonenumber );", dbConn))
{
dbCommand.Transaction = dbTrans;
dbCommand.Parameters.Add("id", SqlType.VarChar).Value = id;
dbCommand.Parameters.Add("accountid", SqlType.VarChar).Value = accountId;
dbCommand.Parameters.Add("firstname", SqlType.VarChar).Value = firstName;
dbCommand.Parameters.Add("lastname", SqlType.VarChar).Value = lastName;
dbCommand.Parameters.Add("jobtitle", SqlType.VarChar).Value = jobTitle;
dbCommand.Parameters.Add("phonenumber", SqlType.VarChar).Value = phoneNumber;
dbCommand.ExecuteNonQuery();
}
dbTrans.Commit();
}
catch (SqlException)
{
dbTrans.Rollback();
throw; // bubble up the exception and preserve the stack trace
}
}
dbConn.Close();
}
This is a good article for beginners with ADO.Net
EDIT - Just as a bit of extra info, I've added a transaction to it so if the SQL command fails it will rollback.

using SqlCommand cmd= conn.CreateCommand())
{
cmd.CommandText = #"INSERT INTO TABLE (COLUMNS) VALUES (#Id, #account etc...
cmdUser.Parameters.Add(new SqlParameter("#User", SqlDbType.UniqueIdentifier) { Value = UserTypeID });
cmdUser.Parameters.Add(new SqlParameter("#Id", SqlDbType.UniqueIdentifier) { Value = ApprovalTypeID });
cmdUser.Parameters.Add(new SqlParameter("#AccountId", SqlDbType.UniqueIdentifier) { Value = UserID });
cmdUser.Parameters.Add(new SqlParameter("#FirstName", SqlDbType.NVarChar, 100) { Value = Name });
cmdUser.Parameters.Add(new SqlParameter("#JobTitle", SqlDbType.NVarChar, 100) { Value = Surname });
cmdUser.Parameters.Add(new SqlParameter("#PhoneNumber", SqlDbType.Bit) { Value = Active });
cmdUser.ExecuteNonQuery();
}

try
{
using (SqlConnection cn = new SqlConnection(this.ConnectionString))
{
SqlCommand cmd = new SqlCommand("Insert_User", cn);
cmd.CommandType = CommandType.StoredProcedure;
if (cn.State != ConnectionState.Open)
cn.Open();
cmd.Parameters.Add("Id", SqlDbType.NVarChar).Value = "00A640BD-1A0D-499D-9155-BA2B626D7B68";
cmd.Parameters.Add("AccountId", SqlDbType.NVarChar).Value = "DCBA241B-2B06-48D7-9AC1-6E277FBB1C2A";
cmd.Parameters.Add("FirstName", SqlDbType.NVarChar).Value = "Mark";
cmd.Parameters.Add("LastName", SqlDbType.NVarChar).Value = "Wahlberg";
cmd.Parameters.Add("JobTitle", SqlDbType.NVarChar).Value = "Actor";
cmd.Parameters.Add("PhoneNumber", SqlDbType.NVarChar).Value = "9889898989";
return cmd.ExecuteNonQuery();
}
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
and for stored procedure, in sql:
create procedure [Insert_User]
(
#id as nvarchar(100),
#accid as nvarchar(100),
#fname as nvarchar(100),
#lname as nvarchar(100),
#jobtitle as nvarchar(100),
#phone as nvarchar(100)
)
INSERT INTO [DB].[dbo].[User]
([Id]
,[AccountId]
,[FirstName]
,[LastName]
,[JobTitle]
,[PhoneNumber]
)
VALUES
(#id
,#accid
,#fname
,#lname
,#jobtitle
,#phone])
also, you can use text boxes or other input type controls to set values. You can change dataType, as you wish, such as uniqueidentifier, int, etc. If one or more of values are set as identifire, eg. AccountID, remove them from query.

Frankly, ADO.NET makes it hard to do things like this correctly. Tools like Dapper exist to make that easier:
dbConn.Execute(
#"insert into [DB].[dbo].[User] ( [Id], [AccountId], [FirstName], [LastName],
[JobTitle], [PhoneNumber] )
values ( #id, #accountId, #firstName, #lastName, #jobTitle, #phoneNumber )",
new { id, accountId, firstName, lastName, jobTitle, phoneNumber });
This will deal with all the parameterization for you, efficiently, effectively, and safely.
There are similar APIs for executing queries and populating objects.

The above answers definitely good solutions for the question. However, I would like to recommend more dynamic and robust solution.
Here is your controller (in my example Ajax call)
public JsonResult RecordThis(FormCollection fc)
{
SqlRecord.RecordThis(fc);
return View();
}
Then, on your Model (in example SqlRecord) create "Record" function;
public static void Record(FormCollection fc)
{
var sql = "INSERT INTO RecordTable VALUES ('#Email','#Name','GETDATE()')";
var parameters = BuildSqlParams(fc);
ExecuteInlineSqlWithParams(sql, yourconnectionstringhere, parameters);
}
Then, add these two functions below to be used for any function like "Record". You can add conditions as well such as excluding tokens.
public static List<SqlParameter> BuildSqlParams(FormCollection fc)
{
var parameters = new List<SqlParameter>();
foreach(var key in fc.AllKeys)
{
if(key != "__RequestVerificationToken")
parameters.Add(new SqlParameter("#"+key+"", fc[key]));
}
return parameters;
}
public static void ExecuteInlineSqlWithParams(string sql, string cnn, List<SqlParameter> parameters )
{
var connection = new SqlConnection(cnn);
var command = new SqlCommand(sql, connection);
command.CommandType = CommandType.Text;
foreach(var param in parameters)
{
command.Parameters.Add(param);
}
using (connection)
{
connection.Open();
command.ExecuteNonQuery();
connection.Close();
}
}

You'll need the System.Data.SqlCommand class.
Change the fixed values to named parameters. For example:
INSERT INTO [TABLE] (Column1) values (#Value1) // the #Value1 is the named parameter
Example:
var connection = new SqlConnection("connectionstring");
var command = connection.CreateCommand();
command.CommandText = "insert...."; // sql command with named parameters
// set the named parameter values
command.Parameters["#Value1"] = "Mark wa...";
// execute
command.ExecuteNonQuery();
SqlCommand reference:
http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.aspx

Related

Convert Command.ExecuteScalar() To Int

I need to insert a line in my question table and retrieve the inserted id. I initialize my sql command and execute it with ExecuteScalar(). I'm trying to convert the result of this method to int but I can not do it.
I tried to do that:
int result = Convert.ToInt32(Command.ExecuteScalar));
or
int result = (int)Command.ExecuteScalar();
but nothing work
here is my function
public int AddQuestionOrientation(Question questionForAdd)
{
try
{
con = new SqlConnection(connectionString);
con.Open();
SqlCommand command;
String sql = "";
sql = "INSERT INTO QUESTION VALUES(#Libelle,
#Bareme,2,#Filliere)";
SqlParameter param = new SqlParameter();
param.ParameterName = "#Libelle";
param.Value = questionForAdd.Libelle;
SqlParameter param2 = new SqlParameter();
param2.ParameterName = "#Bareme";
param2.Value = questionForAdd.Bareme;
SqlParameter param3 = new SqlParameter();
param3.ParameterName = "#Filliere";
param3.Value = questionForAdd.IdFiliere;
command = new SqlCommand(sql, con);
command.Parameters.Add(param);
command.Parameters.Add(param2);
command.Parameters.Add(param3);
int idQuestionInserted = (int)command.ExecuteScalar();
command.Dispose();
con.Close();
return idQuestionInserted;
}
catch(Exception ex)
{
return 0;
}
}
If I try with the cast (int), I have the message error:
Object reference not set to an instance of an object
If I try with the Convert.ToInt32, my variable "IdQuestionInserted" is equal to 0.
This is a big departure from where you started. But you have several issue going on there. You should use the USING statement around objects with the IDisposable interface (connections, commands, etc...).
This code is all untested but should be really close.
Start with creating a stored procedure so you can start creating layers in your application.
create Procedure Question_Insert
(
#Libelle varchar(50)
, #Bareme varchar(50)
, #Filliere varchar(50)
, #QuestionID int output
) as
set nocount on;
INSERT INTO QUESTION
(
Libelle
, Bareme
, Filliere
)
values
(
#Libelle
, #Bareme
, #Filliere
)
select #QuestionID = SCOPE_IDENTITY()
Then in your dotnet code you need to change up a few things to make it cleaner and more robust. Ideally you should do something better than simply return 0 when there is an error. It will be really tough to debug when something goes wrong if you simply return a 0. This is like an error message that says, "An error occurred". Pretty useless. Do something with the error. Capture the message to enable you to fix it.
public int AddQuestionOrientation(Question questionForAdd)
{
try
{
using (SqlConnection con = new SqlConnection(connectionString))
{
con.Open();
using (SqlCommand command = new SqlCommand("Question_Insert"))
{
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add("#Libelle", SqlDbType.VarChar, 50).Value = questionForAdd.Libelle;
command.Parameters.Add("#Bareme", SqlDbType.VarChar, 50).Value = questionForAdd.Bareme;
command.Parameters.Add("#Filliere", SqlDbType.VarChar, 50).Value = questionForAdd.IdFiliere;
command.Parameters.Add("#QuestionID", SqlDbType.Int).Direction = ParameterDirection.Output;
command.ExecuteNonQuery();
return int.Parse(command.Parameters["#QuestionID"].Value.ToString());
}
}
}
catch (Exception ex)
{
return 0;
}
}
To get inserted id use SCOPE_IDENTITY() add SELECT CAST(scope_identity() AS int to command query to do
INSERT INTO QUESTION
VALUES(#Libelle, #Bareme, 2, #Filliere);
SELECT CAST(scope_identity() AS int;
this query will return inserted id for you.

How do I execute a SqlDataReader after sqlInsertCmd.ExecuteNonQuery

I am inserting a data row into my SQL Server database and then I want to query the data to get the unique identifier from the inserted row but my SqlDataReader is returning an empty dataset. I am thinking it maybe that the transaction hasn't been committed or something like that but I am not sure. I do not get an error.
Here is my code:
try
{
strQuery = "INSERT INTO clientnames VALUES(NEWID(),'" + txtACLastName.Text + "','" + txtACFirstName.Text + "'," + 1 + ")";
using (SqlCommand sqlInsertCmd = new SqlCommand(strQuery, sqlConn))
{
intQueryResult = sqlInsertCmd.ExecuteNonQuery();
if (intQueryResult == 0)
{
blnSuccess = false;
goto InsertClientNamesError;
}
else
{
blnSuccess = true;
}
sqlInsertCmd.Dispose();
}
if (blnSuccess)
{
strQuery = "select clientID from clientnames where firstname = '" + txtACFirstName.Text + "' and lastname = '" + txtACLastName.Text + "'";
using (SqlCommand sqlSelectCmd = new SqlCommand(strQuery, sqlConn))
{
SqlDataReader sqlDataRead = sqlSelectCmd.ExecuteReader();
while (sqlDataRead.Read())
{
strClientID = sqlDataRead.ToString();
}
sqlDataRead.Close();
sqlSelectCmd.Dispose();
}
}
}
catch (Exception exQuery)
{
System.Windows.MessageBox.Show("InsertClientNames: Error, " + exQuery.Message + ", has occurred.");
}
You are not getting the desired result because perhaps the SqlConnection is not opened explicitly (just a guess hard to tell without having full code). But this link shows you how to read from reader --> https://msdn.microsoft.com/en-us/library/haa3afyz(v=vs.110).aspx
But I suggest that you Please do not do it this way. Reason is you are making Two round trips to the DB Server when only one would have done the job for you IF you were using stored procedures. Also you are exposing yourselves to SQL Injection attacks as you are not parameterizing your queries.
Stored procedure:
CREATE PROCEDURE dbo.INS_clientnames
(
#FirstName varchar(100),
#LastName varchar(100),
#NewID int out
)
AS
BEGIN
Declare #Err int
set #NewID = NewID() -- Get the New ID and store it in the variable ( #NewID ) that the SP will return back to the caller
INSERT INTO clientnames values (#NewID , #FirstName , #LastName)
SET #Err = ##ERROR
IF #Error <> 0 -- Check If there was an error
Begin
SET #NewID = -1 -- Indicates that there was an error. You could log this into a Log Table with further details like error id and name.
END
RETURN
END
C# code to execute the above stored procedure and get the NewID:
using(SqlConnection conn = new SqlConnection(connectionString ))
{
using(SqlCommand cmd = new SqlCommand("dbo.INS_clientnames", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
// set up the parameters that the Stored Procedure expects
cmd.Parameters.Add("#FirstName", SqlDbType.VarChar, 100);
cmd.Parameters.Add("#LastName" , SqlDbType.VarChar, 100);
cmd.Parameters.Add("#NewId" , SqlDbType.Int).Direction = ParameterDirection.Output;
// set parameter values that your code will send to the SP as parameter values
cmd.Parameters["#FirstName"].Value = txtACFirstName.Text ;
cmd.Parameters["#LastName"].Value = txtACLastName.Text ;
// open connection and execute stored procedure
conn.Open();
cmd.ExecuteNonQuery();
// read output value from #NewId
int NewID = Convert.ToInt32(cmd.Parameters["#NewId"].Value);
}
}
Add the following line to your stored procedure that inserts the record
SELECT SCOPE_IDENTITY()
This will return the last identity value inserted in that table.
And use cmd.ExecuteScalar() instead of ExecuteNonQuery()
ExecuteScalar() executes the query, and returns the first column of the first row in the result set returned by the query. Additional columns or rows are ignored. [More info][1]
I see two approaches to do this:
either you generate the new GUID on the client side in your C# code and pass it into the query - then you already know what the new id is going to be, so you don't need to do a second query to get it:
you create your GUID on the server side and return it to the caller using the OUTPUT clause in your query
Approach #1:
// define connection string and query
string connStr = "--your connection string here--";
string query = "INSERT INTO dbo.Clients(ClientID, FirstName, LastName) VALUES(#ID, #First, #Last);";
using (SqlConnection conn = new SqlConnection(connStr))
using (SqlCommand cmd = new SqlCommand(query, conn))
{
// create the GUID in C# - this is the ID - no need to go get it again - this *IS* the id
Guid id = Guid.NewGuid();
// set the parameters
cmd.Parameters.Add("#ID", SqlDbType.UniqueIdentifier).Value = id;
cmd.Parameters.Add("#First", SqlDbType.VarChar, 50).Value = "Peter";
cmd.Parameters.Add("#Last", SqlDbType.VarChar, 50).Value = "Miller";
// open connection, execute query, close connection
conn.Open();
cmd.ExecuteNonQuery();
conn.Close();
}
Approach #2:
// define connection string and query
string connStr = "--your connection string here--";
// query has an "OUTPUT" clause to return a newly inserted piece of data
// back to the caller, just as if a SELECT had been issued
string query = "INSERT INTO dbo.Clients(ClientID, FirstName, LastName) OUTPUT Inserted.ClientID VALUES(NEWID(), #First, #Last);";
using (SqlConnection conn = new SqlConnection(connStr))
using (SqlCommand cmd = new SqlCommand(query, conn))
{
// set the parameters - note: you do *NOT* send in a GUID value - the NEWID() will create one automatically, on the server
cmd.Parameters.Add("#First", SqlDbType.VarChar, 50).Value = "Frank";
cmd.Parameters.Add("#Last", SqlDbType.VarChar, 50).Value = "Brown";
// open connection
conn.Open();
// execute query and get back one row, one column - the value in the "OUTPUT" clause
object output = cmd.ExecuteScalar();
Guid newId;
if (Guid.TryParse(output.ToString(), out newId))
{
//
}
conn.Close();
}

The stored procedure doesn't exist when calling SqlCommandBuilder.DeriveParameters

I'm tring to get the parameters from a stored procedure but I am getting the error of
The stored procedure 'Generic.proc_UpdateGenericCatalog' doesn't exist.
The namespace Generic. is causing the error but I do not know which setting I should use to get beyond this error.
--My Procedure--
ALTER proc [Generic].[proc_UpdateGenericCatalog]
#UserID UNIQUEIDENTIFIER,
#Name VARCHAR(30),
#SupplierName VARCHAR(30),
#SupplierEmail VARCHAR(50),
#SupplierPhone VARCHAR(12),
#GenericCatalogID INT
AS
UPDATE [Generic].[GenericCatalog]
SET [UserID] = #UserID
,[Name] = #Name
,[SupplierName] = #SupplierName
,[SupplierEmail] = #SupplierEmail
,[SupplierPhone] = #SupplierPhone
WHERE ID = #GenericCatalogID
--The C# code I'm using to get the parameter info--
--The error is right here SqlCommandBuilder.DeriveParameters(cmd);
How should I alter my code below so that I can get the .DeriveParameter info?
private static SqlParameter[] DiscoverParameters(string connectionString, string spName)
{
SqlCommand cmd = null;
SqlParameter[] discoveredParameters = null;
try
{
using (SqlConnection conn = new SqlConnection())
{
conn.ConnectionString = connectionString;
conn.Open();
cmd = new SqlCommand(spName, conn);
cmd.CommandType = CommandType.StoredProcedure;
SqlCommandBuilder.DeriveParameters(cmd);
conn.Close();
discoveredParameters = new SqlParameter[cmd.Parameters.Count];
cmd.Parameters.CopyTo(discoveredParameters, 0);
foreach (SqlParameter discoveredParameter in discoveredParameters)
{
discoveredParameter.Value = DBNull.Value;
}
cmd.Dispose();
}
}
catch (Exception) { throw; }
return discoveredParameters;
}
The most likely explanation is that the user specified in your connection string doesn't have permission to execute the Generic.proc_UpdateGenericCatalog procedure. I've just tested on one of my databases, and if the user doesn't have EXEC permission you'll get the "stored procedure doesn't exist" error.
Also, you should probably be cloning the parameters, rather than just copying them to an array. This is quite easy to do with a bit of LINQ:
private static SqlParameter[] DiscoverParameters(string connectionString, string spName)
{
using (var connection = new SqlConnection(connectionString))
using (var command = new SqlCommand(spName, connection))
{
command.CommandType = CommandType.StoredProcedure;
connection.Open();
SqlCommandBuilder.DeriveParameters(command);
connection.Close();
return command.Parameters
.Cast<ICloneable>()
.Select(p => p.Clone())
.Cast<SqlParameter>()
.ToArray();
}
}

How to pass value to a stored procedure using C#

--Stored procedure
ALTER PROCEDURE [dbo].[Test]
#USERID varchar(25)
AS
BEGIN
SET NOCOUNT ON
IF NOT EXISTS Select * from Users where USERID = #USERID)
BEGIN
INSERT INTO Users (USERID,HOURS) Values(#USERID, 0);
END
I have this stored procedure in sql server 2005 and want to pass userid from a C# application. How can I do that. Many Thanks.
This topic is extensively covered in MSDN here. See the section entitled "Using Parameters with a SqlCommand and a Stored Procedure" for a nice sample:
static void GetSalesByCategory(string connectionString,
string categoryName)
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
// Create the command and set its properties.
SqlCommand command = new SqlCommand();
command.Connection = connection;
command.CommandText = "SalesByCategory";
command.CommandType = CommandType.StoredProcedure;
// Add the input parameter and set its properties.
SqlParameter parameter = new SqlParameter();
parameter.ParameterName = "#CategoryName";
parameter.SqlDbType = SqlDbType.NVarChar;
parameter.Direction = ParameterDirection.Input;
parameter.Value = categoryName;
// Add the parameter to the Parameters collection.
command.Parameters.Add(parameter);
// Open the connection and execute the reader.
connection.Open();
SqlDataReader reader = command.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
Console.WriteLine("{0}: {1:C}", reader[0], reader[1]);
}
}
else
{
Console.WriteLine("No rows found.");
}
reader.Close();
}
}

Getting return value from stored procedure in C#

I have the following query:
set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
go
ALTER PROCEDURE [dbo].[Validate]
#a varchar(50),
#b varchar(50) output
AS
SET #Password =
(SELECT Password
FROM dbo.tblUser
WHERE Login = #a)
RETURN #b
GO
This compiles perfectly fine. I want to execute this query and get the return value. My code is below:
SqlConnection SqlConn = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["MyLocalSQLServer"].ConnectionString.ToString());
System.Data.SqlClient.SqlCommand sqlcomm = new System.Data.SqlClient.SqlCommand("Validate", SqlConn);
string returnValue = string.Empty;
try
{
SqlConn.Open();
sqlcomm.CommandType = CommandType.StoredProcedure;
SqlParameter param = new SqlParameter("#a", SqlDbType.VarChar);
param.Direction = ParameterDirection.Input;
param.Value = Username;
sqlcomm.Parameters.Add(param);
SqlParameter retval = sqlcomm.Parameters.Add("#b", SqlDbType.VarChar);
retval.Direction = ParameterDirection.ReturnValue;
string retunvalue = (string)sqlcomm.Parameters["#b"].Value;
Note: Exception handling cut to keep the code short. Everytime I get to the last line, null is returned. What's the logic error with this code?
Mehrdad makes some good points, but the main thing I noticed is that you never run the query...
SqlParameter retval = sqlcomm.Parameters.Add("#b", SqlDbType.VarChar);
retval.Direction = ParameterDirection.ReturnValue;
sqlcomm.ExecuteNonQuery(); // MISSING
string retunvalue = (string)sqlcomm.Parameters["#b"].Value;
retval.Direction = ParameterDirection.Output;
ParameterDirection.ReturnValue should be used for the "return value" of the procedure, not output parameters. It gets the value returned by the SQL RETURN statement (with the parameter named #RETURN_VALUE).
Instead of RETURN #b you should SET #b = something
By the way, return value parameter is always int, not string.
I was having tons of trouble with the return value, so I ended up just selecting stuff at the end.
The solution was just to select the result at the end and return the query result in your functinon.
In my case I was doing an exists check:
IF (EXISTS (SELECT RoleName FROM dbo.Roles WHERE #RoleName = RoleName))
SELECT 1
ELSE
SELECT 0
Then
using (SqlConnection cnn = new SqlConnection(ConnectionString))
{
SqlCommand cmd = cnn.CreateCommand();
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "RoleExists";
return (int) cmd.ExecuteScalar()
}
You should be able to do the same thing with a string value instead of an int.
This is building on Joel's and Mehrdad's answers: you're never binding the parameter of the retval to the sqlcommand. You need a
sqlcomm.Parameters.Add(retval);
and to make sure you're running the command
sqlcomm.ExecuteNonQuery();
I'm also not sure why you have 2 return value strings (returnValue and retunvalue).
You say your SQL compiles fine, but I get: Must declare the scalar variable "#Password".
Also you are trying to return a varchar (#b) from your stored procedure, but SQL Server stored procedures can only return integers.
When you run the procedure you are going to get the error:
'Conversion failed when converting the varchar value 'x' to data type int.'
There are multiple problems here:
It is not possible. You are trying to return a varchar. Stored
procedure return values can only be integer expressions. See
official RETURN documentation:
https://msdn.microsoft.com/en-us/library/ms174998.aspx.
Your sqlcomm was never executed. You have to call
sqlcomm.ExecuteNonQuery(); in order to execute your command.
Here is a solution using OUTPUT parameters. This was tested with:
Windows Server 2012
.NET v4.0.30319
C# 4.0
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[Validate]
#a varchar(50),
#b varchar(50) OUTPUT
AS
BEGIN
DECLARE #b AS varchar(50) = (SELECT Password FROM dbo.tblUser WHERE Login = #a)
SELECT #b;
END
SqlConnection SqlConn = ...
var sqlcomm = new SqlCommand("Validate", SqlConn);
string returnValue = string.Empty;
try
{
SqlConn.Open();
sqlcomm.CommandType = CommandType.StoredProcedure;
SqlParameter param = new SqlParameter("#a", SqlDbType.VarChar);
param.Direction = ParameterDirection.Input;
param.Value = Username;
sqlcomm.Parameters.Add(param);
SqlParameter output = sqlcomm.Parameters.Add("#b", SqlDbType.VarChar);
ouput.Direction = ParameterDirection.Output;
sqlcomm.ExecuteNonQuery(); // This line was missing
returnValue = output.Value.ToString();
// ... the rest of code
} catch (SqlException ex) {
throw ex;
}
When we return a value from Stored procedure without select statement.
We need to use "ParameterDirection.ReturnValue" and "ExecuteScalar" command to get the value.
CREATE PROCEDURE IsEmailExists
#Email NVARCHAR(20)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
IF EXISTS(SELECT Email FROM Users where Email = #Email)
BEGIN
RETURN 0
END
ELSE
BEGIN
RETURN 1
END
END
in C#
GetOutputParaByCommand("IsEmailExists")
public int GetOutputParaByCommand(string Command)
{
object identity = 0;
try
{
mobj_SqlCommand.CommandText = Command;
SqlParameter SQP = new SqlParameter("returnVal", SqlDbType.Int);
SQP.Direction = ParameterDirection.ReturnValue;
mobj_SqlCommand.Parameters.Add(SQP);
mobj_SqlCommand.Connection = mobj_SqlConnection;
mobj_SqlCommand.ExecuteScalar();
identity = Convert.ToInt32(SQP.Value);
CloseConnection();
}
catch (Exception ex)
{
CloseConnection();
}
return Convert.ToInt32(identity);
}
We get the returned value of SP "IsEmailExists" using above c# function.
This SP looks very strange. It does not modify what is passed to #b. And nowhere in the SP you assign anything to #b. And #Password is not defined, so this SP will not work at all.
I would guess you actually want to return #Password, or to have SET #b = (SELECT...)
Much simpler will be if you modify your SP to (note, no OUTPUT parameter):
set ANSI_NULLS ON set QUOTED_IDENTIFIER ON go
ALTER PROCEDURE [dbo].[Validate] #a varchar(50)
AS
SELECT TOP 1 Password FROM dbo.tblUser WHERE Login = #a
Then, your code can use cmd.ExecuteScalar, and receive the result.
There are two things to fix about this. First set up the stored procedure to store the value in the output ( not return ) parameter.
set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
go
ALTER PROCEDURE [dbo].[Validate]
#a varchar(50),
#b varchar(50) output
AS
SET #b =
(SELECT Password
FROM dbo.tblUser
WHERE Login = #a)
RETURN
GO
This will but the password into #b and you will get it as a return parameter. Then to get it in your C# do this:
SqlConnection SqlConn = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["MyLocalSQLServer"].ConnectionString.ToString());
System.Data.SqlClient.SqlCommand sqlcomm = new System.Data.SqlClient.SqlCommand("Validate", SqlConn);
string returnValue = string.Empty;
try
{
SqlConn.Open();
sqlcomm.CommandType = CommandType.StoredProcedure;
SqlParameter param = new SqlParameter("#a", SqlDbType.VarChar, 50);
param.Direction = ParameterDirection.Input;
param.Value = Username;
sqlcomm.Parameters.Add(param);
SqlParameter retval = new SqlParameter("#b", SqlDbType.VarChar, 50);
retval.Direction = ParameterDirection.ReturnValue;
sqlcomm.Parameters.Add(retval);
sqlcomm.ExecuteNonQuery();
SqlConn.Close();
string retunvalue = retval.Value.ToString();
}
May be this will help.
Database script:
USE [edata]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[InsertNewUser](
#neuname NVARCHAR(255),
#neupassword NVARCHAR(255),
#neuposition NVARCHAR(255)
)
AS
BEGIN
BEGIN TRY
DECLARE #check INT;
SET #check = (SELECT count(eid) FROM eusers WHERE euname = #neuname);
IF(#check = 0)
INSERT INTO eusers(euname,eupassword,eposition)
VALUES(#neuname,#neupassword,#neuposition);
DECLARE #lastid INT;
SET #lastid = ##IDENTITY;
RETURN #lastid;
END TRY
BEGIN CATCH
SELECT ERROR_LINE() as errline,
ERROR_MESSAGE() as errmessage,
ERROR_SEVERITY() as errsevirity
END CATCH
END
Application configuration file:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<add key="conStr" value="Data Source=User\SQLEXPRESS;Initial Catalog=edata;Integrated Security=True"/>
</appSettings>
</configuration>
Data Access Layer (DAL):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
namespace DAL
{
public static class DAL
{
public static SqlConnection conn;
static DAL()
{
conn = new SqlConnection(ConfigurationManager.AppSettings["conStr"].ToString());
conn.Open();
}
}
}
Business Logic Layer(BLL):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data;
using System.Data.SqlClient;
using DAL;
namespace BLL
{
public static class BLL
{
public static int InsertUser(string lastid, params SqlParameter[] coll)
{
int lastInserted = 0;
try
{
SqlCommand comm = new SqlCommand();
comm.Connection = DAL.DAL.conn;
foreach (var param in coll)
{
comm.Parameters.Add(param);
}
SqlParameter lastID = new SqlParameter();
lastID.ParameterName = lastid;
lastID.SqlDbType = SqlDbType.Int;
lastID.Direction = ParameterDirection.ReturnValue;
comm.Parameters.Add(lastID);
comm.CommandType = CommandType.StoredProcedure;
comm.CommandText = "InsertNewUser";
comm.ExecuteNonQuery();
lastInserted = (int)comm.Parameters[lastid].Value;
}
catch (SqlException ex)
{
}
finally {
if (DAL.DAL.conn.State != ConnectionState.Closed) {
DAL.DAL.conn.Close();
}
}
return lastInserted;
}
}
}
Implementation :
BLL.BLL.InsertUser("#lastid",new SqlParameter("neuname","Ded"),
new SqlParameter("neupassword","Moro$ilka"),
new SqlParameter("neuposition","Moroz")
);
You have mixed up the concept of the Return Value and Output variable.
1- Output Variable:
Database----->:
create proc MySP
#a varchar(50),
#b varchar(50) output
AS
SET #Password =
(SELECT Password
FROM dbo.tblUser
WHERE Login = #a)
C# ----->:
SqlConn.Open();
sqlcomm.CommandType = CommandType.StoredProcedure;
SqlParameter param = new SqlParameter("#a", SqlDbType.VarChar);
param.Direction = ParameterDirection.Input;//This is optional because Input is the default
param.Value = Username;
sqlcomm.Parameters.Add(param);
SqlParameter outputval = sqlcomm.Parameters.Add("#b", SqlDbType.VarChar);
outputval .Direction = ParameterDirection.Output//NOT ReturnValue;
string outputvalue = sqlcomm.Parameters["#b"].Value.ToString();
Suppose you need to pass Username and Password to Stored Procedure and know whether login is successful or not and check if any error has occurred in Stored Procedure.
public bool IsLoginSuccess(string userName, string password)
{
try
{
SqlConnection SQLCon = new SqlConnection(WebConfigurationManager.ConnectionStrings["SqlConnector"].ConnectionString);
SqlCommand sqlcomm = new SqlCommand();
SQLCon.Open();
sqlcomm.CommandType = CommandType.StoredProcedure;
sqlcomm.CommandText = "spLoginCheck"; // Stored Procedure name
sqlcomm.Parameters.AddWithValue("#Username", userName); // Input parameters
sqlcomm.Parameters.AddWithValue("#Password", password); // Input parameters
// Your output parameter in Stored Procedure
var returnParam1 = new SqlParameter
{
ParameterName = "#LoginStatus",
Direction = ParameterDirection.Output,
Size = 1
};
sqlcomm.Parameters.Add(returnParam1);
// Your output parameter in Stored Procedure
var returnParam2 = new SqlParameter
{
ParameterName = "#Error",
Direction = ParameterDirection.Output,
Size = 1000
};
sqlcomm.Parameters.Add(returnParam2);
sqlcomm.ExecuteNonQuery();
string error = (string)sqlcomm.Parameters["#Error"].Value;
string retunvalue = (string)sqlcomm.Parameters["#LoginStatus"].Value;
}
catch (Exception ex)
{
}
return false;
}
Your connection string in Web.Config
<connectionStrings>
<add name="SqlConnector"
connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=Databasename;User id=yourusername;Password=yourpassword"
providerName="System.Data.SqlClient" />
</connectionStrings>
And here is the Stored Procedure for reference
CREATE PROCEDURE spLoginCheck
#Username Varchar(100),
#Password Varchar(100) ,
#LoginStatus char(1) = null output,
#Error Varchar(1000) output
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRY
BEGIN
SET #Error = 'None'
SET #LoginStatus = ''
IF EXISTS(SELECT TOP 1 * FROM EMP_MASTER WHERE EMPNAME=#Username AND EMPPASSWORD=#Password)
BEGIN
SET #LoginStatus='Y'
END
ELSE
BEGIN
SET #LoginStatus='N'
END
END
END TRY
BEGIN CATCH
BEGIN
SET #Error = ERROR_MESSAGE()
END
END CATCH
END
GO
When you use
cmd.Parameters.Add("#RETURN_VALUE", SqlDbType.Int).Direction = ParameterDirection.ReturnValue;
you must then ensure your stored procedure has
return #RETURN_VALUE;
at the end of the stored procedure.
The value you are trying to get is not a return value but an output parameter. You need to change parametere direction to Output.
SqlParameter retval = sqlcomm.Parameters.Add("#b", SqlDbType.VarChar);
retval.Direction = ParameterDirection.Output;
command.ExecuteNonquery();
string retunvalue = (string)sqlcomm.Parameters["#b"].Value;
For .net core 3.0 and dapper:
If your stored procedure returns this:
select ID, FILE_NAME from dbo.FileStorage where ID = (select max(ID) from dbo.FileStorage);
Then in c#:
var data = (_dbConnection.Query<FileUploadQueryResponse>
("dbo.insertFile", whateverParameters, commandType: CommandType.StoredProcedure)).ToList();
var storedFileName = data[0].FILE_NAME;
var id = data[0].ID;
As you can see, you can define a simple class to help with retrieving the actual values from dapper's default return structure (which I found impossible to work with):
public class FileUploadQueryResponse
{
public string ID { get; set; }
public string FILE_NAME { get; set; }
}
This Line of code returns Store StoredProcedure returned value from SQL Server
cmd.Parameters.Add("#id", System.Data.SqlDbType.Int).Direction = System.Data.ParameterDirection.ReturnValue;
cmd.ExecuteNonQuery();
Atfer Execution of query value will returned from SP
id = (int)cmd.Parameters["#id"].Value;

Categories