Получение возвращаемого значения из хранимой процедуры в С#
У меня есть следующий запрос:
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
Это прекрасно компилируется.
В С# я хочу выполнить этот запрос и получить возвращаемое значение.
Мой код выглядит следующим образом:
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;
Примечание. Обработка исключений сокращается, чтобы код был коротким. Каждый раз, когда я добираюсь до последней строки, возвращается null. Какая логическая ошибка с этим кодом?
Спасибо
Ответы
Ответ 1
Мехрдад делает несколько хороших моментов, но главное, что я заметил, это то, что вы никогда не запускаете запрос...
SqlParameter retval = sqlcomm.Parameters.Add("@b", SqlDbType.VarChar);
retval.Direction = ParameterDirection.ReturnValue;
sqlcomm.ExecuteNonQuery(); // MISSING
string retunvalue = (string)sqlcomm.Parameters["@b"].Value;
Ответ 2
retval.Direction = ParameterDirection.Output;
ParameterDirection.ReturnValue
следует использовать для "возвращаемого значения" процедуры, а не для вывода параметров. Он получает значение, возвращаемое оператором SQL RETURN
(с параметром с именем @RETURN_VALUE
).
Вместо RETURN @b
вы должны SET @b = something
Кстати, параметр возвращаемого значения всегда int
, а не строка.
Ответ 3
У меня было много проблем с возвращаемым значением, поэтому в итоге я просто выделил материал в конце.
Решение заключалось только в том, чтобы выбрать результат в конце и вернуть результат запроса в ваш functinon.
В моем случае я выполнял проверку наличия:
IF (EXISTS (SELECT RoleName FROM dbo.Roles WHERE @RoleName = RoleName))
SELECT 1
ELSE
SELECT 0
Тогда
using (SqlConnection cnn = new SqlConnection(ConnectionString))
{
SqlCommand cmd = cnn.CreateCommand();
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "RoleExists";
return (int) cmd.ExecuteScalar()
}
Вы должны иметь возможность делать то же самое со строковым значением, а не с int.
Ответ 4
Это построено на Joel's и ответы Mehrdad's: вы никогда не связываете параметр retval
до sqlcommand
. Вам нужно
sqlcomm.Parameters.Add(retval);
и убедитесь, что вы используете команду
sqlcomm.ExecuteNonQuery();
Я также не уверен, почему у вас есть 2 строки возвращаемого значения (returnValue
и retunvalue
).
Ответ 5
Вы говорите, что ваши компиляции SQL отлично, но я получаю: Должен объявить скалярную переменную "@Password".
Также вы пытаетесь вернуть varchar (@b) из хранимой процедуры, но хранимые процедуры SQL Server могут возвращать целые числа.
При запуске процедуры вы получите ошибку:
'Ошибка конверсии при преобразовании значения varchar' x 'в тип данных int.'
Ответ 6
Этот SP выглядит очень странно. Он не изменяет то, что передается на @b. И нигде в SP вы ничего не назначаете @b. И @Password не определен, поэтому этот SP не будет работать вообще.
Я бы предположил, что вы действительно хотите вернуть @Password или установить SET @b = (SELECT...)
Намного проще будет, если вы измените свой SP на (обратите внимание, нет параметра OUTPUT):
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
Затем ваш код может использовать cmd.ExecuteScalar и получить результат.
Ответ 7
Здесь есть несколько проблем:
- Это невозможно. Вы пытаетесь вернуть varchar. Хранится
возвращаемые значения процедуры могут быть только целыми выражениями. Видеть
официальная документация RETURN:
https://msdn.microsoft.com/en-us/library/ms174998.aspx.
- Ваш
sqlcomm
никогда не выполнялся. Вы должны позвонить
sqlcomm.ExecuteNonQuery();
, чтобы выполнить вашу команду.
Вот решение, использующее параметры OUTPUT. Это было протестировано с помощью:
- Windows Server 2012
- .NET v4.0.30319
- С# 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;
}
Ответ 8
Вы перепутали концепцию переменной Return Value и Output.
1- Выходная переменная:
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();
Ответ 9
Есть две причины, чтобы исправить это. Сначала настройте хранимую процедуру для сохранения значения в параметре вывода (не возврата).
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
Это будет только пароль в @b, и вы получите его как возвращаемый параметр. Затем, чтобы получить его в С#, выполните следующее:
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();
}
Ответ 10
Возможно, это поможет.
База данных 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
Файл конфигурации приложения:
<?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>
Уровень доступа к данным (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();
}
}
}
Бизнес-логический уровень (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;
}
}
}
Реализация:
BLL.BLL.InsertUser("@lastid",new SqlParameter("neuname","Ded"),
new SqlParameter("neupassword","Moro$ilka"),
new SqlParameter("neuposition","Moroz")
);
Ответ 11
Когда вы используете
cmd.Parameters.Add("@RETURN_VALUE", SqlDbType.Int).Direction = ParameterDirection.ReturnValue;
вы должны убедиться, что ваша хранимая процедура имеет
return @RETURN_VALUE;
в конце хранимой процедуры.
Ответ 12
Предположим, вам нужно передать Username
и Password
в Хранимую процедуру и узнать, успешна ли регистрация или нет, и проверить, произошла ли какая-либо ошибка в Сохраненной процедуре.
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;
}
Строка подключения в 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>
И вот Сохраненная процедура для справки
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 [email protected] AND [email protected])
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