How to bind parameters via ODBC C#?
Odbc cannot use named parameters. This means that the command string uses placeholders for every parameter and this placeholder is a single question mark, not the parameter name.
OdbcCommand.Parameters
Then you need to add the parameters in the collection in the same order in which they appear in the command string
OdbcCommand cmd = conn.CreateCommand();
cmd.CommandText = "SELECT * FROM [user] WHERE id = ?";
cmd.Parameters.Add("@id", OdbcType.Int).Value = 4;
OdbcDataReader reader = cmd.ExecuteReader();
You have also another problem, the USER word is a reserved keyword per MS Access Database and if you want to use that as field name or table name then it is required to enclose every reference with square brackets. I strongly suggest, if it is possible, to change that table name because you will be hit by this problem very often.
Why is parametrization via ODBC C# not binding?
Your SQL should be
dbCommand.CommandText = "SELECT col2 FROM TableName WHERE ID = @ID";
since your parameter is named as "@ID"
ODBC Command Parameters Not Binding
so i searched for parameterName and i found this article https://msdn.microsoft.com/en-us/library/system.data.odbc.odbcparameter.parametername(v=vs.110).aspx
they had a different way of calling a stored proc and it appears to work...
using (var conn = new OdbcConnection(sConn))
{
const string cmdIns = "{ call spSound_Insert(?,?,?,?) }";
using (var sqlCmdIns = new OdbcCommand(cmdIns, conn))
{
sqlCmdIns.CommandType = CommandType.StoredProcedure;
sqlCmdIns.Parameters.Add("", OdbcType.NVarChar, 40).Value = "123";
sqlCmdIns.Parameters.Add("", OdbcType.VarBinary, -1).Value = new Byte[128];
sqlCmdIns.Parameters.Add("", OdbcType.Bit).Value = true;
sqlCmdIns.Parameters.Add("", OdbcType.NVarChar, 128).Value = "test note";
conn.Open();
}
}
Can't get parameters to work with OdbcConnection in C#
The problem with your query like i said before is the single quote. If you pass in the value without a parameter you need to use those single quotes because this defines a string in a statement.
With the parameters the framework handels all this stuff for you. It also checks for sql injection and removes dissalowed chars. Especially for string and datetime values this is really helpful.
ODBC Comman add multiple parameters with one value
Not possible if using ODBC. In ODBC, named parameters are used only in calls to stored procedures and cannot be used in other SQL statements. https://learn.microsoft.com/en-us/sql/odbc/reference/develop-app/binding-parameters-by-name-named-parameters?view=sql-server-ver15.
However, it is possible if using .Net Oracle driver, not ODBC.
How to bind an Array Parameter (Stored Procedure) via Native ODBC
Digging deeper, I found out that the first ODBC API call failing is actually the binding call for the TVP column / parameter:
SQLBindParameter(hstmt, 3, SQL_PARAM_INPUT, SQL_C_DEFAULT, SQL_SS_TABLE, arr_size, 0, tvp_name, 0, &cb);
This call returns SQL_ERROR
and checking the diagnostic record issued by this gave me the following error:
HY004 [Microsoft][ODBC SQL Server Driver]Invalid SQL data type
This specific issue was asked here already but sadly remained unsolved. Ultimately using an outdated ODBC driver was the cause of this whole problem. See my answer on another question for more details on how to fix that: https://stackoverflow.com/a/47113255/2334932
Then two points brought up by @A.K. in his comment finally solved the issue:
1. Parameter Length
The last value passed to SQLBindParameter
, the parameter length or cb
here, needed to have actual amount of available rows rather than SQL_NTS
, as it is used as input parameter here.
2. Passing the parameter name is not necessary as they are bound by position
I think it will work with or without, but specifying the name of the TVP here is actually not necessary and can be left out.
So changing the third SQLBindParameter
call to this fixed the rest of the issue(s):
cb = arr_size;
SQLBindParameter(hstmt, 3, SQL_PARAM_INPUT, SQL_C_DEFAULT, SQL_SS_TABLE, arr_size, 0, NULL, 0, &cb);
Full working code
void UpdateCharacterNationTestTVP()
{
unsigned int old_nation_id = 2;
unsigned int new_nation_id = 4;
unsigned int excluded_character_ids[] = {24, 36};
SQLLEN arr_size = ARRAYSIZE(excluded_character_ids);
SQLINTEGER cb = arr_size; // Needs to have the actual amount of available rows
SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER, 0, 0, &old_nation_id, 0, NULL);
SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER, 0, 0, &new_nation_id, 0, NULL);
SQLBindParameter(hstmt, 3, SQL_PARAM_INPUT, SQL_C_DEFAULT, SQL_SS_TABLE, arr_size, 0, NULL, 0, &cb); // Does not need the name of the TVP
// Super scary binding stuff tvp requires you to do
SQLINTEGER cb_rows[] = {SQL_NTS, SQL_NTS};
SQLSetStmtAttr(hstmt, SQL_SOPT_SS_PARAM_FOCUS, (SQLPOINTER)3, SQL_IS_INTEGER); // focusing the third parmeter (the TVP one) for the call(s) below
SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER, arr_size /*or 0*/, 0, excluded_character_ids, sizeof(UINT), cb_rows); // binding of the actual array in a column styled fashion here
SQLSetStmtAttr(hstmt, SQL_SOPT_SS_PARAM_FOCUS, (SQLPOINTER)0, SQL_IS_INTEGER); // resetting the focus
SQLRETURN res = SQLExecDirect(hstmt, (SQLCHAR*)"{call dbo.Update_Character_Nation(?,?,?)}", SQL_NTS);
if (res != SQL_SUCCESS && res != SQL_SUCCESS_WITH_INFO)
{
printf("Error during query execution: %hd\n", res);
ProcessLogs(SQL_HANDLE_STMT, hstmt);
}
}
Related Topics
Passing Object in Redirecttoaction
C# Adding Button with Value at Runtime
Converting a Generic List to a CSV String
How to Convert Ienumerable to Observablecollection
Creating Delegates Manually VS Using Action/Func Delegates
Can't Get SQL Server Compact 3.5/4 to Work with Asp .Net MVC 2
C# Inheritance and Default Constructors
What's the Best Way to Pass Event to Viewmodel
Why Can't I Assign a List<Derived> to a List<Base>
Best Programming Practice of Using Dropdownlist in ASP.NET MVC
Programmatically Get a Screenshot of a Page
Two-Way/Bidirectional Dictionary in C#
Creating a Temporary Directory in Windows
Directory.Getfiles of Certain Extension