Execute Stored Procedure With Nameless Parameters - c#

I was just wondering if there is a way to execute a stored procedure with out naming the parameters. Meaning that C# resolves the parameters in the order they're declared within the stored procedure.
public static DataTable GetRelatedResources(string StoredProcedure, object[] Parameters)
{
var Results = new DataTable();
try
{
using (SqlConnection conn = new SqlConnection())
{
using (SqlCommand cmd = new SqlCommand(ConfigurationManager.ConnectionStrings["MK3Entities"].ConnectionString))
{
conn.Open();
cmd.Connection = conn;
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = StoredProcedure;
if (Parameters!= null)
{
foreach(var Param in Parameters)
{
// I Want To Do something like this
cmd.Parameters.AddWithValue(Param);
}
}
SqlDataAdapter adapter = new SqlDataAdapter(cmd);
adapter.Fill(Results);
}
}
}
catch (Exception ex)
{
MMSLogger.Instance.WriteToLog("Exception Executing Stored Procedure:" + ex.Message);
}
return Results;
}

Execute a command instead, and pass in parameters '#p1', '#p2' etc:
cmd.CommandType = CommandType.Text;
cmd.CommandText = 'exec ' + StoredProcedure;
int i=0;
string comma = ' ';
foreach(var Param in Parameters)
{
var paramName = String.Format("#P{0}", i);
cmd.CommandText += comma + paramName;
cmd.Parameters.AddWithValue(paramName, Param);
++i;
comma = ', ';
}
Be aware that AddwithValue is huge performance antipattern. See How Data Access Code Affects Database Performance

As per MSDN:
The ParameterName is specified in the form #paramname. You must set
ParameterName before executing a SqlCommand that relies on parameters.
Another MSDN article for SqlCommand:
Nameless, also called ordinal, parameters are not supported by the
.NET Framework Data Provider for SQL Server.
So answer is no, there is no way to execute a stored procedure without naming the parameters.

Do you mean some thing like derived parameter. This MSDN article gave an overview:
Parameters can also be derived from a stored procedure using the
DbCommandBuilder class. Both the SqlCommandBuilder and
OleDbCommandBuilder classes provide a static method, DeriveParameters,
which automatically populates the parameters collection of a command
object that uses parameter information from a stored procedure. Note
that DeriveParameters overwrites any existing parameter information
for the command.
A nother solution would be to get the parameter of the stored procedure from the sql-database and than set them in your code. But this need one extra query. To get the parameter use:
select * from dbo.parameters where specific_name='procedure-name'
But in any case, you have to use parameter-names.

Related

Call DB2 stored procedure from C#

After reading an interesting article online : Calling DB2 stored procedures from .NET applications
I'd like to share an issue recently encountered with a derived code :
DateTime transa_date = DateTime.ParseExact(trandate, "yyyy-MM-dd",
CultureInfo.InvariantCulture);
DB2Connection conn = new DB2Connection(MyDb2ConnectionString);
conn.Open();
try
{
// MyDb2Connection.Open();
// conn.Open();
// assume a DB2Connection conn
DB2Transaction trans = conn.BeginTransaction();
cmd = conn.CreateCommand();
procName = "MYTBLSCHEMA.TEST";
procCall = "CALL MYTBLSCHEMA.TEST(#NAME, #ADDRESS_LINE, #REGNUM, #TRANSA)";
cmd.Transaction = trans;
cmd.CommandType = System.Data.CommandType.Text;
cmd.CommandText = procCall;
// Register input-output and output parameters for the DB2Command
cmd.Parameters.Add( new DB2Parameter("#NAME", name)); #of string type
cmd.Parameters.Add( new DB2Parameter("#ADDRESS_LINE", adr)); #of string type
cmd.Parameters.Add( new DB2Parameter("#REGNUM", reg)); #of string type
cmd.Parameters.Add( new DB2Parameter("#TRANSA", transa_date)); #of date type (in DB2 table)
// Call the stored procedure
Console.WriteLine(" Call stored procedure named " + procName);
cmd.ExecuteNonQuery();
}
The above code neither generates an exception at cmd.ExecuteNonQuery() nor inserts the (expected) row into the table.
Hence, a Hope to understand through this post the rationale underlying such phenomenon.
Thanks.
N.B: Executing (manually)
CALL MYTBLSCHEMA.TEST('test', 'test_address_', 'test_num', 2021-01-01)
from the IDE does work (e.g. insert the row into the table).
DB2 version: 11.5.6.0.00000.008
I'd either remove this line:
DB2Transaction trans = conn.BeginTransaction();
Or I'd add this line at the end of the try:
trans.Commit();
As to which you'd choose; as it's a single stored procedure, unless there's some internal overriding concern within the sproc that makes sense to have a transaction to be started outside it cover it, I'd remove it. If you have, or plan to have multiple operations that must either all-succeed or all-fail, then I'd keep it/commit it..

How do I use ExecuteScalar with a stored Procedure?

I'm trying to get a count of column records in a Sql database and show the result in a MessageBox.
This is my code:
public DataTable CheckIfNameExist(string name)
{
con = Connect();
cmd = new SqlCommand();
cmd.Connection = con;
cmd.CommandText = "spCheckIfNameExist";
cmd.Parameters.AddWithValue("#Name", SqlDbType.NVarChar).Value = name;
MessageBox.Show(name);
Int32 totalNames = (Int32) cmd.ExecuteScalar();
string tNames = totalNames.ToString();
MessageBox.Show(tNames);
}
And this is my sp:
#Name nvarchar(50) = null
As
Begin
SELECT COUNT(*) from OrdersSent where CustomerName LIKE #Name + '%'
End
Problem:
It always returns 0.
There are a couple of errors in your code:
You should write it as:
cmd.CommandText = "spCheckIfNameExist";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#Name", SqlDbType.NVarChar).Value = name;
First you need to tell the ADO engine that you are calling a stored procedure and not a simple command text, but you also need to use Add instead of AddWithValue to be precise on the type of the parameter passed to the SP. Your code creates a parameter int becase the second parameter of the AddWithValue is the Value of the parameter not the type.
You have a few problems in the c# code - the most important is probably this:
cmd.Parameters.AddWithValue("#Name", SqlDbType.NVarChar).Value = name;
Don't use AddWithValue. Use Add.
Also, you didn't specify the command type - the default is Text.
And you are using fields for SqlConnection and SqlCommand - which is also the wrong thing to do. You should create and dispose both of them inside each method you are using them.
A better version of your code would be this:
using(var con = new SqlConnection(ConnectionString))
{
using(var cmd = new SqlCommand("spCheckIfNameExist", con))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#Name", SqlDbType.NVarChar).Value = name;
con.Open();
var tNames = cmd.ExecuteScalar().ToString();
}
}
Another thing that puzzles me is why a method called CheckIfNameExist returns a DataTable. I would expect it to simply return a bool.
If you really only want to check if the name exists, you can do this better on both the SQL level and the c# level.
A better SQL would be something like this:
SELECT CAST(CASE WHEN EXISTS(
SELECT 1
FROM OrdersSent
WHERE CustomerName LIKE #Name + '%'
) THEN 1 ELSE 0 END AS bit)
And on the c# level, bit translates directly to bool, so the code can simple be this:
public bool CheckIfNameExist(string name)
{
using(var con = new SqlConnection(ConnectionString))
{
using(var cmd = new SqlCommand("spCheckIfNameExist", con))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#Name", SqlDbType.NVarChar).Value = name;
con.Open();
return (bool)cmd.ExecuteScalar();
}
}
}
And another note - you should avoid using the sp prefix for stored procedures.
Microsoft have reserved this prefix for built in system procedures.
For more information, read Aaron Bertrand's Is the sp_ prefix still a no-no?, where you'll see that the short answer to this question is "Yes".
The sp_ prefix does not mean what you think it does: most people think sp stands for "stored procedure" when in fact it means "special." Stored procedures (as well as tables and views) stored in master with an sp_ prefix are accessible from any database without a proper reference (assuming a local version does not exist). If the procedure is marked as a system object (using sp_MS_marksystemobject (an undocumented and unsupported system procedure that sets is_ms_shipped to 1), then the procedure in master will execute in the context of the calling database.
You need to specify the type of your command like this:
cmd.CommandText = "spCheckIfNameExist";
cmd.CommandType = CommandType.StoredProcedure;
See also:
What is the benefit of using CommandType.StoredProcedure versus using CommandType.Text?
Although specify the type directly and use the Value property is more better than AddWithValue:
cmd.Parameters.Add("#Name", SqlDbType.NVarChar).Value = name;
The following article could be also interesting:
https://blogs.msmvps.com/jcoehoorn/blog/2014/05/12/can-we-stop-using-addwithvalue-already/

Can't pass an array to oracle procedure

I've created a procedure for test this problem and it works right in oracle developer. There is a typed named "dizi" (array and varchar2). And procedure has input parameter. I'm trying to pass an array to this to this procedure as a parameter in c#. I've searched a lot but i couldn't solve the problem. The error is: "Not all veriables bound"
public void InsertQuestion(List<string> area_list)
{
quest_areas = area_list.ToArray();
command = new OracleCommand();
command.Connection = connect;
connect.Open();
var arry = command.Parameters.Add("area_array",OracleDbType.Varchar2);
arry.Direction = ParameterDirection.Input;
arry.Size = quest_areas.Length;
arry.CollectionType = OracleCollectionType.PLSQLAssociativeArray;
arry.Value = quest_areas;
command.BindByName = true;
command.CommandText ="TESTPROCEDURE(:area_array)";
command.CommandType = CommandType.StoredProcedure;
command.ExecuteNonQuery();
connect.Close();
}
Here is my procedure (it is just for test but i'll use something like that)
CREATE OR REPLACE PROCEDURE TESTPROCEDURE (t_in IN dizi)
IS
BEGIN
FOR i IN 1..t_in.count LOOP
dbms_output.put_line(t_in(i));
END LOOP;
END;
I've got code that successfully passes array down to oracle sprocs. Takes a slightly different approach to yours. Not entirely sure how much is relevant, but in case it helps my code:
uses the correct name parameter name (t_in in your case)
doesn't bother setting the size of the parameter
creates an object array that is the correct length and copies the contents across into it (ie from quest_areas in your case)
then sets this object array as the Value for the command parameter
doesn't use bind variables when calling the proc, rather just uses the proc name by itself as the CommandText.
That said, I suspect your problem might be around your use of a bind variable when calling the procedure. What happens if you just set 'TESTPROCEDURE' as the CommandText?
Or go the other way and put change it into a proper anonymous PLSQL block 'begin TESTPROCEDURE(:area_array); end;' and change the CommandType to CommandType.Text (as just suggested by Wernfried while I was typing...)
Update
public void InsertQuestion(List<string> area_list)
{
var input_array = area_list.Select(s => (object)s).ToArray();
command = new OracleCommand();
command.Connection = connect;
connect.Open();
var arry = command.Parameters.Add("area_array",OracleDbType.Varchar2);
arry.Direction = ParameterDirection.Input;
arry.CollectionType = OracleCollectionType.PLSQLAssociativeArray;
arry.Value = input_array;
command.CommandText ="TESTPROCEDURE";
command.CommandType = CommandType.StoredProcedure;
command.ExecuteNonQuery();
connect.Close();
}

C# Stored procedure or function expects parameter which is not supplied

I am fairly new to C# and I'm trying to set up call to a stored procedure in my database which takes one parameter.
I get the error "Procedure or function 'SP_getName' expects parameter '#username', which was not supplied. "
My Stored procedure works ok when I supply it with the parameter and I run it via SQL management studio.
GO
DECLARE #return_value int
EXEC #return_value = [dbo].[SP_getName]
#username = 'bob101'
SELECT 'Return Value' = #return_value
GO
However when I try and call it the error is with how I'm passing the parameter in, but I can't spot what the issue is.
//create a sql command object to hold the results of the query
SqlCommand cmd = new SqlCommand();
//and a reader to process the results
SqlDataReader reader;
//Instantiate return string
string returnValue = null;
//execute the stored procedure to return the results
cmd.CommandText = "SP_getName";
//set up the parameters for the stored procedure
cmd.Parameters.Add("#username", SqlDbType.NVarChar).Value = "bob101";
cmd.CommandType = CommandType.Text;
cmd.Connection = this.Connection;
// then call the reader to process the results
reader = cmd.ExecuteReader();
Any help in spotting my error would be greatly appreciated!
I've also tried looking at these two posts, but I haven't had any luck:
Stored procedure or function expects parameter which is not supplied
Procedure or function expects parameter, which was not supplied
Thanks!
You have stated:
cmd.CommandType = CommandType.Text;
Therefore you are simply executing:
SP_getName
Which works because it is the first statement in the batch, so you can call the procedure without EXECUTE, but you aren't actually including the parameter. Change it to
cmd.CommandType = CommandType.StoredProcedure;
Or you can change your CommandText to:
EXECUTE SP_getName #username;
As a side note you should Avoid using the prefix 'sp_' for your stored procedures
And a further side note would be to use using with IDisposable objects to ensure they are disposed of correctly:
using (var connection = new SqlConnection("ConnectionString"))
using (var cmd = new new SqlCommand("SP_getName", connection))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#username", SqlDbType.NVarChar).Value = "bob101";
connection.Open();
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
// Do something
}
}
}
I had this problem, but it wasn't about parameter name of Command Type.
My problem was that when C# calls SP, for each parameter that has no value passes 'default' keyword (i found it in SQL Profiler):
... #IsStop=0,#StopEndDate=default,#Satellite=0, ...
in my case my parameter Type was DateTime :
#StopEndDate datetime
. I Solved my problem by seting default value to this parameter in Stored Procedure :
#StopEndDate datetime=null
Try remove #:
cmd.Parameters.Add("username", SqlDbType.NVarChar).Value = "bob101";

Schema from stored procedure

I have a procedure, I want to read schema of the procedure. To retrieve view schema I use the query shown here. Same way I want to get schema of stored procedure. How to get it? Plz show some syntax.
public static DataTable SchemaReader(string tableName)
{
string sql = string.Format("Select * from {0}", tableName);
conn.Open();
SqlCommand cmd = new SqlCommand(sql, conn);
cmd.CommandType = CommandType.Text;
SqlDataReader reader = cmd.ExecuteReader();
DataTable schema = reader.GetSchemaTable();
reader.Close();
conn.Close();
return schema;
}
If have any query plz ask.Thanks in advance.
you could do
public static DataTable SchemaReader(string tableName)
{
string sql = "MySP";//replace this with your store procedure name
conn.Open();
SqlCommand cmd = new SqlCommand(sql, conn);
cmd.CommandType = CommandType.StoredProcedure;
SqlDataReader reader = cmd.ExecuteReader();
DataTable schema = reader.GetSchemaTable();
reader.Close();
conn.Close();
return schema;
}
Hope this help
This is an answer that does not call the SP - if you do, you may inadvertently affect data:
SELECT * FROM sys.dm_exec_describe_first_result_set ('owner.sprocName', NULL, 0) ;
This returns the result set :
is_hidden
column_ordinal
name
is_nullable
system_type_id
system_type_name
max_length
precision
scale
collation_name
user_type_id
user_type_database
user_type_schema
user_type_name
assembly_qualified_type_name
xml_collection_id
xml_collection_database
xml_collection_schema
xml_collection_name
is_xml_document
is_case_sensitive
is_fixed_length_clr_type
source_server
source_database
source_schema
source_table
source_column
is_identity_column
is_part_of_unique_key
is_updateable
is_computed_column
is_sparse_column_set
ordinal_in_order_by_list
order_by_is_descending
order_by_list_length
error_number
error_severity
error_state
error_message
error_type
error_type_desc
You could get information about a stored procedure's parameters but, without executing it, SQL Server cannot tell you the structure of the dataset(s) returned by the stored procedure. Since executing a stored procedure can have side effects, ADO.NET doesn't provide a method for telling you what the result set(s) would look like were the stored procedure to be executed. Furthermore, the result set(s) might change depending on the parameters passed to the procedure when it is executed.
I am not getting your question clearly I think this would work with you
Select *
from sys.objects
where type='p' and name = (procedure name)
Replace your query with this and it will work fine
I've created various code generators that use the output of stored procs. In my experience, most procedures that SELECT anything output their schema just the same if you call them with null (DbNull.Value) as the value for all parameters. You can get the parameter list itself from system views, though I find it convenient to use INFORMATION_SCHEMA.PARAMETERS.
By executing the procedure in a transaction and always rolling back you can safely execute stuff even when you have no idea what the procedure does.
You'll probably need a basic GUI and allow the user to modify the parameters - or a config file or some other way to provide parameter values for specific procedures. A stored proc may produce output with different schemas depending on the parameters, though I haven't seen many that do.
App.config
<appSettings>
<add key="Schema_Name" value ="[dev]."/> <!-- use any one [dev]. or [dbo]. -->
</appSettings>
c# read Key
string schema_Name = Configuration["Schema_Name"].ToString();
Store Procedure
SqlConnection objConn = new SqlConnection(Connection);
objConn.Open();
SqlCommand cmd = new SqlCommand("Exec WLTCVarification", objConn);
cmd.Parameters.Add("#SchemaName", SqlDbType.Text);
cmd.Parameters["#Schema_Name"].Value = schema_Name; // dev or dbo;
rowsAmount = (string)cmd.ExecuteScalar();
objConn.Close();
c# Sql Query
SqlConnection objConn = new SqlConnection(Connection);
objConn.Open();
SqlCommand cmd = new SqlCommand("select * from " + schema_Name + "receive_agv_onlyerror, objConn);
rowsAmount = (string)cmd.ExecuteScalar();
objConn.Close();

Categories