Parameterized query not working in C# - c#

I am currently in a corner and have no idea why the following code will not execute properly and update the database (Access).
newUser = All of the new user's data including their ID
list = Contains a list of GermanResources (class) entries that correspond to the pages checkboxes. Class includes .Name (text value of checkbox) and .Value (checked? 1 or 0)
I want to update the database with the checkbox value of each GermanResource.
IF i replace #acc_Value with the value 1 this code works. It seems to not work with the first parameter in place. Debugging this showed me that everything had the proper values at the proper times and since "1" worked I know the data types are not mismatched.
Note: There were no errors with or without the parameter in place.
I would appreciate any input about this.
This is one of the CommandTexts that are generated:
UPDATE VMS_GRM_GermanResource_Access SET VTOFZN = #acc_Value WHERE UserId = #userId
private bool NewUser_Insert_GermanResourceAccess(OleDbConnection connection, User newUser, List<GermanResource> list)
{
bool result = false;
try
{
foreach (var item in list)
{
string column = item.Name.Replace(" ", "");
string query = #"UPDATE VMS_GRM_GermanResource_Access SET " + column + " = #acc_Value WHERE UserId = #userId";
OleDbCommand command = new OleDbCommand(query, connection);
command.Parameters.AddWithValue("#userId", newUser.Id);
command.Parameters.Add(new OleDbParameter("#acc_Value", OleDbType.Integer, 1));
command.Parameters["#acc_Value"].Value = item.Access;
command.ExecuteNonQuery();
}
result = true;
}
catch (OleDbException ex)
{
UADConnection.Close();
MessageBox.Show(ex.ErrorCode.ToString() + ": " + ex.Message);
return result;
}
return result;
}

Use this to prepare sql statement :-
string query = #"UPDATE VMS_GRM_GermanResource_Access SET column_name=" +
#acc_Value + " WHERE UserId = " +#userId+";

#Tetsuya Yamamoto:
OLEDB parameters were not in order according to the query. Swapping them around to match the order in the query set things straight. All good again and thanks for everyone's inputs.

Related

Using C# SqlCommand to Query SQL Table

I have a class SqlGetGroupCutRates which has a
public bool UpdateDefaultTarget(string param1)
method. In this method, I am using the SqlClient.SqlCommand class.
public bool UpdateDefaultTarget(string g1)
{
string myworkCenterCode = Form1.globalWorkCenter;
try
{
string connString = #"Data Source =" + server + "; Initial Catalog =" + redGreenDB + ";Integrated Security=SSPI";
// WHERE CUT_RATE_GROUP = #CUT_RATE_GROUP... WHAT IS #CUT_RATE_GROUP????
string myCommandString = "SELECT TOP 1 Default_Cut_Rate_Per_Hour FROM " + groupCutRateTable + " WHERE " +
"Cut_Rate_Group= #Cut_Rate_Group ORDER BY Record_Date DESC";
// SQL connection using the connString taking us to the redGreenDB
using (SqlConnection conn = new SqlConnection(connString))
{
conn.Open();
// Get the specific default cut rate from groupCutRateTable ("dbo.ABR_Tape_Cvt_Group_Cut_Rates") where Cut_Rate_Group (column)
using (SqlCommand cmd = new SqlCommand(myCommandString, conn))
{
cmd.Parameters.AddWithValue("Cut_Rate_Group", g1);
using (SqlDataReader reader = cmd.ExecuteReader())
{
if (reader.HasRows)
{
defaultFoundInQuery = true;
while (reader.Read())
{
int ordTarget = reader.GetOrdinal("Default_Cut_Rate_Per_Hour");
// Handle potential null database values:
if (!reader.IsDBNull(ordTarget))
{
int ord1 = reader.GetOrdinal("Default_Cut_Rate_Per_Hour");
default_Cut_Rate_Per_Hour = reader.GetDouble(ord1);
}
else
{
default_Cut_Rate_Per_Hour = 0;
}
}
}
// else if no data rows found.
else
{
default_Cut_Rate_Per_Hour = 0;
}
conn.Close();
}
}
}
}
catch (Exception ex)
{
MessageBox.Show("Problem getting Group Cut Rates from database: \n" + ex);
}
return defaultFoundInQuery;
}
In my myCommandString variable, you will notice it is set to the value
string myCommandString = "SELECT TOP 1 Default_Cut_Rate_Per_Hour FROM " + groupCutRateTable + " WHERE " +
"Cut_Rate_Group= #Cut_Rate_Group ORDER BY Record_Date DESC";
I am querying the top 1/first Default_Cut_Rate_Per_Hour column value from the groupCutRateTable WHERE the Cut_Rate_Group column value is #Cut_Rate_Group.
My question is... in this query... what is meant by the #Cut_Rate_Group part of the query? Is this just going to return the first Cut_Rate_Group column value? Essentially making this a "static" query that will always return the same value? Or is the syntax of #Cut_Rate_Group seen as dynamic? Meaning #Cut_Rate_Group is assigned a value somewhere in my code?
Apologies, I am very new to this. If you have any docs I could read further into this, I would also appreciate that so I can better understand any answer I may get. Thanks for taking the time to read this.
I am expecting that this syntax would make #Cut_Rate_Group dynamic in the sense that it is almost viewed as a variable that is assigned a value.
The statement
cmd.Parameters.AddWithValue("Cut_Rate_Group", g1)
creates the parameter #Cut_Rate_Group that is referred to in the select statement. Since its value comes from the parameter g1, it will be "dynamic" in that whatever value is passed in g1 will become the value of the parameter #Cut_Rate_Group used in the select statement.
The statement above could have been written
cmd.Parameters.AddWithValue("#Cut_Rate_Group", g1)
If you call UpdateDefaultTarget with the same value of g1, and no records have been deleted from the table, it will return the same record if no new records have a record date less than or equal to that original record.
However, not knowing what you are trying to accomplish, this may not be what you actual want to happen.
#Cut_Rate_Group is a sql paramter (and is dynamic like a variable). Parameterization of sql commands is to safe guard from sql injections.
It's value is added here
cmd.Parameters.AddWithValue("Cut_Rate_Group", g1)

Application returns data for me but not other users, even when I log in under their username - SQL Server/C#/WPF

Having a little bit of a strange error here that I have never encountered before. I have an application where users can type in a list of accounts in a datagrid and a date range and press a button and it will return the data for these accounts in a datagrid and give them the option to export it to an excel file. This works perfectly for me, logged in under my username and even when I log in under other people's username. The problem is when they try it, they get no data back. No errors, just it doesn't pull any data.
The interesting thing is this is all in the same database as the other information which they access without any problem. The only difference, which I think might be the explanation is I am calling this SQL code directly from the Application whereas everything else is called using stored procedures that sit on the server. The reason for this is I have to concatenate the SQL Query string for each item in the accounts field. Since they are able to enter as many accounts as they want, I cannot use a stored procedure since I don't know how many parameters it will have ultimately(if someone could let me know a method of doing this, I would actually prefer this way for keeping things consistent). Obviously the query string is working properly, as it's pulling data back for me, but the question I have is why is it failing to return data for others? The connection string is an SQL Authentication, so it shouldn't have anything to do with them not having Windows Authentication on the server, plus they are already able to log in to the application and it displays data on their dashboard, which couldn't happen...
Anyone that can point me in the right direction with this I would appreciate it...the only thing I can think of is it is an issue with using an in-code SQL string versus a stored procedure, but this doesn't make any sense since other people do this all the time in applications without issue.
public ICommand GetData
{
get => new RelayCommand(() =>
{
//call the SQL Code to lookup the account numbers
var SQLStr = "SELECT * FROM [Clients].[Data] WHERE (Account_Number = '";
for (var i = 0; i< AccountNums.Count; i++)
{
if (!String.IsNullOrEmpty(AccountNums[i].accNum)) SQLStr += i == 0 ? $"{AccountNums[i].accNum}'" : $" OR Account_Number = '{AccountNums[i].accNum}'";
}
SQLStr += $") AND SUB_QUERY_CREATED_ON BETWEEN '{StartDate.ToString()}' AND '{EndDate.ToString()}'";
_Data = DBMethods.GetSQLData(_Data, new Models.Clients.Data(), SQLStr, new List<string> { "ID" }, true);
ShowResPnl = true; //there are results, toggle the panel visibility bound variable
});
}
public static ObservableCollection<T> GetSQLData<T>(ObservableCollection<T> myCollection, T myClass, String SQLString, List<string> remParams, bool UseSQLQuery) where T : class
{
var conn = new SqlConnection();
try
{
var paramList = GenerateSQLParameters(myClass, remParams);
using (getConnection(conn))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(SQLString, conn))
{
cmd.CommandType = CommandType.Text;
SqlDataReader reader;
reader = cmd.ExecuteReader();
//only execute if the reader has data
if (reader.HasRows)
{
while (reader.Read())
{
var tempModel = Global.GenerateNewInstance(myClass) as T;
Type model = tempModel.GetType();
var prop = model.GetProperties();
PropertyInfo pi;
//set the values for each property in the model
foreach (var p in prop)
{
if (!remParams.Contains(p.Name))
{
pi = tempModel.GetType().GetProperty(p.Name);
if (reader[p.Name] == DBNull.Value)
{
pi.SetValue(tempModel, null);
}
else
{
pi.SetValue(tempModel, reader[p.Name]);
}
}
}
myCollection.Add(tempModel);
}
reader.Close();
cmd.Dispose();
}
}
}
}
catch (Exception ex)
{
ErrorWindow errWin = new ErrorWindow("There was a problem trying to Get the Data with the Query '" + SQLString + "'! Error: " + ex.Message);
errWin.Show();
}
return myCollection;
}
UPDATE: OK I got it working perfectly with help from THIS thread:
How do I split a string so I can access item x?
and more specifically this post:
What about using string and values() statement?
DECLARE #str varchar(max)
SET #str = 'Hello John Smith'
DECLARE #separator varchar(max)
SET #separator = ' '
DECLARE #Splited TABLE(id int IDENTITY(1,1), item varchar(max))
SET #str = REPLACE(#str, #separator, '''),(''')
SET #str = 'SELECT * FROM (VALUES(''' + #str + ''')) AS V(A)'
INSERT INTO #Splited
EXEC(#str)
SELECT * FROM #Splited
I created a stored procedure using this, then did a left join on Account numbers from the Data Table and used a WHERE clause to set the Start and End Dates and exclude items that were NULL(checked one of the columns). Works perfectly and only took about 2 or 3 seconds to return the data. I had another working method as detailed here https://sqlperformance.com/2012/07/t-sql-queries/split-strings#comments using a function which was taking well over a minute to return data for only 4 accounts...obviously was not going to work well enough so I found the method mentioned prior and it works excellently!

IndexOutOfRange Exception in sqldatareader using c#

I create an application using c# , In my authentification interface , i have a test control , i want to know profile user .
My database contains table named user which contains 4 columns
(id_user,name ,mail, profile)
Here is my code
public string profil_user(string login)
{
SqlConnection conn = new database().connect_user();
SqlCommand cmd = conn.CreateCommand();
cmd.CommandText = "select profile from user where name = '" + login + "';";
SqlDataReader s = cmd.ExecuteReader();
if (s.Read())
{
return ( s.GetString(3));
}
else{return ("false"); }
}
but i have an exception in s.GetString(3)
system.IndexOutOfRange : index was outside the bounds of the array
You're only selecting a single field (profile) but then you're trying to select the 4th field (index 3) here:
return ( s.GetString(3));
In addition to just returning s.GetString(0) I would strongly advise you to:
Use parameterized SQL - always do this, to prevent SQL injection attacks, make your code more readable, and prevent unexpected text conversion problems
Either throw an exception or return null if the profile isn't found, instead of returning the string "false"
Use using statements for disposable things like SqlCommand, SqlConnection and SqlDataReader to ensure that you clean up resources appropriately
Start following .NET naming conventions to make your code more idiomatic
So something like:
public string GetUserProfile(string login)
{
string sql = select profile from user where name = #login";
// I assume Connect() returns an *open* connection?
using (var conn = new Database().Connect())
{
using (var command = new SqlCommand(sql, conn))
{
command.Parameters.Add("#login", SqlDbType.NVarChar).Value = login;
using (var reader = command.ExecuteReader())
{
// If it's an error (code failure) for there to be no matching profile,
// you may want to throw an exception instead.
return s.Read() ? s.GetString(0) : null;
}
}
}
}
So you want the fourth row, not the fourth column which you try to access with s.GetString(3):
int rowNum = 0;
while(s.Read())
{
if(++rowNum == 4)
{
return s.GetString(0);
}
}
return "false";
However, it is a bit strange to access the fourth row when you don't use an Order By. You should also only return the row that you want with the correct sql query.
You are also open for sql injection if you use string concatenation here:
cmd.CommandText = "select profile from user where name = '" + login + "';";
Use sql parameters:
cmd.CommandText = "select profile from user where name = #login";
cmd.Parameters.Add("#login", SqlDbType.VarChar).Value = login;
have 4 columns not rows
Ok, so you instead want the fourth column. Why don't you use the name instead?
Since you only select the profile-column(the fourth), you could simply use GetString(0). But you could also select all columns and then determine the correct index with GetOrdinal:
int profileColumnIndex = s.GetOrdinal("profile");
return s.GetString(profileColumnIndex);
This is useful if you don't control the query or it might be changed in future.
You are selecting only 1 field, thus index 3 is out of bounds. It also very important to Use parameters. Try:
cmd.CommandText = "select profile from user where name = #login;";
cmd.Parameters.Add("#login, SqlDbType.NVarChar).Value = login;
SqlDataReader s = cmd.ExecuteReader();
while (s.Read())
{
return s[0].ToString();
}
The parameter for SqlDataReader.GetString should be the column index. You're only selecting one column so you get an exception.
Because you do not have all the fields in your select list
Change the SQL to:
select id_user,name ,mail, profile from user where name = '" + login + "';

C# SQL : update with params - getting 0 and without the text

I am making DatabaseManager class for my solution and I am getting the number 0 when I am trying to update the text.
For example : I have now the name michael and I wanted to change it to "michael , mike" so I'll probably use update.
public void AddCrime(CSteamID id, string crime, string time)
{
try
{
MySqlConnection connection = createConnection();
MySqlCommand command = connection.CreateCommand();
crime = "," + crime;
command.CommandText = "update `" + Main.Instance.Configuration.Instance.DatabaseTableName
+ "` set `crime` = crime + ( #crime ) where `steamId` = #steamID; select `crime` from `"
+ Main.Instance.Configuration.Instance.DatabaseTableName
+ "` where `steamId` = #steamID";
command.Parameters.AddWithValue("#steamID", id);
command.Parameters.AddWithValue("#crime", crime);
connection.Open();
command.ExecuteNonQuery();
connection.Close();
AddTime(id, time);
}
catch (Exception ex) { Logger.Log(ex); }
}
How do I call it :
DatabaseManager.AddWanted(player.CSteamID, command[1], command[2]);
Thanks everyone!
yor last sentence in your command is a select statement, NonQuery does not return values, only the number of rows affected. Change it to ExecuteScalar and store the value of the select in a variable.
Second error is the data type of the parameter #steamID. You set the value id, which is declares as CSteamID id... CStreamId is not string, change the AddWithValue
Fixed, I added another method to get crime from table and then changed the void crime string to the current string + the table text.
The error was : Truncated incorrect DOUBLE value.

Query parameterized cannot insert value with c# and SQL Server

I have a parameterized query and it was working fine, but when I delete de DB and create it again, with the same values and everything, it throws an exception that says it cannot insert value NULL with the value sexo, but all the values are assigned, here it's the code:
try{
var cmdPersona_Log = new SqlCommand();
cmdPersona_Log.Parameters.Clear();
cmdPersona_Log.Connection = mySqlConnection;
cmdPersona_Log.CommandType = CommandType.Text;
cmdPersona_Log.CommandText = #"INSERT INTO [Tomin].[TominRH].[Persona_Log] "
+ "([Id_Action],[Id_User],[Id_Date],[Id_Entidad],[Nombre],[Paterno],[Materno],[Sexo],[Id_Nacionalidad])"
+ " Values (1, 'Admin', #fecha, #id_entidad, #nombre, #paterno, #materno, #sexo, 52)";
cmdPersona_Log.Parameters.AddWithValue("#fecha", DateTime.Now);
cmdPersona_Log.Parameters.AddWithValue("#id_entidad", dbRow["CUENTA"].ToString().Trim());
cmdPersona_Log.Parameters.AddWithValue("#nombre", nombre ?? string.Empty);
cmdPersona_Log.Parameters.AddWithValue("#paterno", paterno ?? string.Empty);
cmdPersona_Log.Parameters.AddWithValue("#materno", materno ?? string.Empty);
cmdPersona_Log.Parameters.AddWithValue("#sexo", 1);
cmdPersona_Log.ExecuteNonQuery();
}
catch(Exception e)
{
MessageBox.Show(dbRow["CUENTA"] + " Persona_log " + e.ToString());
}
I've checked the DB and it doesn't seem to be the problem, any sugestion??
You may be running into a case where AddWithValue isn't inferring your parameter type of bit properly. Use true/false instead of 1/0:
cmdPersona_Log.Parameters.AddWithValue("#sexo", true);
Make sure that all fields of the table which are supposed to be not null are listed in the INSERT-clause
Have you tried using this form
cmd.Parameters.Add("#SomeID", SqlDbType.Int, 4).Value =
where you can be explicit about the SqlDbType?

Categories