Sorry if someone has already given an answer to this, I have been browsing the site and trying tips and bits of code for hours, to no avail.
I have a stored procedure which basically retrieves the id of an element, if this element exists, and zero if it doesn't.
Here we go:
PROCEDURE [dbo].[ChecksProductExistence]
#Product_Name varchar(50),
#Return_Value Int output
AS
BEGIN
IF EXISTS (SELECT prod.ProductID
FROM dbo.Products prod
WHERE prod.ProductName = #Product_Name)
SET #Return_Value = 1;
ELSE
SET #Return_Value = 0;
END
Okay, so far so good. Execute that in my database, and it works like a charm... not that it's such complex code.
Then, in Visual Studio, I coded the following:
private static bool checkExistingProduct(ShoppingListContext ctx, Product product)
{
var cmd = ctx.Database.Connection.CreateCommand();
cmd.CommandText = "[dbo].[ChecksProductExistence]";
ctx.Database.Connection.Open();
int bla = 0;
SqlParameter inParameter = new SqlParameter();
inParameter.ParameterName = "#Product_Name";
inParameter.SqlDbType = SqlDbType.NVarChar;
inParameter.Direction = ParameterDirection.Input;
inParameter.Value = product.ProductName;
SqlParameter outParameter = new SqlParameter();
outParameter.ParameterName = "#Return_Value";
outParameter.SqlDbType = SqlDbType.Int;
outParameter.Direction = ParameterDirection.Output;
cmd.Parameters.Add(inParameter);
cmd.Parameters.Add(outParameter);
bla = cmd.ExecuteNonQuery();
Console.WriteLine(outParameter.Value);
Console.ReadLine();
if ((int)outParameter.Value == 1) return true; else return false;
}
As you can see... simple enough. I create a command, add parameters, execute it, try some decision making on the output.
But... I get an error on the
cmd.ExecuteNonQuery();
line. I have changed it to ExecuteScalar, for example, but still no luck. I have executed it without the "bla = ...", and still the same.
The error I get is
An unhandled exception of type 'System.Data.SqlClient.SqlException' occurred in System.Data.dll
Additional information: Procedure or function 'ChecksProductExistence' expects parameter '#Product_Name', which was not supplied.
But then again, not only can you see clearly in the code that I AM IN FACT SUPPLYING IT, but also on the debug session I can explore the cmd object and browse the parameters, and I can see that IT IS THERE, and the values are correct...
So what else can I do?? Am I making some silly mistake I can't see...?
As stated in the documentation for SqlCommand.CommandType you have to set it to CommandType.StoredProcedure and set the CommandText to the name of the stored procedure. It defaults to CommandType.Text where you have to include the named parameters in the text of the query.
When you set the CommandType property to StoredProcedure, you should
set the CommandText property to the name of the stored procedure. The
command executes this stored procedure when you call one of the
Execute methods.
The Microsoft .NET Framework Data Provider for SQL
Server does not support the question mark (?) placeholder for passing
parameters to a SQL Statement or a stored procedure called with a
CommandType of Text. In this case, named parameters must be used. For
example:
SELECT * FROM Customers WHERE CustomerID = #CustomerID
So, just add the following
cmd.CommandType = CommandType.StoredProcedure;
somewhere before executing it.
Related
I have a stored procedure that has a parameter called UserName and in my code behind I have a SqlCommand object that I add the parameters to with the Add method. But for some reason when the command object tries to run the ExecuteReader method, it throws an exception. I am totally at a loss and have no idea why it's not recognizing the parameter. Before the ExecuteReader method is run I have a break point set so I can confirm the command object does contain the parameters being set, which is true. I know the stored procedure does return the correct data when the parameters are not added to the command object, but are hard coded in the actual stored procedure. Below is the exception message that is given in the catch block. I will also paste my code and first part of stored procedure. I would greatly appreciate any help in this issue, seeing that I have tried many different approaches to no avail. Thanks in advance.
Exception Message
Procedure or function 'someStoredProcedure' expects parameter '#UserName', which was not supplied.
Code Behind
private DataTable GetLossMitData(string code, DateTime? start, DateTime? end)
{
DataTable results = new DataTable();
string connectionString = ConfigurationManager.ConnectionStrings["asdf"].ConnectionString;
string userName = String.Empty;
try
{
using (SPSite site = new SPSite(ConfigurationManager.AppSettings["someName"]))
{
using (SPWeb web = site.OpenWeb())
{
userName = web.CurrentUser.Email.ToString();
}
}
using (SqlConnection connection1 = new SqlConnection(connectionString))
{
connection1.Open();
using (SqlCommand command1 = new SqlCommand("someStoredProcedure", connection1))
{
command1.Parameters.Add(new SqlParameter("#UserName", userName));
command1.Parameters.Add(new SqlParameter("#ProductCode", code));
SqlDataReader dr = command1.ExecuteReader(CommandBehavior.CloseConnection);
results.Load(dr);
}
connection1.Close();
}
}
catch (Exception ex)
{
}
return results;
}
Stored Procedure
#UserName nvarchar(256),
#ProductCode nvarchar(256),
#StartDate nvarchar(256) = '1/1/1900',
#EndDate nvarchar(256) = '12/30/2012'
AS
BEGIN
SET NOCOUNT ON;
Declare #UserID int
Select #UserID = Users.UserID
from Users
where Users.Email = #UserName
Try making sure that the command type is set to stored procedure.
mycommand.CommandType = System.Data.CommandType.StoredProcedure;
You will get this exception if the value of your 'userName' variable is null
If null is valid, then pass 'DBNull.Value' to the db instead:
command1.Parameters.Add(new SqlParameter("#UserName", (userName ?? DBNull.Value));
Command1.CommandType = System.Data.CommandType.StoredProcedure
This will force the ExecuteReader to perform the exec instead of just trying it as a flat command.
By default, the CommandText property needs to contain a complete SQL command, not just the name of the stored procedure.
You can change this by to set the SqlCommand's CommandType property to StoredProcedure.
Alternatively, you could explicitly pass the parameters, by changing the CommandText to "someStoredProcedure #UserName, #ProductCode"; this is a complete SQL statement and will work with the default CommandType of Text.
EDIT: I just tried it, and the only way to get that error message without setting CommandType to StoredProcedure (which he did not do) is if CommandText is EXEC someStoredProcedure. Passing a null parameter gives a different error.
I am using a bacgroundworker in my C# Winforms app to call a stored procedure in SQL Server. The reason I am doing this is that I am using raiserror messages to send back rows counts and messages to a progress bar that shows the progress of the stored procedure. That all works fine. My problem is at the end of the stored procedure I have a message that returns a success message. The success message is declared an an output parameter in the stored procedure.
DECLARE #returnMessage OUTPUT
Then I say something like:
SET #returnMessage = "Posting dues has successfully completed on ' + GetDate()
Now in my app, I have my DoWork method where I connect and execute my stored procedure. Here is the pertinent code:
conn.InfoMessage += (o, args) => self.ReportProgress(0, args.Message);
SqlCommand cmd = new SqlCommand("ua_sp_PostPeriodicDues", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandTimeout = 0;
cmd.Parameters.Add(new SqlParameter("#periodCode", periodCode));
cmd.Parameters.Add(new SqlParameter("#nextPostingDate", nextPostingDate));
cmd.Parameters.Add(new SqlParameter("#repost", repost));
cmd.Parameters.Add(new SqlParameter("#localFieldName", localFieldName));
cmd.Parameters.Add(new SqlParameter("#userCd", userCd));
cmd.Parameters.Add(new SqlParameter("#runNow", runNow));
cmd.Parameters.Add(new SqlParameter("#returnMessage", returnMessage)).Direction = ParameterDirection.Output;
cmd.ExecuteNonQuery();
returnMessage = cmd.Parameters["#returnMessage"].Value.ToString();
returnMessage is a string that is defined at the class level so it is available to the RunWorkerCompleted method.
In my RunWorkerCompleted method I have the following lines.
form.Kill(); //closes progress bar modeless dialog
Cursor.Current = Cursors.Default;
MessageBox.Show(returnMessage); //display the success
Problem is the returnMessage variable always returns an empty string. I am just using an output parameter in the stored procedure with no return. Does my output parameter have to be handled differently on the C# side because it is in a backgroundworker? Is there a better way to return back a success at the end of the stored procedure. Is doing with a backgroundworker causing the problem?
I would break it down and add the Output Paramter and declare the datatype as well as follows:
SqlParameter returnMessage = new SqlParameter("#returnMessage", SqlDbType.String);
returnMessage.Direction = ParameterDirection.ReturnValue;
cmd.Parameters.Add(returnMessage);
cmd.Open();
cmd.ExecuteNonQuery();
var result = returnParameter.Value;
-Please change this code
SET #returnMessage = 'Posting dues has successfully completed on ' + GetDate()
to
SET #returnMessage = 'Posting dues has successfully completed on ' + GetDate()
DECLARE #returnMessage VARCHAR(100) OUTPUT
is not valid TSQL. This should return an error.
OUTPUT parameters have to be actual parameters of the stored procedure. You can't declare them inside the procedure scope and try to return them from there.
Make the output parameter part of the SP definition and it should work.
You say it's working in the SQL Server query analyzer, but not in (I assume) production. The likely culprit is a schema issue. You are referencing your stored procedure without schema-qualifying the name. Reference to SQL objects that are not schame-qualified are looked up in the following manner:
First the current connection's default schema is probed for an object matching the referenced name. If found, the reference is resolved. Otherwise...
The dbo schema is probed for an object matching the referenced name.
It is possible to have multiple objects of the same name that are owned by different schemas, with the result that different logins (attached to different default schemas) can (and will) resolve such unqualified references differently. With the odd results you seem to be seeing.
Unless you have a very good reason not to do so, always schema-qualify your object references and SQL create statements.
That being said, given this stored procedure:
create procedure dbo.mySproc
#p1 varchar(32) ,
#p2 varchar(8000) output
as
set #p2 = case
when #p1 is null then 'Hello, <null>!'
else 'Hello, ' + #p1
end
return 0
go
This code should do you:
using ( SqlConnection connection = new SqlConnection(connectString) )
using( SqlCommand cmd = connection.CreateCommand() )
{
cmd.CommandText = "dbo.mySproc" ;
cmd.CommandType = CommandType.StoredProcedure ;
cmd.Parameters.AddWithValue( "#p1" , "Emily" ) ;
cmd.Parameters.Add( "#p2" , SqlDbType.VarChar , 8000 );
cmd.Parameters["#p2"].Direction = ParameterDirection.Output ;
connection.Open();
int rc = cmd.ExecuteNonQuery();
connection.Close();
string message = (string) cmd.Parameters["#p2"].Value ;
Console.WriteLine( "The message is: {0}",message);
}
And should produce
The message is: Hello, Emily
Attempted (non-working) solution included below.
I have an sql function called get_parameter which looks in a table for a given string and returns the associated string:
declare #str varchar(20);
set #str = dbo.get_parameter('filecount')
print #str
It works! I run this and it prints out exactly what it should print. In this case, the parameter is the string '44'.
Now I want to run a C# CLR. But I want the CLR to be able to look up the parameter that it needs.
[SqlFunction(DataAccess = DataAccessKind.Read)]
public static string Import_TestFunc()
{
using (SqlConnection conn = new SqlConnection("context connection=true"))
{
SqlCommand command = new SqlCommand();
command.Connection = conn;
conn.Open();
// Find out how many files DSAMS processing requires.
command.CommandText = #"EXEC get_parameter 'filecount' ";
string cnt = (string)command.ExecuteScalar();
if (String.IsNullOrEmpty(cnt))
{
return "'cnt' not found."; // error return code. can't find this parameter.
}
return cnt;
}
}
However, this does not work. It constantly thinks the value for cnt is null (or empty) when it returns from get_parameter.
As requested, the code for get_parameter
ALTER FUNCTION [dbo].[get_parameter]
(
#SelectedParameterName nvarchar(max)
)
RETURNS nvarchar(max)
AS
BEGIN
DECLARE #result nvarchar(max);
SET #result = (SELECT ParameterValue from Parameters WHERE ParameterName = #SelectedParameterName);
RETURN isnull(#result,'');
END
I have tried the solution as per Mike Dinescu below, but it problem is that the call to ExecuteScalar() still returns a null. I did try to change to CommandType.Text and in that case I get the following interesting message:
A .NET Framework error occurred during execution of user-defined routine or aggregate "Import_TestFunc":
System.Data.SqlClient.SqlException: Procedure or function 'get_parameter' expects parameter '#SelectedParameterName', which was not supplied.
This is interesting, because I'm looking right at where it adds the parameter #SelectedParameterName.
command.Parameters.Add(new SqlParameter("#SelectedParameterName", SqlDbType.NVarChar )).Value = "filecount";
If you want to execute a user-defined function, or stored procedure from .NET, you should set the CommandType to CommandType.StoredProcedure, and add the needed parameters to the command object before executing the command.
command.CommandType = CommandType.StoredProcedure;
command.CommandText = #"dbo.get_parameter";
// here you add the paramters needed for your procedure/function
// in your case it will be just one (make sure you add the correct name for you function)
command.Parameters.Add(new SqlParamter("SelectedParameterName", SqlDbType.NVarChar));
command.Prepare();
command.Parameters[0].Value = "filecount";
string cnt = (string)command.ExecuteScalar();
I am trying to INSERT my data into two tables Services and Service-line
Explaining my stored procedure:
It checks for existing ID in the Form
If not found it creates a new record in the Services table the info in the Form, then gets the ID using scope_identity and uses the ID for the Service-Line table.
Also the ID is returned to the Form and stays tre.
Later while inserting second record in Service-Line, the stored procedure checks for existing ID; if found, this time, it takes the ID from the Form and uses it in the Service-Line
This Is my stored procedure
please bear with me, as I am working in this code and testing, a lot of line are commented out
ALTER PROCEDURE [dbo].[InsertServiceServiceLine] (
--Services Entry
--FOR IF CONDITION ---CHECK THE DEFAULT VALUE IN THE FORM on SID
#ExistingSID int,
--SEELCT PARAMETES DEF VALUES
#ComboBoxSelectedBike varchar(100),
-- INPUT PARAMETERES FOR NEW RECORD
#CID int,
#Status bit = 1,
#CurrentMeter int,
#Labor decimal(20,0),
#GrandTotal decimal(20,0) = ISNULL,
--#NextService datetime,
--Service Line
#Spare nvarchar(500),
#Quantity int,
#Uprice decimal(20,2),
#Subtotal decimal(20,2)
)
AS
BEGIN
IF (#ExistingSID <= 0)
BEGIN
SET NOCOUNT ON;
DECLARE #BikeID int
SELECT #BikeID = (SELECT BikeID FROM TblBikeNames WHERE BikeName = #ComboBoxSelectedBike)
INSERT INTO [AutoDB_Sample].[dbo].[TblServices]
(CID,BikeID,Status,CurrentMeter,Labor,DateOfService)
VALUES
(#CID,#BikeID,#Status,#CurrentMeter,#Labor,GETDATE())
DECLARE #SID int
SET #SID = SCOPE_IDENTITY()
INSERT INTO [AutoDB_Sample].[dbo].[TblServiceLine]
(SID,Spare,Quantity,Uprice,Subtotal,DateCreated)
VALUES
(#SID,#Spare,#Quantity,#Uprice,#Subtotal,GETDATE())
RETURN #SID
END
ELSE
BEGIN
INSERT INTO [AutoDB_Sample].[dbo].[TblServiceLine]
(SID,Spare,Quantity,Uprice,Subtotal,DateCreated)
VALUES
(#ExistingSID,#Spare,#Quantity,#Uprice,#Subtotal,GETDATE())
END
END
It gives me an error when I use this stored procedure in a C# Windows Forms
Procedure or Function has too many Arguments
Here's a screenshot of the error
I thought that Putting my login in SP would be great and improve the performance of my application. But now am stuck.
This is my C# Code
public void AddItemIntoServices_ServiceLine()
{
ConnectionStringSettings consetting = ConfigurationManager.ConnectionStrings["AutoDB"];
String ConnectionString = consetting.ConnectionString;
SqlConnection con = new SqlConnection(ConnectionString);
try
{
con.Open(); // open the connection
// Specify the name of the Stored Procedure you will call
String SP_Name = "InsertServiceServiceLine";
// Create the SQL Command object and specify that this is a SP.
SqlCommand cmd = new SqlCommand(SP_Name, con);
cmd.CommandType = CommandType.StoredProcedure;
// Specify values for the input parameters of our Stored Procedure
// Parameters MUST be named the same as the parameters defined in the Stored Procedure.
//~~ If Condition Parameter ****************************************************************************~~//
int exitstingSID;
if (int.TryParse(LblSID_Data.Text, out exitstingSID)) ;
SqlParameter ExistingSID = new SqlParameter("#ExistingSID", exitstingSID);
ExistingSID.Direction = ParameterDirection.Input;
ExistingSID.DbType = DbType.Int16;
cmd.Parameters.Add(ExistingSID);
//Parameter to select Bike ID from Selected Bike Name
SqlParameter ParamBikeID = new SqlParameter("#ComboBoxSelectedBike", ComboBx_BikeNames.Text);
ParamBikeID.Direction = ParameterDirection.Input;
ParamBikeID.DbType = DbType.String;
cmd.Parameters.Add(ParamBikeID);
//~~ Customer Info ************************************************************************************~~//
//CID Convertion
int P_CID;
if (int.TryParse(LblCID_Data.Text, out P_CID)) ;
cmd.Parameters.AddWithValue("#CID", P_CID);
cmd.Parameters.AddWithValue("#Cname", this.TxtBx_CustomerName.Text);
cmd.Parameters.AddWithValue("#Vnum", this.TxtBx_VehicleNumber.Text);
//~~ Service Info ************************************************************************************~~//
//Labor Convertion
int Laborint;
if (int.TryParse(TxtBxLabor.Text, out Laborint)) ;
SqlParameter ParamLabor = new SqlParameter("#Labor", Laborint);
ParamLabor.Direction = ParameterDirection.Input;
ParamLabor.DbType = DbType.Int16;
cmd.Parameters.Add(ParamLabor);
//CurrentMeterConversion
int currentMeterint;
if (int.TryParse(TxtBx_CurrentMeter.Text, out currentMeterint)) ;
SqlParameter ParamCurrentMeter = new SqlParameter("#CurrentMeter", currentMeterint);
ParamCurrentMeter.Direction = ParameterDirection.Input;
ParamCurrentMeter.DbType = DbType.Int16;
cmd.Parameters.Add(ParamCurrentMeter);
//Return Value
SqlParameter ParamReturn = new SqlParameter("#SID", SqlDbType.Int);
ParamReturn.Direction = ParameterDirection.Output;
ParamReturn.DbType = DbType.Int16;
cmd.Parameters.Add(ParamReturn);
//~~ Service Info ************************************************************************************~~//
//Converstions
Decimal UP, ST;
if (Decimal.TryParse(TxtBx_UnitPrice.Text, out UP)) ;
if (Decimal.TryParse(TxtBxTotal.Text, out ST)) ;
//SpareName
cmd.Parameters.AddWithValue("#Spare", ComboBx_SparesName.Text);
//Quantity
cmd.Parameters.AddWithValue("#Qty", NumericBx_Quantity.Value);
//Unit Price
SqlParameter ParamUp = new SqlParameter("#Uprice", UP);
ParamUp.Direction = ParameterDirection.Input;
ParamUp.DbType = DbType.Decimal;
cmd.Parameters.Add(ParamUp);
//Total
SqlParameter ParamTot = new SqlParameter("Subtotal", ST);
ParamTot.Direction = ParameterDirection.Input;
ParamTot.DbType = DbType.Decimal;
cmd.Parameters.Add(ParamTot);
cmd.ExecuteNonQuery();
String _returnedSID = cmd.Parameters["#SID"].Value.ToString();
LblSID_Data.Text = _returnedSID;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
con.Close();
clear();
ToolStripLable_Status.Text = "New Service Record Created";
}
}
The list of parameters passed through the command collection should match exactly what the stored procedure expects by name, type and direction.
Your list of parameters included in the command collection doesn't match with the parameters required by the stored procedure and thus you get the error.
At first sight I could say:
cmd.Parameters.AddWithValue("#Cname", this.TxtBx_CustomerName.Text);
cmd.Parameters.AddWithValue("#Vnum", this.TxtBx_VehicleNumber.Text);
SqlParameter ParamReturn = new SqlParameter("#SID", SqlDbType.Int);
are parameters added to the command collection but are not present in the stored procedure list of parameters.
On the contrary we have the parameters #status bit and #GrandTotal decimal(20,0) = ISNULL, required by the stored procedure but not present in the list. (By The way, the default syntax for a NULL parameter is #GrandTotal decimal(20,0) = NULL
Finally the stored procedure names a parameter #Quantity but you add a parameter named #Qty.
Now there is also the problem of the parameter types that should match otherwise you risk an automatic conversion at best or a error message stating about type mismatches.
You have numerous parameter of type int but you pass parameters of type Int16 while the correct type is Int32. And there is also a more clear error in the type of the parameter #Labor that is expected to be a decimal by the sp but you add it as an integer (16)
You have 12 parameters in the stored procedure definition (if I counted correctly), however, your code may have more or less than that. There is either one too many parameters defined within the code, or the stored procedure expects a 13th parameter. I think the error mentions the former, but I sometimes get them confused.
Anyway that error is always due to a mismatch in the number of parameters, and make sure the direction (input/output) is setup correctly, and everything is typed OK.
I hope this is not one of those questions where I slap myself afterwards, but this is really confusing me. I have this working for another one of my stored procedures which is why this is so confusing. It's basically the same setup in both. Here's what's happening.
Here's an example of my stored procedure:
ALTER PROCEDURE [dbo].[CreateRecord]
-- Add the parameters for the stored procedure here
#Link1Id INT = NULL,
#Link2Id INT = NULL,
#Amount MONEY,
#Output int out
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SET #Output = 0
-- Insert statements for procedure here
IF #Link1Id = NULL
BEGIN
IF NOT EXISTS(SELECT * FROM dbo.Records WHERE Link2Id = #Link2Id)
INSERT INTO [dbo].[Records]
([Link1Id]
,[Link2Id])
VALUES
(#Link1Id
,#Link2Id)
SET #Output = (SELECT RecordId FROM dbo.Records WHERE Link2Id = #Link2Id)
END
ELSE
BEGIN
IF NOT EXISTS(SELECT * FROM dbo.Records WHERE Link1Id = #Link1Id)
INSERT INTO [dbo].[Records]
([Link1Id]
,[Link2Id])
VALUES
(#Link1Id
,#Link2Id)
SET #Output = (SELECT RecordId FROM dbo.Records WHERE Link1Id = #Link1Id)
END
END
Now, I have created a unit test that basically runs this procedure, and tries to Assert that the returned #Output is greater than 0, but the #Output parameter never has a value on the SqlCommand in the code. Here's some of the C# code:
private int ExecuteNonQueryWithOutput(string procedureName, SqlParameter[] parameters)
{
SqlCommand command = this.GenerateCommand(procedureName, parameters);
connection.Open();
command.ExecuteNonQuery();
int retval = (int)command.Parameters[OUTPUT].Value;
connection.Close();
return retval;
}
Now, I can step over the line that calls ExecuteNonQuery(), and verify in the database that the new (and correct) record is there, but then on the next line, it throws an exception when it calls (int)command.Parameters[OUTPUT].Value; as the Value is not there.
This is working perfectly for another procedure that I have which is setup in the same exact fashion. Do you know why it wouldn't be working here?
Thanks, I'm kind of stumped. I've debugged for a while now with no luck.
Edit:
Code that generates the parameters array:
List<SqlParameter> parameters = new List<SqlParameter>();
parameters.Add(new SqlParameter { ParameterName = "#Link1Id", SqlDbType = SqlDbType.Int, Direction = ParameterDirection.Input, Value = link1Val });
parameters.Add(new SqlParameter { ParameterName = "#Link2Id", SqlDbType = SqlDbType.Int, Direction = ParameterDirection.Input, Value = link2Val });
parameters.Add(new SqlParameter { ParameterName = OUTPUT, SqlDbType = SqlDbType.Int, Direction = ParameterDirection.Output });
return this.ExecuteNonQueryWithOutput("CreateRecord", parameters.ToArray());
I don't see where you've declared #Output. Did you mean:
ALTER PROCEDURE [dbo].[CreateRecord]
-- Add the parameters for the stored procedure here
#Link1Id INT = NULL,
#Link2Id INT = NULL,
#Amount MONEY,
#Output INT = NULL OUTPUT
AS
Also I'm not 100% sure you have the syntax right for retrieving a named output parameter. But the parameter has to exist before you can reference it anyway. How did you save that stored procedure without declaring #Output?
There are numerous things wrong with the code that go beyond the output parameter issue.
To answer the actual question, you are likely passing a NULL value back as the Output. When it tries to convert this to an Int you are getting an error.
Also the sql line:
IF #Link1ID = null
Will ALWAYS fail. In SQL parlance, null is an indeterminate value, so (null != null). The way to test for null values is to use IS. For example:
IF (#Link1ID is null)
Which leads me to believe that you are actually getting a primary key violation in the sql code.
Now, onto the bigger issue. Your C# code is flawed. The command object is never disposed of and if there are any issues your connection object won't be disposed of either. This will lead to fun sql errors due running out of available sql connections..
It should look something like the following:
private int ExecuteNonQueryWithOutput(string procedureName, SqlParameter[] parameters)
{
int retval = 0;
using (SqlConnection conn = new SqlConnection("connection string here"))
using (SqlCommand command = this.GenerateCommand(procedureName, parameters)) {
connection.Open();
command.ExecuteNonQuery();
retval = (int)command.Parameters[OUTPUT].Value;
}
return retval;
}
Note that this declares, uses and disposes of your connection and command objects locally. If there is a problem this will make sure the resources are properly disposed of.
Also note that it does not use a global "connection" object. Connection pooling offered by the operating system is incredibly efficient at opening/closing connections as needed. Because of this the best practice is to instantiate and keep them around only long enough to deal with the current operation. The longer it's open the more likely you'll run into issues.