I've written a query parser that should create a SqlCommand and execute a stored procedure. The query for the stored procedure can come in many forms, including this one:
exec dbo.sp_StoredProcedureName 1599800
In this case, I create the SqlParameter this way:
var param = new SqlParameter() { Value = paramValue };
I get an error stating that '#Parameter1' is not a parameter for procedure sp_StoredProcedureName.
Is there a way I can do this without running it as a standard query? I'd like to keep it consistent and build the SqlCommand as a StoredProcedure type if possible.
I was thinking maybe I could reflect the parameter names of the stored proc first, but wondering if there's another approach.
Whilst there is a constructor for SQLParameter which doesn't set the name, you can't actually use a SQLParameter without setting the ParameterName property.
From MSDN (emphasis mine) :
The ParameterName is specified in the form #paramname. You must set
ParameterName before executing a SqlCommand that relies on
parameters.
Within your SP the parameter will have a name, look at the query's definition to find it.
If it's not known in advance what the name is going to be, try querying the sys.parameters table to find out what parameters a particular stored procedure takes.
It's better to call stored procedures specifying which parameter is which anyway (especially if there's more than one parameter):
exec dbo.sp_StoredProcedureName #myParam = 1599800
From C# you can add it by name once you know what your parameter is called:
cmd.Parameters.AddWithValue("#myParam", 1599800);
When you create a command of the type you specified in the question, this command gets converted to a dynamic SQL query which is somewhat similar to the following snippet:
EXECUTE sp_executesql 'exec dbo.sp_storedprocName #FirstParam=value, ...'
Here, #FirstName is name of the parameter you specify while adding it to the SqlCommand object. This type of query cannot be created without specifying names of parameters.
You can view the query that gets generated by ADO.NET using SQL Server profiler. It is a good practice to open SQL Server profiler and see how a query is interpreted, as it helps in avoiding common mistakes we do while writing queries in ADO.NET
Related
I am converting a winforms application from MS Access to SQLServer Express.
I have some code which is used throughout the application for updating the database, I pass in the table name, ID for the entry the fields to update and the values in an array... works ok with access
The code generates an SQL statememnt, which is passed as a query along with the values as parameters... an example output is
UPDATE userVersion SET lastUpdated=?, userId=?, userName=?, version=? WHERE userId = 1299
if I try run this on sqlserver, the system crashes with this error
System.Data.SqlClient.SqlException: 'Incorrect syntax near '?'.
Incorrect syntax near '?'.'
What is the correct format for sql server?
Based on the error you are using SQLClient to execute the query, but the structure of the query looks like it may be closer to ODBC's command.
In case it helps, System.Data.ODBC.ODBCCommand uses the "?" symbol as a place holder for parameters. The order of the parameters is important in this case.
https://msdn.microsoft.com/en-us/library/system.data.odbc.odbccommand.parameters(v=vs.110).aspx
The System.Data.SQLClient.SQLCommand uses named variables (for example #MyVariable) to specify variables. In this case, order is not important as long as the names are specified correctly.
https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.parameters(v=vs.110).aspx
ODBCCommand would look like:
UPDATE Sales.Store SET Demographics = #demographics WHERE CustomerID = ?;
While SQLCommand would look like:
UPDATE Sales.Store SET Demographics = #demographics WHERE CustomerID = #ID;
Is there any way to intercept the SQL that's generated by SqlCommand?
I currently have a method that will execute a stored procedure:
public int ExecSP(string spName, params objec[] params)
{
using (SqlConnection con = new SqlConnection("AdventureWorks"))
using (SqlCommand cmd = new SqlCommand(spName, con))
{
cmd.CommandType = CommandType.StoredProcedure;
//..calls method to add params and values to cmd object
con.Open();
return cmd.ExecuteNonQuery();
}
}
When I use this to call the following:
ExecSP("HumanResources.uspUpdateEmployeePersonalInfo", 1, "295847284", new DateTime(1963, 3, 2), "S", "M");`
I get the following in SQLProfiler:
exec HumanResources.uspUpdateEmployeePersonalInfo #BusinessEntityID=1,#NationalIDNumber=N'295847284',#BirthDate='1963-03-02 00:00:00',#MaritalStatus=N'S',#Gender=N'M'
What I would like to do is intercept that SQL command so that I may add a comment to the end of it that will contain some pertinent information so that it looks like:
exec HumanResources.uspUpdateEmployeePersonalInfo #BusinessEntityID=1,#NationalIDNumber=N'295847284',#BirthDate='1963-03-02 00:00:00',#MaritalStatus=N'S',#Gender=N'M' -- IMPORTANT INFORMATION HERE
I can't change the CommandType to Text and I can't add an extra parameter to the stored procedure. I tried looking at these other questions but had no luck:
Can I override SqlCommand functions?
SqlCommand to T-SQL
In the case of CommandType = CommandType.StoredProcedure, this isn't possible.
Why? The SQL you see in SQL Profiler isn't generated by the client. :-/
When SqlCommand executes a CommandType.StoredProcedure command, it sends SQL Server an execute remote procedure call message with the name of the stored procedure and a data structure containing the parameters.
The TextData SQL Profiler displays for the RPC:Starting/RPC:Completed events is generated server-side. Since this text isn't generated client-side, it's not possible to modify it client-side.
What you're seeing is a debug/profile message, but that doesn't perfectly represent what was actually executed (the database will use sp_executesql() behind the scenes).
What you are looking for doesn't exist. The whole point of using parameter objects is that parameter values are NEVER, at any time, included as part of the sql command string, even on the server.
This accomplishes three main things:
It prevents any possibility of sql injection attacks via that query. You'll never have a problem with some weird unicode character set problem allowing an attacker to insert a command into the data for your query, like they did a couple years back with php/mysql, or some new language feature creating a situation where your old escape code wasn't good enough. The user-data portion of a query is always separated from the command logic portion of the query at every stage.
It improves performance, by allowing the server to cache the execution plan. This saves compile steps as your application executes what may be the same query over and over, just with different parameter data.
It avoids problems with formatting for things like dates, text data with apostrophes, and the like.
If you want to get debugging data, write a method to output the query and the parameter data that goes with it. If you want to add info the your profile trace, you can switch to CommandType.Text and call the stored procedure via your own EXEC procedurename #param1, #param2, ... #paramN -- INFO HERE string.
I use C# and I instantiate a DbCommand in order to execute an Oracle stored procedure.
My question is: why does the procedure receive the value through a different named parameter than the on in db?
When I add a parameter to the dbCommand:
...
string value = "Whatever"
db.AddInParameter(dbCommand,"WrongParamName",DbType.String);
db.SetParameterValue(dbCommand, "WrongParamName", value);
and I execute:
dataSet = db.ExecuteDataSet(dbCommand);
It will pass the dbCommand parameter to the stored procedure parameter correctly.
Why is that?
Does it set the value to the first parameter without a value or is it based on position?
If it's based on position why do we need the name for?
Is the name only to help the dev understand the code?
I need to know if you execute the stored procedure like "EXEC sp_StoredProcedure #Param1 = #Param1, #Param2 = #Param2" or "EXEC sp_StoredProcedure #Param1, #Param2". In the first case, if you change the order of parameters in the sp, the call won't be affected. In the second case on the other hand, it does, because first parameter from the command fulfill the first parameter from the sp, the second to second and so on.
If you cannot obtain a list of parameter names you can at least ask the other developer to not change the order and add parameters only to the end of the parameter list in stored procedure.
If you cannot do this either, you have nothing else to do then to pray not to change them. With some scripts, you can determine the list of parameters though (see this How to get stored procedure parameters details?). You can execute this command like an ordinary select statement.
After further investigation it seems that dbCommand passes parameters by order, not by name because this is how dbCommand is supposed to behave.
I did not find the purpose of the name, other than it only helps the developer to know which parameter is which.
I also did not find any property in dbCommand to set BindByName (a property in OracleCommand).
This question already has answers here:
When executing a stored procedure, what is the benefit of using CommandType.StoredProcedure versus using CommandType.Text?
(4 answers)
Closed 8 years ago.
Question: What is the difference between using a standard SQLCommand and SQLCommand.ComandType = StoredProcedure?
Since I'm not sure if parameters are passed to the command object by name or by order, I prefer this:
SqlCommand oCmd = new SqlCommand("exec sp_StoredProcedure #Param1, #Param2, #Param3", oDBConnection);
oCmd.Parameters.Add("Param1", SqlDbType.Bit).Value = var_param1;
oCmd.Parameters.Add("Param2", SqlDbType.NVarChar).Value = var_param2;
oCmd.Parameters.Add("Param3", SqlDbType.NVarChar).Value = var_param3;
rather than
SqlCommand oCmd = new SqlCommand("sp_StoredProcedure", oDBConnection);
oCmd.CommandType = StoredProcedure;
oCmd.Parameters.Add("Param1", SqlDbType.Bit).Value = var_param1;
oCmd.Parameters.Add("Param2", SqlDbType.NVarChar).Value = var_param2;
oCmd.Parameters.Add("Param3", SqlDbType.NVarChar).Value = var_param3;
//Do the parameter names and the parameter order matter here?
I don't understand why I should do the second?
The first is a completely redundant step, that forces a second (but trivial) query-plan to be parsed, generated, cached and executed. It also offers great opportunity to mess up by (for example) forgetting to add the parameters. You also need to consider that the parameters in the first are now passed by position (in the inner TSQL), where-as in the second they are passed by name; by name is usually preferable here. Likewise, if you add a new parameter to oCmd.Parameters you now have an extra maintenance step of maintaining the inner command - or risk introducing bugs, where-as in the second example you don't need to do anything extra.
Basically, the first example has nothing at all positive, and lots of negative points.
Re pass-by-name versus pass-by-position, this is basically a feature of the exec keyword in TSQL. There are two uses:
exec MyProc 'abc', 123
or
exec MyProc #foo='abc', #bar=123
The first is by-position; 'abc' is passed to the first declared parameter of MyProc, and 123 is passed to the second declared parameter of MyProc. Any additional parameters assume their default values if they have one.
The second is by-name; 'abc' is passed to the parameter of MyProc called #foo, and 123 is passed to the parameter of MyProc called #bar. Any other parameters assume their default values if they have one.
So in your specific example:
exec sp_StoredProcedure #Param1, #Param2, #Param3
is pass-by-position, and:
exec sp_StoredProcedure #Param1=#Param1, #Param2=#Param2, #Param3=#Param3
is bass-by-name.
As stated on the microsoft site:
...the names of the parameters added to the Parameters collection must match the names of the parameter markers in the stored procedure.
So if I use the same code with CommandType = StoredProcedure for different stored procedures, then I need to make sure for each stored procedure possibly executed, that it gets only those parameters in the parameter collection that are named the same as in the stored procedure to be executed.
The c# code below is self explanatory. I want to pass a DataTable as a table valued parameter to a stored procedure in SQL Server 2008 through WebMatrix.Data.Database.Query but in the last line of the c# code below(i.e. appDatabase.Query) an exception is thrown
"The table type parameter '0' must have a valid type name."
public JsonResult GetUserListForRoles(int[] RoleIds)
{
using (Database appDatabase = Database.Open("DefaultConnection"))
{
DataTable roleIdsTable = new DataTable();
roleIdsTable.Columns.Add("RoleId", typeof(int));
foreach(int role in RoleIds)
{
roleIdsTable.Rows.Add(role);
}
List<UserNameIdModel> userNameIdModelList = new List<UserNameIdModel>();
var userNameIdList = appDatabase.Query("EXEC dbo.sp_GetUserListForRoles #roleIdsList=#0", roleIdsTable);
In the Database i have created a User defined table type using this bit of TSQL
CREATE TYPE dbo.RoleIdsList AS TABLE (RoleId int)
it executed fine and shows up under Types/User-Defined Table Type in SQL Server Management Studio
This is the stored proc(Only adding the relevant portion to show that i have assigned the right type to the #roleIdsList parameter and also marked it as READONLY as is required for Table Valued parameters)
ALTER PROCEDURE [dbo].[sp_GetUserListForRoles]
-- Add the parameters for the stored procedure here
#roleIdsList dbo.RoleIdsList READONLY
I have done a good deal of search but could not find the solutions that deals with WebMatrix.Data.Database... All the solutions i found involved SqlCommand and SqlParameter. I have used WebMatrix.Data.Database through out my app and need to be consistent.
PLEASE NOTE: i do not use the WebMatrix IDE. I just use this namespace in my controllers for DB interaction in my MVC4 app.
Eagerly awaiting for some guidance from someone having expertise in this. Thanks a lot in advance.
According to the documentation you must specify a type name for the table-valued parameter by using the TypeName property of a SqlParameter. The WebMatrix.Data.Database helper doesn't provide an API for specifying any aspect of a parameter except its value. It hasn't been designed for use with stored procedures - it was initially designed for use with plain SQL only. Therefore you have no other option but to use plain ADO.NET in this instance so you can set the required properties of the SqlParameter yourself.