I have written a stored procedure for simple user Login function.
USE [project]
GO
/****** Object: StoredProcedure [dbo].[authenticateLogin] Script Date: 10/18/2013 9:24:57 AM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[authenticateLogin] #userName varchar, #password varchar
AS
BEGIN
Declare #userRole int;
Declare #uPassword varchar;
Declare #uRole int;
SET #uPassword = (SELECT uPassword FROM [user] WHERE uName=#userName);
IF(#uPassword = #password)
SET #userRole = (SELECT uRole FROM [user] WHERE uName=#userName);
ELSE
SET #userRole = 0;
--RETURN #userRole
SELECT #userRole
END
And I am calling this from my program like this:
internal int Authenticate(string userName, string password)
{
int userRole = 0;
Shared shrObj=new Shared();
string encPassword = shrObj.EncryptToString(password);
SqlConnection sqlCon = Shared.GetSqlCon();
var sqlCom = new SqlCommand("authenticateLogin", sqlCon);
sqlCom.CommandType = CommandType.StoredProcedure;
sqlCom.Parameters.AddWithValue("#userName", userName);
sqlCom.Parameters.AddWithValue("#password", encPassword);
try
{
userRole = Shared.ExecuteNonQueryOnProcedure(sqlCon, sqlCom);
if (userRole > 0) return userRole;
}
catch (SqlException sqlEx)
{
//catch
}
finally
{
Shared.COC(sqlCon);
}
return userRole;
}
In Shared.cs
public static int ExecuteNonQueryOnProcedure(SqlConnection sqlCon, SqlCommand sqlCom) {
int rowCount = 0;
SqlParameter returnParameter = sqlCom.Parameters.Add("userRole", SqlDbType.Int);
returnParameter.Direction = ParameterDirection.ReturnValue;
try
{
sqlCon.Open();
//rowCount = sqlCom.ExecuteNonQuery();
var inu = sqlCom.ExecuteScalar().ToString();
}
catch (Exception ex)
{
//catch
return rowCount;
}
return (int)returnParameter.Value;
}
But the issue is it always return me 0 even where provided arguments are matched. please help me where am i doing wrong.
You shouldn't pass EXEC procedure with SqlCommand. Use CommandType.StoredProcedure
var sqlCom = new SqlCommand("authenticateLogin",sqlCon);
sqlComm.CommandType = CommandType.StoredProcedure; //here
sqlCom.Parameters.AddWithValue("#userName", userName);
sqlCom.Parameters.AddWithValue("#password", encPassword);
try
{
userRole = (short)sqlComm.ExecuteScalar();
if (userRole > 0) return userRole;
}
To get Return Value you can add a parameter like:
SqlParameter returnParameter = sqlCom.Parameters.Add("userRole", SqlDbType.Int);
returnParameter.Direction = ParameterDirection.ReturnValue;
sqlCom.ExecuteScalar();
int returnValue = (int) returnParameter.Value;
In your SQL you should have
SELECT #userRole
instead of
RETURN #userRole
ExecuteScalar() will execute the command and return value of the first column in the first row that the SQL command produces, not the return value.
You don't return a value from an SP like that, in fact the return value is not much use.
If you want to use an SP for authentication like that then something like the following would work:
CREATE PROCEDURE [dbo].[authenticateLogin] #userName varchar, #password varchar
AS
BEGIN
SELECT uRole FROM [User]
WHERE uName=#userName AND uPassword = #password
This will return the uRole field for all users with a matching username and password which will hopefully be either 1 or 0 rows.
ok Guys, got it working it was a stupid mistake which me or neither anyone else realized.
I was not specifying the characters values in varchar(?) hence it was only taking first character of every variable and hence the if-Else block was failing. fixed that and everything is fine now.
Thanks everyone for helping out. :)
Related
I am having a problem returning an output parameter from a Sql Server stored procedure into a C# variable. I have read the other posts concerning this, not only here but on other sites, and I cannot get it to work. Here is what I currently have. Currently I am just trying to print the value that comes back. The following code returns a null value. What I an trying to return is the primary key. I have tried using ##IDENTITY and SCOPE_INDENTITY() (i.e. SET #NewId = SCOPE_IDENTITY()).
Stored Procedure:
CREATE PROCEDURE usp_InsertContract
#ContractNumber varchar(7),
#NewId int OUTPUT
AS
BEGIN
INSERT into [dbo].[Contracts] (ContractNumber)
VALUES (#ContractNumber)
Select #NewId = Id From [dbo].[Contracts] where ContractNumber = #ContractNumber
END
Opening the database:
pvConnectionString = "Server = Desktop-PC\\SQLEXPRESS; Database = PVDatabase; User ID = sa;
PASSWORD = *******; Trusted_Connection = True;";
try
{
pvConnection = new SqlConnection(pvConnectionString);
pvConnection.Open();
}
catch (Exception e)
{
databaseError = true;
}
Executing the command:
pvCommand = new SqlCommand("usp_InsertContract", pvConnection);
pvCommand.Transaction = pvTransaction;
pvCommand.CommandType = CommandType.StoredProcedure;
pvCommand.Parameters.Clear();
pvCommand.Parameters.Add(new SqlParameter("#ContractNumber", contractNumber));
SqlParameter pvNewId = new SqlParameter();
pvNewId.ParameterName = "#NewId";
pvNewId.DbType = DbType.Int32;
pvNewId.Direction = ParameterDirection.Output;
pvCommand.Parameters.Add(pvNewId);
try
{
sqlRows = pvCommand.ExecuteNonQuery();
if (sqlRows > 0)
Debug.Print("New Id Inserted = ",
pvCommand.Parameters["#NewId"].Value.ToString());
}
catch (Exception e)
{
Debug.Print("Insert Exception Type: {0}", e.GetType());
Debug.Print(" Message: {0}", e.Message);
}
}
I slightly modified your stored procedure (to use SCOPE_IDENTITY) and it looks like this:
CREATE PROCEDURE usp_InsertContract
#ContractNumber varchar(7),
#NewId int OUTPUT
AS
BEGIN
INSERT INTO [dbo].[Contracts] (ContractNumber)
VALUES (#ContractNumber)
SELECT #NewId = SCOPE_IDENTITY()
END
I tried this and it works just fine (with that modified stored procedure):
// define connection and command, in using blocks to ensure disposal
using(SqlConnection conn = new SqlConnection(pvConnectionString ))
using(SqlCommand cmd = new SqlCommand("dbo.usp_InsertContract", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
// set up the parameters
cmd.Parameters.Add("#ContractNumber", SqlDbType.VarChar, 7);
cmd.Parameters.Add("#NewId", SqlDbType.Int).Direction = ParameterDirection.Output;
// set parameter values
cmd.Parameters["#ContractNumber"].Value = contractNumber;
// open connection and execute stored procedure
conn.Open();
cmd.ExecuteNonQuery();
// read output value from #NewId
int contractID = Convert.ToInt32(cmd.Parameters["#NewId"].Value);
conn.Close();
}
Does this work in your environment, too? I can't say why your original code won't work - but when I do this here, VS2010 and SQL Server 2008 R2, it just works flawlessly....
If you don't get back a value - then I suspect your table Contracts might not really have a column with the IDENTITY property on it.
Before changing stored procedure please check what is the output of your current one. In SQL Server Management run following:
DECLARE #NewId int
EXEC #return_value = [dbo].[usp_InsertContract]
N'Gary',
#NewId OUTPUT
SELECT #NewId
See what it returns. This may give you some hints of why your out param is not filled.
I had a similar problem and first closed the connection and then read the parameters and it worked fine.
you can use pvConnection.Close(); before read the output parameter
try
{
pvConnection = new SqlConnection(pvConnectionString);
pvConnection.Open();
}
catch (Exception e)
{
databaseError = true;
}
pvCommand = new SqlCommand("usp_InsertContract", pvConnection);
pvCommand.Transaction = pvTransaction;
pvCommand.CommandType = CommandType.StoredProcedure;
pvCommand.Parameters.Clear();
pvCommand.Parameters.Add(new SqlParameter("#ContractNumber", contractNumber));
SqlParameter pvNewId = new SqlParameter();
pvNewId.ParameterName = "#NewId";
pvNewId.DbType = DbType.Int32;
pvNewId.Direction = ParameterDirection.Output;
pvCommand.Parameters.Add(pvNewId);
try
{
sqlRows = pvCommand.ExecuteNonQuery();
pvConnection.Close();
if (sqlRows > 0)
Debug.Print("New Id Inserted = ",
pvCommand.Parameters["#NewId"].Value.ToString());
}
catch (Exception e)
{
Debug.Print("Insert Exception Type: {0}", e.GetType());
Debug.Print(" Message: {0}", e.Message);
}
}
Stored Procedure.........
CREATE PROCEDURE usp_InsertContract
#ContractNumber varchar(7)
AS
BEGIN
INSERT into [dbo].[Contracts] (ContractNumber)
VALUES (#ContractNumber)
SELECT SCOPE_IDENTITY() AS [SCOPE_IDENTITY]
END
C#
pvCommand.CommandType = CommandType.StoredProcedure;
pvCommand.Parameters.Clear();
pvCommand.Parameters.Add(new SqlParameter("#ContractNumber", contractNumber));
object uniqueId;
int id;
try
{
uniqueId = pvCommand.ExecuteScalar();
id = Convert.ToInt32(uniqueId);
}
catch (Exception e)
{
Debug.Print(" Message: {0}", e.Message);
}
}
EDIT: "I still get back a DBNull value....Object cannot be cast from DBNull to other types. I'll take this up again tomorrow. I'm off to my other job,"
I believe the Id column in your SQL Table isn't a identity column.
In your C# code, you are using transaction for the command.
Just commit the transaction and after that access your parameter value, you will get the value.
Worked for me. :)
I am trying to insert into a table while returning its identity value. But "Index was outside the bounds of the array" error is thrown. I can execute query in dbForge successfully but not when I try to execute query in C# with oracle managed data access .
Query is very simple . If I disable transaction, the row is inserted in the database but i get the error and cannot get the return value.
var query = #"insert into table1 VALUES (97,'Mondon') RETURNING Id INTO :id";
OracleTransaction transaction = null;
using (var connection = new OracleConnection(_conStr))
{
try
{
connection.Open();
var command = connection.CreateCommand();
transaction = connection.BeginTransaction();
command.Transaction = transaction;
command.CommandText = query;
command.CommandTimeout = 5 * 60;
command.Parameters.Add(new OracleParameter("id", OracleDbType.Int32, ParameterDirection.ReturnValue));
var result = command.ExecuteNonQuery();
transaction.Commit();
var id = Convert.ToInt32(command.Parameters["id"].Value);
}
catch (Exception ex)
{
transaction.Rollback();
Logger.LogError(ex);
}
}
you have too much code
you have misconception(s)
Let me know if you have questions, see comments inline
int newId = 0;
// NOTE, if you don't insert into field 'ID' you need to list fields
var sql = "insert into table1 (fld1, fd2) VALUES (97,'Mondon') RETURNING Id INTO :id";
try
{
using (var conn = new OracleConnection(_conStr))
{
using (var cmd = new OracleCommand(sql, conn))
{
cmd.CommandType = CommandType.Text;
cmd.Parameters.Add(new OracleParameter("id", OracleDbType.Int32, ParameterDirection.Output)); // this is output, not return
conn.Open();
var count = cmd.ExecuteNonQuery();
if (count > 0) // table can have a trigger so number of rows changed can be more than 1
{
// YOUR BIG MISCONCEPTION HERE (FIXED)
OracleDecimal val = (OracleDecimal)cmd.Parameters["id"].Value; // this returns special oracle struct
int newId = val.ToInt32(); // you can use val.IsNull but here it is not possible
}
else
throw new Exception("Value not inserted");
}
}
}
catch (Exception ex)
{
Logger.LogError(ex);
}
Note that for insert of a single record explicit transaction is not needed
Old post, but I had this same problem today, and found a solution here: https://stackoverflow.com/a/29660204/210916
"You need to declare the variable as shown below. As a rule of thumb, always test your query on the Oracle server before you embed it into your code. Most, importantly use parametrized Store Procedures to avoid sql injection attacks. So Do not embed queries into your code." #Dan Hunex
In your query, you need to declare id before INSERT command
I think you have to encapsulate the insert into a function:
create function InsertTable1(n in integer, val in varchar2) return integer as
res integer;
begin
insert into table1 VALUES (n, val) RETURNING Id INTO res;
RETURN res;
end;
And then in your application:
var query = #"BEGIN :0 := InsertTable1(97,'Mondon'); END;";
It would be a good idea to define also the input values as bind-parameters, rather than static strings.
A full dynamic solution could be similar to this:
create function InsertTable(cmd in varchar2) return integer as
res integer;
begin
EXECUTE IMMEDIATE cmd USING OUT res;
RETURN res;
end;
var query = #"BEGIN :0 := InsertTable('insert into table1 VALUES (97,''Mondon'') RETURNING Id INTO :res'); END;";
I calling a stored procedure and it has an int return value. However there is an error on returning the value back to my back end.
public async Task<string> CreatePortfolio(Portfolio portfolio)
{
string statusMessage;
using (SqlConnection conn = new SqlConnection(Connection))
{
SqlParameter returnValue = new SqlParameter(); //Holds the bit that determines if insert was successful or not
SqlCommand command;
command = new SqlCommand();
command.Connection = conn;
conn.Open();
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "USP_Add_Portfolio";
command.Parameters.AddWithValue("#portfolioName", portfolio.PortfolioName);
command.Parameters.AddWithValue("#description", portfolio.Description);
command.Parameters.AddWithValue("#createID", portfolio.CreateID);
command.Parameters.AddWithValue("#updateID", portfolio.UpdateID);
command.Parameters.AddWithValue("#statusMessage", SqlDbType.NVarChar).Direction = ParameterDirection.Output;
returnValue.Direction = ParameterDirection.ReturnValue;
command.Parameters.Add(returnValue);
int i = await command.ExecuteNonQueryAsync().ConfigureAwait(false);
if(i == 1)
{
statusMessage = command.Parameters["#statusMessage"].Value.ToString();
}
else
{
statusMessage = "Error while adding please contact your administrator";
}
}
return statusMessage;
}
This is the stored procedure:
create procedure USP_Add_Portfolio
(#portfolioName as nchar(30) = null,
#description as nvarchar(200) = null,
#createID as nvarchar(40) = null,
#updateID as nvarchar(40) = null,
#statusMessage as nvarchar(max) output)
as
declare #success as int = 0
if #portfolioName is null
raiserror('Stored procedure USP_Add_Portfolio - Missing Parameter #portfolioName', 16,1)
else if exists( select * from Portfolio where [Portfolio_Name] = #portfolioName COLLATE SQL_Latin1_General_CP1_CI_AS)
begin
set #statusMessage = rtrim(#portfolioName) + ' already exists please try another portfolio name'
raiserror('Stored procedure USP_Add_Portfolio - Already exists #portfolioName', 16,1)
return 0
end
else if #createID is null
raiserror('Stored procedure USP_Add_Portfolio - Missing Parameter #Create_ID', 16,1)
else if #updateID is null
raiserror('Stored procedure USP_Add_Portfolio - Missing Parameter #Update_ID', 16,1)
else
begin
insert into Portfolio ([Portfolio_Name], [Long_Description], [Create_ID], [Create_TS], [Update_ID], [Update_TS])
values (#portfolioName, #description, #createID, getdate(), #updateID, getdate())
--Check to see if insert worked
set #statusMessage = case when ##ROWCOUNT = 1 then 'Successfully added ' + #portfolioName else 'Unable to add please try again' end
set #success = case when ##ROWCOUNT = 1 then 1 else 0 end
end
return #success
go
The stored procedure finishes and it adds the new record but it errors on
int i = await command.ExecuteNonQueryAsync().ConfigureAwait(false);
Error:
expecting an int but gets nvarchar
ExecuteNonQuery (not worrying about the async for the moment...) returns the number of rows affected for UPDATE, INSERT, DELETE, and -1 otherwise. It does NOT return information directly from the stored procedure.
In your case above, I think you should call the "await" without the "int i =" and not worry about the return value from the ExecuteNonQueryAsync call. Instead, after the value, look at the value in returnValue.Value, which would be the value of the "return" parameter. It is an object, so verify the type or use Convert.ToInt32().
This doesn't look correct because SqlDbType.NVarChar is an enumeration value:
command.Parameters.AddWithValue("#statusMessage", SqlDbType.NVarChar).Direction = ParameterDirection.Output;
What happens if you use this instead:
command.Parameters.Add(new SqlParameter("#statusMessage", SqlDbType.NVarChar, -1)).Direction = ParameterDirection.Output;
I'm trying to get the OUTPUT parameter to work, but for whatever reason, it's refusing to. I keep getting null as a result. If I do everything on the SQL side, it works fine. I tested it like this,
DECLARE #test INT
EXEC MyProc #number = 1, #id = #test
PRINT #test
which gave me the output exactly as I expected. I've looked over this code for an hour and it looks right. The likely cause is an ID10T error, but my brain just isn't seeing it.
public static int MyFunc(int id)
{
using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["cnString"].ConnectionString))
{
connection.Open();
using (var cmd = new SqlCommand("dbo.MyProc", connection)
{
CommandTimeout = 120,
CommandType = CommandType.StoredProcedure
})
{
cmd.Parameters.AddWithValue("#number", SqlDbType.Int);
var param = new SqlParameter("#id", SqlDbType.Int)
{
Direction = ParameterDirection.Output
};
cmd.Parameters.Add(param);
cmd.ExecuteNonQuery();
Debug.WriteLine(param.Value);
return Convert.ToInt32(param.Value);
}
}
}
ALTER PROCEDURE dbo.MyProc
(
#number INT ,
#id INT OUTPUT
)
AS
BEGIN
SET NOCOUNT ON;
SET #id = (
SELECT Id
FROM SomeTable
WHERE SomeValue = #number
);
RETURN;
END;
You have a simple typo ...
You are passing the value of the SqlDbType.Int that you are expecting instead of the parameter to MyFunc.
cmd.Parameters.AddWithValue("#number", SqlDbType.Int);
should be
cmd.Parameters.AddWithValue("#number", id);
You can try this :
cmd.Parameters.AddWithValue("#number", SqlDbType.Int).Value = id;
cmd.Parameters.Add(param).Value = id
ALTER PROCEDURE dbo.SP_InsertTicket
/*
(
#parameter1 int = 5,
#parameter2 datatype OUTPUT
)
declare #i as numeric
exec SP_InsertTicket 'asd','cd#y.com','232323','dasasd','sdasdas','01-jan-2010',#i output,'sdas','sdasd','02-jan-2010'
select #i*/
#Client_FullName varchar(30),
#Client_EmailAdd varchar(50),
#Client_Telephn varchar(15),
#Ticket_Subject varchar(50),
#Ticket_Source varchar(15),
#Ticket_CreateDate Datetime,
#Ticket_Id integer output,
#Que_Message varchar(100),
#Que_Attachment varchar(max),
#Que_UpdateDate Datetime
AS
declare #TickID integer;
/* SET NOCOUNT ON */
BEGIN
INSERT INTO tbl_Ticket (Client_FullName,Client_EmailAdd,Client_Telephn,Ticket_Subject,Ticket_Source,Ticket_CreateDate)
VALUES (#Client_FullName, #Client_EmailAdd ,#Client_Telephn,#Ticket_Subject,#Ticket_Source,#Ticket_CreateDate)
Select #TickID = MAX(Ticket_Id) from tbl_Ticket
set #Ticket_Id=#TickID
INSERT INTO tbl_TicketQuestion (Ticket_Id,Que_Message,Que_Attachment,Que_UpdateDate)
VALUES (#TickID,#Que_Message,#Que_Attachment,#Que_UpdateDate)
END
RETURN
This is my store procedure in which i need to return Ticket_Id to send it via email app
It insert records well bt not able to retirn value in DAL
Below is the code for executing stored procedure which returns value
public class cls_DAL
{
public cls_DAL()
{
//
// TODO: Add constructor logic here
//
}
static string strConn = System.Configuration.ConfigurationManager.ConnectionStrings["conString"].ConnectionString.ToString();
SqlConnection con = new SqlConnection(strConn);
SqlCommand cmd = new SqlCommand();
SqlDataAdapter da = new SqlDataAdapter();
DataSet ds = new DataSet();
DataTable dt = new DataTable();
public int insert_NewTicket(string fullname, string emailadd, string telephone, string subject, string source, DateTime date,string Message, string attachment, DateTime updatedate)
{
try
{
con.Open();
cmd = new SqlCommand("SP_InsertTicket", con);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#Client_FullName", fullname);
cmd.Parameters.AddWithValue("#Client_EmailAdd", emailadd);
cmd.Parameters.AddWithValue("#Client_Telephn",telephone);
cmd.Parameters.AddWithValue("#Ticket_Subject", subject);
cmd.Parameters.AddWithValue("#Ticket_Source",source);
cmd.Parameters.AddWithValue("#Ticket_CreateDate",date);
cmd.Parameters.AddWithValue("#Ticket_Id",0);
cmd.Parameters.AddWithValue("#Que_Message", Message);
cmd.Parameters.AddWithValue("#Que_Attachment", attachment);
cmd.Parameters.AddWithValue("#Que_UpdateDate",updatedate);
cmd.Parameters["#Ticket_Id"].Direction = ParameterDirection.InputOutput;
return cmd.ExecuteNonQuery();
int i = (int)cmd.Parameters["#Ticket_Id"].Value;
}
catch
{
throw;
}
finally
{
cmd.Dispose();
con.Close();
con.Dispose();
}
}
}
Its just a guess, not sure. You can give a try the following:
cmd.Parameters["#Ticket_Id"].Direction = ParameterDirection.InputOutput;
TO
cmd.Parameters["#Ticket_Id"].Direction = ParameterDirection.Output;
That won't compile you'll get unreachable code
cmd.Parameters["#Ticket_Id"].Direction = ParameterDirection.InputOutput; cmd.ExecuteNonQuery();
return (int)cmd.Parameters["#Ticket_Id"].Value;
or #Matt's solution below...
That cast is iffy as well...
And in a multi user scenario, ticketid will race.
Think about what could (will!!!) happen if you run two of these at the same time
Should be wrapped in a transaction.
And you don't need Max, either, Use Scope_Identity
You could run Select Scope_Identity() after the Insert statement. Then in your DAL Method return Convert.ToInt32(cmd.ExecuteScalar())
Change this:
return cmd.ExecuteNonQuery();
to
Int i = cmd.ExecuteScalar();
If you are only returning one integer from that procedure.
ExecuteNonQuery() isnt the method you want to be using here