SQL Server stored procedure Nullable parameter - sql-server

Problem:
When values are provided to the following script then executed using a setup in C# like below (or in SQL Server environment) the values do not update in the database.
Stored procedure:
-- Updates the Value of any type of PropertyValue
-- (Type meaining simple Value, UnitValue, or DropDown)
CREATE PROCEDURE [dbo].[usp_UpdatePropertyValue]
#PropertyValueID int,
#Value varchar(max) = NULL,
#UnitValue float = NULL,
#UnitOfMeasureID int = NULL,
#DropDownOptionID int = NULL
AS
BEGIN
-- If the Property has a #Value, Update it.
IF #Value IS NOT NULL
BEGIN
UPDATE [dbo].[PropertyValue]
SET
Value = #Value
WHERE
[dbo].[PropertyValue].[ID] = #PropertyValueID
END
-- Else check if it has a #UnitValue & UnitOfMeasureID
ELSE IF #UnitValue IS NOT NULL AND #UnitOfMeasureID IS NOT NULL
BEGIN
UPDATE [dbo].[UnitValue]
SET
UnitValue = #UnitValue,
UnitOfMeasureID = #UnitOfMeasureID
WHERE
[dbo].[UnitValue].[PropertyValueID] = #PropertyValueID
END
-- Else check if it has just a #UnitValue
ELSE IF #UnitValue IS NOT NULL AND #UnitOfMeasureID IS NULL
BEGIN
UPDATE [dbo].[UnitValue]
SET
UnitValue = #UnitValue
WHERE
[dbo].[UnitValue].[PropertyValueID] = #PropertyValueID
END
-- Else check if it has a #DropDownSelection to update.
ELSE IF #DropDownOptionID IS NULL
BEGIN
UPDATE [dbo].[DropDownSelection]
SET
SelectedOptionID = #DropDownOptionID
WHERE
[dbo].[DropDownSelection].[PropertyValueID] = #PropertyValueID
END
END
When I do an execution of this script, like below, it does not update any values.
Example execution:
String QueryString = "EXEC [dbo].[usp_UpdatePropertyValue] #PropertyValueID, #Value, #UnitValue, #UnitOfMeasureID, #DropDownOptionID";
SqlCommand Cmd = new SqlCommand(QueryString, this._DbConn);
Cmd.Parameters.Add(new SqlParameter("#PropertyValueID", System.Data.SqlDbType.Int));
Cmd.Parameters.Add(new SqlParameter("#Value", System.Data.SqlDbType.Int));
Cmd.Parameters.Add(new SqlParameter("#UnitValue", System.Data.SqlDbType.Int));
Cmd.Parameters.Add(new SqlParameter("#UnitOfMeasureID", System.Data.SqlDbType.Int));
Cmd.Parameters.Add(new SqlParameter("#DropDownOptionID", System.Data.SqlDbType.Int));
Cmd.Parameters["#PropertyValueID"].Value = Property.Value.ID; // 1
Cmd.Parameters["#Value"].IsNullable = true;
Cmd.Parameters["#Value"].Value = DBNull.Value;
Cmd.Parameters["#UnitValue"].IsNullable = true;
Cmd.Parameters["#UnitValue"].Value = DBNull.Value;
Cmd.Parameters["#UnitOfMeasureID"].IsNullable = true;
Cmd.Parameters["#UnitOfMeasureID"].Value = DBNull.Value;
Cmd.Parameters["#DropDownOptionID"].IsNullable = true;
Cmd.Parameters["#DropDownOptionID"].Value = 2; // Current Value in DB: 3
Details:
After running an execute (via C# code or SQL Server environment) it does not update dbo.DropDownSelection.SelectedOptionID. I'm guessing that it might be because dbo.DropDownSelection.SelectedOptionID is non-nullable and the parameter I'm using to set it is nullable (despite that when setting it shouldn't ever be null). Upon execution the return value is 0. If I run one of the Updates outside of the procedure they work perfectly, hence my suspicion that it has to do with null-able types.
Question(s):
Could this be because the parameters to the stored procedure are nullable and the fields I'm setting aren't?
If not, what could it be?

It looks like you're passing in Null for every argument except for PropertyValueID and DropDownOptionID, right? I don't think any of your IF statements will fire if only these two values are not-null. In short, I think you have a logic error.
Other than that, I would suggest two things...
First, instead of testing for NULL, use this kind syntax on your if statements (it's safer)...
ELSE IF ISNULL(#UnitValue, 0) != 0 AND ISNULL(#UnitOfMeasureID, 0) = 0
Second, add a meaningful PRINT statement before each UPDATE. That way, when you run the sproc in MSSQL, you can look at the messages and see how far it's actually getting.

You can/should set your parameter to value to DBNull.Value;
if (variable == "")
{
cmd.Parameters.Add("#Param", SqlDbType.VarChar, 500).Value = DBNull.Value;
}
else
{
cmd.Parameters.Add("#Param", SqlDbType.VarChar, 500).Value = variable;
}
Or you can leave your server side set to null and not pass the param at all.

Related

Stored procedure runs correctly but DataReader gets wrong result...sometimes

I have a stored procedure for a travel booking system that takes in a TripID and identifies whether the trip is domestic or not (e.g. whether the origin and destination countries are the same for all trip legs). When I run the procedure from SSMS, it correctly returns 1 for domestic and 0 for international. However, when I try to access the data in my application through DataReader, it inappropriately returns 0 for domestic trips.
That being said, I don't think the problem lies purely with the DataReader because when I alter my stored procedure to return 1 immediately, DataReader will correctly detect this value.
Can anyone suggest changes to my code to fix this behavior?
Here is the stored procedure, pared down:
SET NOCOUNT ON;
-- EXEC CheckIsDomestic 6343
Declare #HomeOffice INT = (SELECT TOP 1 o.DestinationID
FROM TR_Trips t
JOIN TR_Travelers ta ON t.TravelerID = ta.TravelerID
JOIN TR_OfficeLocations o ON ta.OfficeID = o.Office_Loc_Id
WHERE t.TripID = #TripID)
SELECT l.Destination_ID AS DestinationID
INTO #TempDest
FROM TR_Trips t JOIN TR_Legs l ON t.TripID = l.TripID
WHERE t.TripID = #TripID
--Check whether there is a destination in the list that is different than the home country
DECLARE #CountRows int = (SELECT COUNT(*)
FROM #TempDest t
WHERE DestinationID <> #HomeOffice )
IF #CountRows > 0
BEGIN
SELECT 0
RETURN --tried with and without RETURN; no change
END
ELSE
BEGIN SELECT 1
RETURN
END
And here are the applicable parts of my application:
public bool IsDomestic(int TripID)
{
bool ReturnValue = true;
NewStoredProcedureCommand("CheckIsDomestic");
AddParameter("#TripID", TripID, System.Data.SqlDbType.Bit);
ReturnValue = Execute_ReturnValueBool();
return ReturnValue;
}
public Boolean Execute_ReturnValueBool()
{
if (sqlCommand == null)
NewCommand();
if (sqlCommand.Connection.State != ConnectionState.Open)
sqlCommand.Connection.Open();
bool ReturnValue = false;
SqlDataReader DR = sqlCommand.ExecuteReader();
if (DR.HasRows)
{
DR.Read();
System.Diagnostics.Debug.WriteLine(DR[0]);
ReturnValue = Convert.ToBoolean(DR[0]);
}
DR.Close();
sqlCommand.Connection.Close();
return ReturnValue;
}
Why are you using the BIT type for your TripID parameter in the application code? Try setting it to INT.

Dapper Distinguishing Return Value and Result Set

I am using Dapper's QueryMultipleAsync method to read multiple result set. My stored procedure checks some conditions (for example a user might be trying to get someone else's data by sending an Id to my API) to determine whether the data should return or not.
I, on C# side, first need to read the return value (not result set) to determine if data is returned or SP simply returned 3 (which means insufficient rights). To illustrate the case:
IF #UserRole < 10
BEGIN
RETURN 3; -- Insufficient rights.
END
IF #IsCurrentUserOwner = 0
BEGIN
RETURN 5; -- Not owner.
END
-- Get users.
SELECT
Id
, [Name]
, LastName
FROM
Users
-- WHERE ...
-- Get chat messages.
SELECT
*
FROM
ChatMessages
-- WHERE ...
I know that output and return values are written at the end of reader so I can only read return parameter when all the data (result set) is read. So, I always have to read the result set first then return/output parameters.
What if my SP looked like this:
-- ...some code above...
IF #IsCurrentUserOwner = 0
BEGIN
RETURN 5; -- Not owner.
END
DECLARE #RType TINYINT = NULL
-- some other code here to get #RType value here...
SELECT #RType -- To make this a result so QueryMultiple's reader can read this.
-- ...some other code to get users and chat messages...
To describe the problem:
#RType variable could be 5 as well as SP's return value. When I read the result first (because output/return parameters are at the end of reader), how do I know the value I just read (which is 5 in this case) is the #RType or return value? (they are convertible)
This is how my C# code roughly looks like:
var parameters = new DynamicParameters();
parameters.Add("#RetVal", dbType: DbType.Int32, direction: ParameterDirection.ReturnValue);
var dbResult = await Context.SqlConnection.QueryMultipleAsync(sql: "my_sp_name", param: parameters, commandType: CommandType.StoredProcedure)
// Some code here...
using (var reader = dbResult)
{
var rType = reader.Read<byte>(); // <----- How to know if I read #RType or just return value of SP since they are convertible to each other?
var users = reader.Read<User>();
var chatMessages = reader.Read<ChatMessage>();
// ...
}
var returnValue = parameters.Get<int>("#RetVal");
if (returnValue == 5)
{
return "You are not allowed to see this data";
}
How do you recommend me to handle the case?
Your C# code uses async - await (QueryMultipleAsync) and currently Dapper doesn't support for Async methods that no perform a SELECT statement.
There is an open issue about that in Github:
https://github.com/StackExchange/Dapper/issues/591
Option 1
You can split your stored procedure into 2 parts:
1. Check the Return value.
2. Getting the select statements.
Option 2
ALTER PROCEDURE dbo.sp
... your params..
#Output INT OUTPUT
AS
IF #UserRole < 10
BEGIN
SET #Output = 3; -- Insufficient rights.
END
IF #IsCurrentUserOwner = 0
BEGIN
SET #Output = 5; -- Not owner.
END
-- Get users.
SELECT
Id
, [Name]
, LastName
FROM
Users
-- WHERE ...
-- Get chat messages.
SELECT
*
FROM
ChatMessages
-- WHERE ...
Option 3
Use ADO.NET
I wasn't really clear on your question, but the snippet below should work. Since you are defining the #RetVal as ParameterDirection.ReturnValue you should be able to access it before accessing the actual readers. I was able to verify this works in SQL SERVER.
var returnValue = parameters.Get<int>("#RetVal");
if (returnValue == 5 || returnValue == 3)
{
return "You are not allowed to see this data";
}
using (var reader = dbResult)
{
var rType = reader.Read<byte>(); // <----- How to know if I read #RType or just return value of SP since they are convertible to each other?
var users = reader.Read<User>();
var chatMessages = reader.Read<ChatMessage>();
// ...
}
here is my stored procedure
ALTER PROCEDURE mysp_TestOne
#p1 int
AS
BEGIN
SET NOCOUNT ON;
if #p1 > 0
return 5;
SELECT 1 F1
union
Select 2 F1;
SELECT 1 F1
union
Select 2 F1;
END
GO
and more complete snippet of c#;
using (SqlConnection cn = new SqlConnection(connectionString))
{
var sql = #"mysp_TestOne";
var parameters = new DynamicParameters();
// #p1 triggers whether to return query results or just the returnvalue
parameters.Add("p1", 0, direction: ParameterDirection.Input);
parameters.Add("#RetVal", 0, direction: ParameterDirection.ReturnValue);
var dbResult = await cn.QueryMultipleAsync(sql, parameters, commandType: CommandType.StoredProcedure);
var returnValue = parameters.Get<int>("#RetVal");
if (returnValue == 5 || returnValue == 3)
{
return "You are not allowed to see this data";
}
using (var reader = dbResult)
{
var rType = reader.Read<byte>(); // <----- How to know if I read #RType or just return value of SP since they are convertible to each other?
var users = reader.Read<User>();
var chatMessages = reader.Read<ChatMessage>();
// ...
}
}
HTH

ExecuteNonQuery always returns -1

I have created a stored procedure for deleting record. In this stored procedure I am first checking for the usage of data which I am going to delete. If it is being used, then the stored procedure will return -2 otherwise it deletes the record.
But the problem is that even the record exists its return -1 instead of -2. I have also set the NOCOUNT OFF but don't know where is the problem.
I know this question is already answered by setting NOCOUNT OFF but its not working for me
ALTER PROCEDURE [dbo].[spDeletePIDNumber]
#Id int
AS
BEGIN
SET NOCOUNT OFF;
-- Insert statements for procedure here
if(exists(select * from tblBills where PID = #Id))
begin
return -2
end
else
begin
Delete from HelperPIDNumber
where Id = #Id
end
END
public int DeletePIDNumber(int Id)
{
try
{
int result = 0;
using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.connection))
{
var cmd = new SqlCommand("spDeletePIDNumber", conn);
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#Id", Id);
conn.Open();
result = cmd.ExecuteNonQuery();
}
return result;
}
catch
{
throw;
}
}
From the ExecuteNonQuery documentation:
Executes a Transact-SQL statement against the connection and returns the number of rows affected.
Having SET NOCOUNT ON; in your procedure explicitely tells to SQL Server not to return a row count. In that case the return of the ExecuteNonQuery function is -1.
Also if the procedure does not affect any rows, it will not return a row count either even if NOCOUNT is OFF. In that case the return will also be -1.
What you appear to want to do is get the return value of the stored procedure. You will not get that from the result of ExecuteNonQuery. Please refer to this question on StackOverflow: Getting return value from stored procedure in ADO.NET
Generally ExecuteNonQuery will return number of affected records. It will return -1 in two cases:
When SET NOCOUNT ON has been set. From your code, its clear, you have SET NOCOUNT OFF and so this is not an issue at your case.
If number of affected rows is nothing, it will return -1. In your case, it looks like you are checking the data exists from one table tblBills and delete from another table HelperPIDNumber. So there is more chance there will be no matching record and nothing deleted.
Please check the point # 2 above.
if( exists(select * from tblBills where PID = #Id))
begin
return -2
end
else
begin
Delete from HelperPIDNumber where Id = #Id
end
use cmd.ExecuteScalar() instead of cmd.ExecuteNonQuery() ascmd.ExecuteNonQuery() return only the number of affected rows and not the value you are selecting.

Receiving one out of few Result Sets in stored procedure

I have a stored procedure that works fine but it has inside it three "select"s.
The selects are not from an inner temporary table.
This is mainly the format of the procedure:
ALTER PROCEDURE [dbo].[STProce]
#param1 int,
#param2 int,
#param3 int,
#param4 int,
#param5 int
AS
select #param1 as p1, #param2 as p2, #param3 as p3
.
.
.
select #param4 as p4
.
.
.
select #param5 as p5
I'm executing the procedure from another procedure and need to catch it there.
I created a table and inserts into it the "exec" from the procedure, like that:
CREATE TABLE #stalledp
(
RowNumber INT,
fldid INT,
fldLastUpdated datetime,
fldCreationDate datetime,
fldName nvarchar(255),
fldPending nvarchar(255)
)
INSERT INTO #stalledp (RowNumber,fldid,fldLastUpdated,fldCreationDate,fldName,fldPending)
EXEC spDebuggerViews_GetStuckWorkflowInstances #workflowSpaceId='00000000-0000-0000-0000-000000000000',#pageNum=1,#pageSize=100000,#orderByColumn=N'fldid',#sortOrder=1,#workflowInstanceId=0,#stuckInstanceType=1,#createdDateFrom='1900-01-01 00:00:00',#createdDateTo='9999-01-01 23:59:59',#updatedDateFrom='1900-01-01 00:00:00',#updatedDateTo='9999-01-01 23:59:59'
Afterwards I receive this error:
Column name or number of supplied values does not match table definition.
The order and name of columns of the table is exactly like the procedure returns.
Is there a possibility to catch only one of the tables that the procedure returns and avoid the other? I cannot change the procedure at all.
I tried declaring a table the same fields as the first select of the procedure and I get an error says that
Thank you in advance!
If all of the result sets returned are of the same structure, then you can dump them to a temp table as you are trying to do. However, that only gets you so far because if the data in the fields cannot be used to determine which result set a particular row came from, then you just have all of the result sets with no way to filter out the ones you don't want.
The only way to interact with multiple result sets individually, regardless of them having the same or differing structures, is through app code (i.e. a client connection). And if you want to do this within the context of another query, then you need to use SQLCLR.
The C# code below shows a SQLCLR stored procedure that will execute a T-SQL stored procedure that returns 4 result sets. It skips the first 2 result sets and only returns the 3rd result set. This allows the SQLCLR stored procedure to be used in an INSERT...EXEC as desired.
The code for the T-SQL stored proc that is called by the following code is shown below the C# code block. The T-SQL test proc executes sp_who2 and only return a subset of the fields being returned by that proc, showing that you don't need to return the exact same result set that you are reading; it can be manipulated in transit.
C# SQLCLR proc:
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
public class TheProc
{
[Microsoft.SqlServer.Server.SqlProcedure]
public static void Get3rdResultSetFromGetStuckWorkflowInstances()
{
int _ResultSetsToSkip = 2; // we want the 3rd result set
SqlConnection _Connection = null;
SqlCommand _Command = null;
SqlDataReader _Reader = null;
try
{
_Connection = new SqlConnection("Context Connection = true;");
_Command = _Connection.CreateCommand();
_Command.CommandType = CommandType.StoredProcedure;
_Command.CommandText = "tempdb.dbo.MultiResultSetTest";
// (optional) add parameters (but don't use AddWithValue!)
// The SqlDataRecord will be used to define the result set structure
// and act as a container for each row to be returned
SqlDataRecord _ResultSet = new SqlDataRecord(
new SqlMetaData[]
{
new SqlMetaData("SPID", SqlDbType.Char, 5),
new SqlMetaData("Status", SqlDbType.NVarChar, 30),
new SqlMetaData("Login", SqlDbType.NVarChar, 128),
new SqlMetaData("HostName", SqlDbType.NVarChar, 128),
new SqlMetaData("BlkBy", SqlDbType.VarChar, 5),
new SqlMetaData("DBName", SqlDbType.NVarChar, 128)
});
SqlContext.Pipe.SendResultsStart(_ResultSet); // initialize result set
_Connection.Open();
_Reader = _Command.ExecuteReader();
// Skip a predefined number of result sets
for (int _Index = 0;
_Index < _ResultSetsToSkip && _Reader.NextResult();
_Index++) ;
// Container used to move 1 full row from the result set being read
// to the one being sent, sized to the number of fields being read
Object[] _TempRow = new Object[_Reader.FieldCount];
while (_Reader.Read())
{
_Reader.GetValues(_TempRow); // read all columns
_ResultSet.SetValues(_TempRow); // set all columns
SqlContext.Pipe.SendResultsRow(_ResultSet); // send row
}
}
catch
{
throw;
}
finally
{
if(SqlContext.Pipe.IsSendingResults)
{
SqlContext.Pipe.SendResultsEnd(); // close out result set being sent
}
if(_Reader != null && !_Reader.IsClosed)
{
_Reader.Dispose();
}
_Command.Dispose();
if (_Connection != null && _Connection.State != ConnectionState.Closed)
{
_Connection.Dispose();
}
}
return;
}
}
T-SQL test proc:
USE [tempdb]
SET ANSI_NULLS ON;
IF (OBJECT_ID('dbo.MultiResultSetTest') IS NOT NULL)
BEGIN
DROP PROCEDURE dbo.MultiResultSetTest;
END;
GO
CREATE PROCEDURE dbo.MultiResultSetTest
AS
SET NOCOUNT ON;
SELECT 1 AS [ResultSet], 'asa' AS [test];
SELECT 2 AS [ResultSet], NEWID() AS [SomeGUID], GETDATE() AS [RightNow];
EXEC sp_who2;
SELECT 4 AS [ResultSet], CONVERT(MONEY, 131.12) AS [CashYo];
GO
EXEC tempdb.dbo.MultiResultSetTest;
To do:
Adjust _ResultSetsToSkip as appropriate. If you only want the first result set, simply remove both _ResultSetsToSkip and the for loop.
Define _ResultSet as appropriate
Set _Command.CommandText to be "spDebuggerViews_GetStuckWorkflowInstances"
Create the necessary parameters via SqlParameter (i.e. #workflowSpaceId='00000000-0000-0000-0000-000000000000',#pageNum=1,#pageSize=100000,#orderByColumn=N'fldid',#sortOrder=1,#workflowInstanceId=0,#stuckInstanceType=1,#createdDateFrom='1900-01-01 00:00:00',#createdDateTo='9999-01-01 23:59:59',#updatedDateFrom='1900-01-01 00:00:00',#updatedDateTo='9999-01-01 23:59:59')
If needed, add input parameters to the SQLCLR proc so that they can be used to set the values of certain SqlParameters
Then use as follows:
INSERT INTO #stalledp
(RowNumber,fldid,fldLastUpdated,fldCreationDate,fldName,fldPending)
EXEC Get3rdResultSetFromGetStuckWorkflowInstances;
There is a way to get the first record set but the others, I'm afraid, you're out of luck.
EXEC sp_addlinkedserver #server = 'LOCALSERVER', #srvproduct = '',
#provider = 'SQLOLEDB', #datasrc = ##servername
SELECT * FROM OPENQUERY(LOCALSERVER, 'EXEC testproc2')
EDIT: If you only need to check the other result set for columns to be not null you could predefine the expected results sets like so:
EXEC testproc2 WITH RESULT SETS (
(a VARCHAR(MAX) NOT NULL, b VARCHAR(MAX) NOT NULL),
(a VARCHAR(MAX) NOT NULL)
);
If the query within the stored procedure returns null values a exception is raised at that point in procedure. This will only work on sql server 2012 and upwards though.

Getting a result feedback from a stored procedure in Entity Framework

In a SQL Server 2008 I have a simple stored procedure moving a bunch of records to another table:
CREATE PROCEDURE [dbo].MyProc(#ParamRecDateTime [datetime])
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO [dbo].Table2
SELECT
...,
...
FROM [dbo].Table1
WHERE RecDateTime <= #ParamRecDateTime
DELETE FROM [dbo].Table1
WHERE RecDateTime <= #ParamRecDateTime
END
Running it from within SQL Server Management Studio, I get the job done and return value = 0
DECLARE #return_value int
EXEC #return_value = dbo.MyProc #ParamRecDateTime = '2011-06-25 11:00:00.000'
SELECT 'Return Value' = #return_value
But when I call the same stored procedure from an app using Entity framework, I also get the job done but the return value is "-1":
int result = myrepository.MyProc(datetimePar);
MessageBox.Show(result.ToString());
I didn't manage to find an explanation for this error, but found this discouraging post, where it's said that there is no standard for this type of return codes in SQL Server.
What is the good, reliable way of getting know of a Stored Procedure execution result when calling it from Entity Framework and when the Stored Procedure doesn't return any entities?
One way to do it is to call ExecuteStoreCommand, and pass in a SqlParameter with a direction of Output:
var dtparm = new SqlParameter("#dtparm", DateTime.Now);
var retval = new SqlParameter("#retval", SqlDbType.Int);
retval.Direction = ParameterDirection.Output;
context.ExecuteStoreCommand("exec #retval = MyProc #dtparm", retval, dtparm);
int return_value = (int)retval.Value;
Originally I tried using a direction of ReturnValue:
retval.Direction = ParameterDirection.ReturnValue;
context.ExecuteStoreCommand("MyProc #dtparm", retval, dtparm);
but retval.Value would always be 0. I realized that retval was the result of executing the MyProc #dtparm statement, so I changed it to capture the return value of MyProc and return that as an output parameter.
using (dbContext db = new dbContext())
{
var parameters = new[]
{
new SqlParameter("#1","Input Para value"),
new SqlParameter("#2",SqlDbType.VarChar,4){ Value = "default if you want"},
new SqlParameter("#3",SqlDbType.Int){Value = 0},
new SqlParameter("#4","Input Para Value"),
new SqlParameter("#5",SqlDbType.VarChar,10 ) { Direction = ParameterDirection.Output },
new SqlParameter("#6",SqlDbType.VarChar,1000) { Direction = ParameterDirection.Output }
};
db.ExecuteStoreCommand("EXEC SP_Name #1,#2,#3,#4,#5 OUT,#6 OUT", parameters);
ArrayList ObjList = new ArrayList();
ObjList.Add(parameters[1].Value);
ObjList.Add(parameters[2].Value);
}
See OUTPUT attribute for SQL param of store procedure,
here
For future reference: I had the same issue but needed multiple OUTPUT variables. The solution was a combination of both answers. Below is a complete sample.
public void MyStoredProc(int inputValue, out decimal outputValue1, out decimal outputValue2)
{
var parameters = new[] {
new SqlParameter("#0", inputValue),
new SqlParameter("#1", SqlDbType.Decimal) { Direction = ParameterDirection.Output },
new SqlParameter("#2", SqlDbType.Decimal) { Direction = ParameterDirection.Output }
};
context.ExecuteStoreCommand("exec MyStoredProc #InParamName=#0, #OutParamName1=#1 output, #OutParamName2=#2 output", parameters);
outputValue1 = (decimal)parameters[1].Value;
outputValue2 = (decimal)parameters[2].Value;
}
Please note the Types used (decimal.) If another type is needed, remember to not only change it in the method argument list but also the SqlDbType.XXX.

Resources