I've done some trouble shooting by taking out the parameters and replacing them with text, and the only parameter that is not working is #seat and I can't figure out why.
allSeats is an array of custom controls. I've tried replacing the parameter contents with an actual string ie. "A1" and that still doesn't work. If I remove the #seat parameter and replace it with A1, it works, but I need to be able to set the column name dynamically.
myConnection.Open();
SqlCommand myCommand = new SqlCommand("UPDATE Events SET #seat = #truefalse WHERE Name = #name", myConnection);
SqlParameter param = new SqlParameter();
SqlParameter param2 = new SqlParameter();
SqlParameter param3 = new SqlParameter();
param.ParameterName = "#seat";
param2.ParameterName = "#truefalse";
param2.DbType = System.Data.DbType.Boolean;
param3.ParameterName = "#name";
param.Value = allSeats[i].Name;
param2.Value = allSeats[i].taken;
param3.Value = name;
myCommand.Parameters.Add(param);
myCommand.Parameters.Add(param2);
myCommand.Parameters.Add(param3);
myCommand.ExecuteNonQuery();
Any help is appreciated. If I need to post more relevant code please let me know and I shall add it.
In your
SET #seat = #truefalse
part, you try to parameterize your column name. You can't do that. You only can parameterize your values, not column name or table names.
You can use dynamic SQL for such a case but it is not recommended. Read
The Curse and Blessings of Dynamic SQL
SELECT * FROM #tablename
As a recommendation, use a white list such a case. I hope, there can only be a fixed set of possible correct values for the column name. Of course, this requires strong validation in your inputs part.
Agree with Soner. Change the string before you create the command
string cmdStr = string.Format("UPDATE Events SET {0} = #truefalse WHERE Name = #name", allSeats[i].Name)
Then
only use 2 parameters.
SqlCommand myCommand = new SqlCommand(cmdStr, myConnection);
SqlParameter param = new SqlParameter();
SqlParameter param2 = new SqlParameter();
etc.
cmd.parameter.addwithvalue("#param1", value1);
cmd.parameter.addwithvalue("#param2", value2);
use like this.
As Soner has mentioned, columns cannot be parameterized. This means you will either have to create dynamic queries, or create all the parameterized once at the startup, one query per column name.
this can be done in the following example:
private static Dictionary<string, SqlCommand> parameterizedCommands = new Dictionary<string,SqlCommand>();
public static void CreateparameterizedCommandsy(string[] colums)
{
parameterizedCommands = new Dictionary<string,SqlCommand>();
foreach (string colname in colums)
{
parameterizedCommands.Add(colname, CreateCommandForColumn(colname));
}
}
public static SqlCommand CreateCommandForColumn(string columnName)
{
SqlCommand myCommand = new SqlCommand(string.Format("UPDATE Events SET {0} = #truefalse WHERE Name = #name",columnName));
// the following statement creates the parameter in one go. Bit = boolean
myCommand.Parameters.Add("#truefalse", SqlDbType.Bit);
myCommand.Parameters.Add("#name", SqlDbType.Text);
return myCommand;
}
public int ExccuteColumnUpdate(string columnName, bool setToValue, string name, SqlConnection connection)
{
connection.Open();
try
{
SqlCommand command;
if (parameterizedCommands.TryGetValue(columnName, out command))
{
command.Connection = connection;
command.Parameters["#truefalse"].Value = setToValue;
command.Parameters["#name"].Value = name;
return command.ExecuteNonQuery();
}
}
finally
{
connection.Close();
}
return 0;
}
Related
I'd like to execute Sybase stored procedure the same way I am doing in a SQL IDE, i.e. something like this in SQL:
exec sp_GetCMyDataPerRegion JAPAN'
However, instead of this, in the C# code I am forced to define each parameter individually, each having all those types defined:
AseCommand command = new AseCommand(spName, DbConnection);
command.CommandType = CommandType.StoredProcedure;
AseParameter param = command.CreateParameter();
param.ParameterName = "#region";
param.AseDbType = AseDbType.VarChar;
param.Direction = ParameterDirection.Input;
param.Value = myValue;
command.Parameters.Add(param);
Quite a pain and not finding anyway so far to have it "generic", i.e. would just like to wrap the store procedure call in a method with this kind of signature:
public AseDataReader ExecuteStoredProcedure(string spExecutionString){}
Would you have any way on doing so?
Thank you in advance!
I do have an example of an SqlDataReader where the Function call is
ExecuteNonQuery("dbo.[Sp_Skp_UpdateFuncties]", parameters);
This is in a class DataBaseManager which hold the databaseconnectionstring
public classDataBaseManager
{
...
public int ExecuteStoredProcedure(string storedprocedureNaam, IEnumerable<KeyValuePair<string, object>> parameters)
{
var sqlCommand = new SqlCommand
{
Connection = DatabaseConnectie.SqlConnection,
CommandType = CommandType.StoredProcedure,
CommandText = storedprocedureNaam,
};
foreach (KeyValuePair<string, object> keyValuePair in parameters)
{
sqlCommand.Parameters.Add(
new SqlParameter { ParameterName = "#" + keyValuePair.Key, Value = keyValuePair.Value ?? DBNull.Value }
);
}
if (sqlCommand == null)
throw new KoppelingException("Stored procedure ({0}) aanroepen lukt niet", storedprocedureNaam);
return sqlCommand.ExecuteNonQuery();
}
....
}
So i've been making a simple c# win forms application for something league of legends related and i've been using a .SDF Database which I'm very new with.
When trying to make a search option I came across this error:
A Parameter is missing. [Parameter ordinal = 1]
Here is my Code: (I did Add the Parameter to the command.)
public DataTable GetDataTableSummoners(string Summoner, string Champion)
{
DataTable t = new DataTable();
try
{
var cmd = new SqlCeCommand();
cmd.CommandText = "SELECT * FROM Summoner";
if (!String.IsNullOrEmpty(Summoner))
{
SqlCeParameter param = new SqlCeParameter();
param.ParameterName = "#Summoner";
param.Value = Summoner;
cmd.Parameters.Add(param);
cmd.CommandText = "SELECT * FROM Summoner WHERE name = #Summoner";
}
var adapter = new SqlCeDataAdapter(cmd.CommandText, conn);
adapter.Fill(t);
}
catch (System.Data.SqlServerCe.SqlCeException ex)
{
System.Diagnostics.Debug.Print("Error: " + ex.Message);
}
return t;
}
You're adding the parameter to the command - but then you're ignoring the command itself here:
var adapter = new SqlCeDataAdapter(cmd.CommandText, conn);
You're only passing in the command text, which doesn't include the parameter. You want:
var adapter = new SqlCeDataAdapter(cmd, conn);
(I'd also suggest using a using statement for the command, and changing your method parameter names to follow .NET naming conventions. Oh, and you don't appear to be using the champion parameter...)
Oh, and you can add a parameter much more easily like this (also specifying the type, which is a good idea):
cmd.Parameters.Add("#Summoner", SqlDbType.NVarChar).Value = Summoner;
In DataAdapter Pass complete Command Object not only Command Text
var adapter = new SqlCeDataAdapter(cmd, conn);
or You can add parameter like this
using (SqlConnection connection = new SqlConnection(connectionString))
{
SqlCommand command = new SqlCommand(commandText, connection);
command.Parameters.Add("#ID", SqlDbType.Int);
command.Parameters["#ID"].Value = customerID;
}
or you can use command.Parameters.AddWithValue also.
I'm not seeing why my update statement isn't actually updating. Here is what I have:
private void submit_button_Click(object sender, EventArgs e)
{
string insert = insertbox.Text;
SqlParameter param2 = new SqlParameter("#param2", SqlDbType.Text);
param2.Value = insert;
var connlink = new SqlConnection("Data Source=.\\SQLEXPRESS;AttachDbFilename=C:\\Program Files\\Microsoft SQL Server\\MSSQL.2\\MSSQL\\Data\\Inserts.mdf;Integrated Security=True;Connect Timeout=30;User Instance=True");
var cmd1 = new SqlCommand(#"SELECT qty_onhand FROM [insert] WHERE (Name LIKE #param2)", connlink);
connlink.Open();
cmd1.Parameters.Add(param2);
var onhand = Convert.ToInt16(cmd1.ExecuteScalar());
// The param2 in the statement passes fine and returns the value into "onhand".
// Below, the parameters don't seem to be passed. There is no error but the record isn't updated.
int new_onhand = Convert.ToInt16(qtybox1.Text);
Convert.ToInt16(onhand);
new_onhand = onhand - new_onhand;
SqlParameter param1 = new SqlParameter("#param1", SqlDbType.SmallInt);
param1.Value = new_onhand;
SqlParameter param3 = new SqlParameter("#param3", SqlDbType.Text);
param3.Value = param2.ToString();
var cmd = new SqlCommand(#"UPDATE [insert] SET qty_onhand = #param1 WHERE (Name LIKE #param3)", connlink);
cmd.CommandType = CommandType.Text;
cmd.Parameters.Add(param1);
cmd.Parameters.Add(param3);
cmd.ExecuteNonQuery();
connlink.Close();
}
I'm not sure of the difference of why one works and the other doesn't.
you set the value of param3 by calling ToString on param2: param3.Value = param2.ToString();
Calling ToString on a SqlParameter returns the parameter name. In our case, it returns "#param2" as a string, and not the value of it. Try using param2.Value.
Or actually to insert, since you wrote param2.Value = insert;.
The lines below looks incorrect to me because param2 is of type SqlParameter and you using param2.ToString() as value for param3.value
SqlParameter param3 = new SqlParameter("#param3", SqlDbType.Text);
**param3.Value = param2.ToString();**
I am trying to pass a table-value parameter to a stored procedure, but I keep getting an exception (see below).
SqlCommand c = new SqlCommand("getPermittedUsers", myConn) { CommandType = CommandType.StoredProcedure };
c.Parameters.AddWithValue("#intNotifyingUserId", notifyingUserId);
c.Parameters.AddWithValue("#tSelectedPdfIds", sharedPdfs).SqlDbType = SqlDbType.Structured;
SqlDataReader dr = c.ExecuteReader();
The type is defined on the server like this:
CREATE TYPE [dbo].[IdList] AS TABLE(
[Id] [int] NOT NULL
)
I have tried passing sharedPdfs as a List<int>, and IQueryable<int>, but keep getting the following exception:
Object must implement IConvertible.
Anyone know what I am doing wrong? The documentation implies that I should be able to pass a list as a TVP but doesn't give any examples.
Thank you.
The following example illustrates using either a DataTable or an IEnumerable<SqlDataRecord>:
SQL Code
CREATE TABLE dbo.PageView
(
PageViewID BIGINT NOT NULL CONSTRAINT pkPageView PRIMARY KEY CLUSTERED,
PageViewCount BIGINT NOT NULL
);
CREATE TYPE dbo.PageViewTableType AS TABLE
(
PageViewID BIGINT NOT NULL
);
CREATE PROCEDURE dbo.procMergePageView
#Display dbo.PageViewTableType READONLY
AS
BEGIN
MERGE INTO dbo.PageView AS T
USING #Display AS S
ON T.PageViewID = S.PageViewID
WHEN MATCHED THEN UPDATE SET T.PageViewCount = T.PageViewCount + 1
WHEN NOT MATCHED THEN INSERT VALUES(S.PageViewID, 1);
END
C# Code
private static void ExecuteProcedure(bool useDataTable, string connectionString, IEnumerable<long> ids) {
using (SqlConnection connection = new SqlConnection(connectionString)) {
connection.Open();
using (SqlCommand command = connection.CreateCommand()) {
command.CommandText = "dbo.procMergePageView";
command.CommandType = CommandType.StoredProcedure;
SqlParameter parameter;
if (useDataTable) {
parameter = command.Parameters.AddWithValue("#Display", CreateDataTable(ids));
}
else {
parameter = command.Parameters.AddWithValue("#Display", CreateSqlDataRecords(ids));
}
parameter.SqlDbType = SqlDbType.Structured;
parameter.TypeName = "dbo.PageViewTableType";
command.ExecuteNonQuery();
}
}
}
private static DataTable CreateDataTable(IEnumerable<long> ids) {
DataTable table = new DataTable();
table.Columns.Add("ID", typeof(long));
foreach (long id in ids) {
table.Rows.Add(id);
}
return table;
}
private static IEnumerable<SqlDataRecord> CreateSqlDataRecords(IEnumerable<long> ids) {
SqlMetaData[] metaData = new SqlMetaData[1];
metaData[0] = new SqlMetaData("ID", SqlDbType.BigInt);
SqlDataRecord record = new SqlDataRecord(metaData);
foreach (long id in ids) {
record.SetInt64(0, id);
yield return record;
}
}
You can pass the parameter as a DataTable, IEnumerable<SqlDataRecord>, or DbDataReader.
Adding a new answer with updated links.
According to the documentation (learn.microsoft.com), you can use one of the following parameter types:
SqlClient supports populating table-valued parameters from DataTable, DbDataReader or IEnumerable \ SqlDataRecord objects. You must specify a type name for the table-valued parameter by using the TypeName property of a SqlParameter. The TypeName must match the name of a compatible type previously created on the server.
Not included in the docs but important for high-performance apps, a sample using IEnumerable<SqlDataRecord> (.NET Core 3.1, async):
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(30));
using SqlConnection connection = this.GetConnection();
await connection.OpenAsync(timeout.Token);
using SqlCommand command = connection.CreateCommand();
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "Mycommand";
IEnumerable<SqlDataRecord> records = // ...
SqlParameter parameter = command.Parameters.Add("#MyObjects", SqlDbType.Structured);
parameter.TypeName = "MyCustomTableType";
parameter.Value = records;
await command.ExecuteNonQueryAsync(timeout.Token);
Example using a DataTable:
// Create a DataTable with the modified rows.
DataTable addedCategories = CategoriesDataTable.GetChanges(DataRowState.Added);
// Configure the SqlCommand and SqlParameter.
SqlCommand insertCommand = new SqlCommand("usp_InsertCategories", connection);
insertCommand.CommandType = CommandType.StoredProcedure;
SqlParameter tvpParam = insertCommand.Parameters.AddWithValue("#tvpNewCategories", addedCategories);
tvpParam.SqlDbType = SqlDbType.Structured;
// Execute the command.
insertCommand.ExecuteNonQuery();
Example using DbDataReader:
// Assumes connection is an open SqlConnection.
// Retrieve data from Oracle.
OracleCommand selectCommand = new OracleCommand(
"Select CategoryID, CategoryName FROM Categories;",
oracleConnection);
OracleDataReader oracleReader = selectCommand.ExecuteReader(
CommandBehavior.CloseConnection);
// Configure the SqlCommand and table-valued parameter.
SqlCommand insertCommand = new SqlCommand(
"usp_InsertCategories", connection);
insertCommand.CommandType = CommandType.StoredProcedure;
SqlParameter tvpParam =
insertCommand.Parameters.AddWithValue(
"#tvpNewCategories", oracleReader);
tvpParam.SqlDbType = SqlDbType.Structured;
// Execute the command.
insertCommand.ExecuteNonQuery();
Here's my method:
public void EjecutarGuardar(string ProcedimientoAlmacenado, object[] Parametros)
{
SqlConnection Connection = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString);
SqlCommand Command = Connection.CreateCommand();
Command.CommandText = ProcedimientoAlmacenado;
Command.CommandType = CommandType.StoredProcedure;
foreach (object X in Parametros)
{
Command.Parameters.Add(X);
}
Connection.Open();
Command.ExecuteNonQuery();
Connection.Close();
Connection.Dispose();
}
Say I added an int to my object array PARAMETROS, when it reaches the foreach statement I get an error:
The SqlParameterCollection only
accepts non-null SqlParameter type
objects, not Int32 objects.
So, how can I load all of my parameters outside of this class, and then place them all into a generic array, and pass it on to this method to do it's magic. Any help?
Edit: A friend sent me this code, would it work? I cant understand what it's doing. :S
protected void CargarParametros(SqlCommand Com, System.Object[] Args)
{
for (int i = 1; i < Com.Parameters.Count; i++)
{
SqlParameter P = (SqlParameter)Com.Parameters[i];
if (i <= Args.Length )
P.Value = Args[i - 1];
else
P.Value = null;
}
}
Use AddWithValue method,
string []para={"#eno","#ename","#edate"};
object []val={11,"A","1-1-2002"};
System.Data.SqlClient.SqlConnection cn = new System.Data.SqlClient.SqlConnection(#"");
System.Data.SqlClient.SqlCommand cmd = new System.Data.SqlClient.SqlCommand();
cmd.CommandText = "proc_name";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Connection = cn;
for(int i=0;i<para.Length;i++){
cmd.Parameters.AddWithValue(para[i], val[i]);
}
cn.Open();
cmd.ExecuteNonQuery();
cn.Close();
Your SqlCommand wraps up the stored procedure. In order to call it, you will need to create an instance of a SqlParameter for each parameter that you pass into or get out of the stored procedure. You cannot just simply add your values - how would ADO.NET know which value to assign to which parameter??
Each SqlParameter contains things like:
a name
its datatype
possibly restrictions (on size, length)
and possibly a value
So in your case, your statement should look something like this:
SqlCommand Command = Connection.CreateCommand();
Command.CommandText = ProcedimientoAlmacenado;
Command.CommandType = CommandType.StoredProcedure;
foreach (object X in Parametros)
{
SqlParameter param = new SqlParameter();
param.ParameterName = Parametros.Name;
// you need to find a way to determine what DATATYPE the
// parameter will hold - Int, VarChar etc.
param.SqlDbType = SqlDbType.Int;
param.Value = Parametros.Value;
Command.Parameters.Add(param);
}
So, just adding values isn't going to work - you need to capture those parameters with their name, data type, length etc. and their values.
Marc
You need to do this:
Sql command example:
"SELECT * FROM YourTable WHERE FirstColumn = #YourParameterID;"
To add a parameter for this command:
Command.Parameters.Add(new SqlParameter("#YourParameterID", SqlDbType.Int).Value = X);
I'm not an expert, but I guess you should name your parameters; so instead of just having an array of object, you should consider having an array of key-value pairs.
Then, you should take a look at one of SqlParameter constructors: http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlparameter.sqlparameter.aspx
You can also use an abbreviated version. ADO.NET will know it's a number and will insert the proper datatype if you do this:
Command.Parameters.Add(new SqlParameter("#YourParameterID", 4));
etc.
Also, make sure you're not inserting a NULL into a NOT NULL data field, and is implicitly castable to type SqlParameter.