Request replacing colum name by #variable - c#

I'm trying to develop an application on C# WPF and I try to set the name of the column in the request as #variable. I explain, i have a comboBox with some options. These options are my column's names. When I select one of these options, I have to write in a textBox a word. And when I click on the validation button, the function which executes the display of the table needs two parameters : the value of the combo box and the textbox. And would like to know if I can make a request like
SELECT * FROM customer WHERE #boxContent = #text
I think this is where the problem is.
Here is my code for for xml.cs :
private void Button_Click_1(object sender, RoutedEventArgs e)
{
//InitializeComponent();
text = testBox.Text;
afficheListe.ListeModif(text,boxContent);
}
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (ID.IsSelected)
{
boxContent = "id_client";
}
else if (Nom.IsSelected)
{
boxContent = "nom";
}
else if (Prenom.IsSelected)
{
boxContent = "prenom";
}
else if (Sexe.IsSelected)
{
boxContent = "sexe";
}
else if (DateNaissance.IsSelected)
{
boxContent = "date_Naiss";
}
else if (Mail.IsSelected)
{
boxContent = "mail";
}
else if (Adresse.IsSelected)
{
boxContent = "adresse";
}
else if (Pays.IsSelected)
{
boxContent = "pays";
}
}
Here is my code for request' function :
public void ListeModif(string text, string boxContent)
{
bdd.connection.Open();
MySqlCommand cmd = bdd.connection.CreateCommand();
cmd.Parameters.AddWithValue("#text", text);
cmd.Parameters.AddWithValue("#boxContent", boxContent);
cmd.CommandText = " SELECT * FROM client WHERE #boxContent = #text";
MessageBox.Show(cmd.ToString());
cmd.ExecuteNonQuery();
MySqlDataReader reader = cmd.ExecuteReader();
clients.Clear();
while (reader.Read())
{
clients.Add(new Client()
{
Id = Convert.ToInt32(reader["id_Client"]),
Nom = Convert.ToString(reader["nom"]),
Prenom = Convert.ToString(reader["prenom"]),
Sexe = Convert.ToString(reader["sexe"]),
Date_Naissance = Convert.ToDateTime(reader["date_Naiss"]),
Mail = Convert.ToString(reader["mail"]),
Adresse = Convert.ToString(reader["adresse"]),
Pays = Convert.ToString(reader["pays"])
});
}
reader.Close();
bdd.connection.Close();
}
When I use the debugger, the value of cmd variable is:
MySql.Data.MySqlClient.MySqlCommand}
Hoping I explained my problem well.

And would like to know if i can make a request like
"SELECT * FROM customer where #boxContent = #text "
No, you cannot. In SQL (in general, not just in SQL Server) you can replace constant values with parameters. You cannot replace other types of values:
Identifiers (table names, column names, etc.)
Operators
Function names
SQL key words
In other words, parameter replacement is not string substitution. You can do that in the application when you are constructing the query string. But you cannot pass the value as a parameter. Note: Be careful doing this in the application because the code is subject to SQL injection attacks.
Sometimes, I handle the identifier replacement by doing something like:
SET #SQL = 'SELECT * FROM customer where #boxContent = #text';
SET #SQL = REPLACE(#SQL, '#boxContent', #boxContent);
(This is T-SQL syntax.) That is, the value is replaced before the query is executed.
This may seem like an arcane restriction. But one of the purposes of prepared statements is to pre-compile the query -- saving on the expensive of parsing, compiling, and optimizing the query. In an environment where you have many small queries all of the same form being processed, this can be an important performance enhancement.

No, that's wrong. You can't have the column name dynamically replaced like that. Rather you can do like below and replace the value of boxContent variable
cmd.CommandText = $"SELECT * FROM client where {boxContent} = #text ";
If you are using C# version lower than 6 then use string.Format()
cmd.CommandText = string.Format("SELECT * FROM client where {0} = #text ", boxContent);

Related

Trying to search a SQL database via C# based on any search criteria entered, Getting Input string was not in a correct format [duplicate]

This question already has answers here:
"Input string was not in a correct format" when converting to an int
(2 answers)
Closed 10 months ago.
I am attempting to search my database with a stored procedure via my program. The goal is for it to search using any of the three criteria entered. ID#, Firstname, and/or Lastname. I created the following stored procedure in MySql:
CREATE DEFINER=`mainuser`#`localhost` PROCEDURE `searchvisitor`(
enteredid int,
enteredfn varchar(25),
enteredln varchar(25)
)
begin
select visitors.visitorid, visitors.firstname, visitors.lastname, visitors.middleinitial from visitors where visitors.visitorid = enteredid or visitors.firstname like '%enteredfn%' or visitors.lastname like '%enteredln%';
end
My C# code is as follows:
the Database Query:
public DataView searchUserQuery(int id, string fn, string ln)
{
using (MySqlConnection conn = new MySqlConnection(Helper.connVal("ntpantry")))
{
conn.Open();
DataTable dt = new DataTable();
string lgquery = "searchvisitor";
MySqlCommand cmd = new MySqlCommand(lgquery, conn);
cmd.Connection = conn;
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("enteredid", id);
cmd.Parameters.AddWithValue("enteredfn", fn);
cmd.Parameters.AddWithValue("enteredln", ln);
MySqlDataReader dr = cmd.ExecuteReader();
dt.Load(dr);
DataView dv = new DataView(dt);
return dv;
}
}
The bridgeing query (to try edit or remove extra logic in one place):
public DataView Searchvisitor()
{
if (GetVisitor.VisitorFirstName.Length == 0)
{
GetVisitor.VisitorFirstName = " ";
}
if (GetVisitor.VisitorLastName.Length == 0)
{
GetVisitor.VisitorLastName = " ";
}
return dq.searchUserQuery(Convert.ToInt32(GetVisitor.VisitorID), GetVisitor.VisitorFirstName, GetVisitor.VisitorLastName);
}
And the call after hitting the search button:
private void button2_Click(object sender, EventArgs e)
{
//Add get set to put logic in bridge data instead
GetVisitor.VisitorID = Convert.ToInt32(textBox_searchid.Text);
GetVisitor.VisitorFirstName = textBox_searchfn.Text;
GetVisitor.VisitorLastName = textBox_searchln.Text;
data_searchresults.DataSource = bd.Searchvisitor();
DataGridViewColumn columnid = data_searchresults.Columns[0];
columnid.Width = 40;
DataGridViewColumn columnfn = data_searchresults.Columns[1];
columnfn.Width = 60;
DataGridViewColumn columnln = data_searchresults.Columns[2];
columnln.Width = 60;
//was currently working on fixing search results. works for id but not first name or last name. Also results box only shows 2 items
}
And in case needed, here are my properties used:
public class GetVisitor
{
public static int VisitorID { get; set; }
public static string VisitorFirstName { get; set;}
public static string VisitorLastName { get; set;}
}
When searching for the ID it works great! but if i try and just search the firstname and or lastname i get the following error:
System.FormatException: 'Input string was not in a correct format.'
My thoughts are that the names are null causing it to have format issues on whatever is left blank so I tried to entered a space to add something but it did not work. Does it go back to how my stored procedure is set up or is it in the C# code? I tried at it for a while and am having trouble.
Thanks in advance!
There's almost certainly a whole lot of irrelevant information in that question. This almost certainly has nothing to do with databases or queries or anything other than converting a string to a number. You didn't even tell us where the exception was thrown but I'm guessing that it's here:
GetVisitor.VisitorID = Convert.ToInt32(textBox_searchid.Text);
If that TextBox doesn't contain a valid representation of a 32-bit integer then that code will fail with that error message. The obvious solution is to validate the user input BEFORE converting it to a number. It is a TextBox after all, so the user might enter anything.
I would suggest that you first test whether the user has entered anything at all. If they haven't then you can assume that they're not trying to search by ID and proceed accordingly. How you do that is up to you but I would do it quite differently. That's beyond the scope of this question though. If they have entered something then you should check whether it's a valid number and stop if it's not. Only if it is should you proceed. The TryParse method of any numerical type (and a few others besides) can do the validation and conversion in a single step, e.g.
if (textBox_searchid.TextLength == 0)
{
// No ID entered
}
else if (int.TryParse(textBox_searchid.Text, out var id))
{
// Use id here
}
else
{
// Invalid ID entered
}

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!

How to run SQL transactions using C# (Using TSql110Parser)

I have created an ASP.Net C# application to run the SQL server (MSSQL) queries.
The application reads all the user input queries in the text box provided and using the TSql110Parser, it will break down to individual SQL statements.
Its running fine in almost all the cases except when the SQL statements are like the below
DECLARE #user VARCHAR(50)
SET #user = 'ABC'
PRINT #user
SELECT * FROM user_table WHERE username = #user
The execution stops at line 2 and error from SQL server saying that
Must declare the scalar variable "#user"
even though its defined in the first line.
Then I figured out the below way to run. Adding a BEGIN and END statements.
BEGIN
DECLARE #user VARCHAR(50)
SET #user = 'ABC'
PRINT #user
SELECT * FROM user_table WHERE username = #user
END
But still, then there is a limitation that we won't be able to return the results from the SELECT query.
C# code snippet is below
protected void btnQuery_Click(object sender, EventArgs e)
{
if (qry.Length > 0)
{
using (sqlCon = new SqlConnection())
{
dbConnString = dbConnString + "database=" + ddlDBNames.SelectedValue + ";";
sqlCon.ConnectionString = dbConnString;
sqlCon.Open();
cmd = new SqlCommand();
cmd.Connection = sqlCon;
IList<ParseError> Errors;
var parser = new TSql110Parser(false);
var script = parser.Parse(new StringReader(qry), out Errors) as TSqlScript;
if (Errors.Count > 0)
{
lblErrorMessage.Text = "***** Error: No statements executed *****";
}
else
{
foreach (var ts in script.Batches)
{
foreach (var st in ts.Statements)
{
q = qry.Substring(st.StartOffset, st.FragmentLength);
ExecStatement(st, q);
}
}
}
}
}
}
protected void ExecStatement(TSqlStatement statement, string qry)
{
cmd.CommandText = qry;
if (statement is SelectStatement)
{
SqlDataReader dr = cmd.ExecuteReader();
//code to populate the tabular result
}
else
{
cmd.ExecuteNonQuery();
//code to show the non query execution result
}
}
Is there any possible way i can run the SQL queries with persistent connection to DB server so that the declaration in the first line will be able to use through out the queries?
The application screenshot below.
Use CONTEXT_INFO. It is the connection specific global variable.
Its usage is not simple since it is just a 128 bit value. To set it, run the following code (N = the int you want to store connectionwise)
DECLARE #BinVar binary(128);
SET #BinVar = cast(N as binary(128));
set context_info #BinVar
You can get the N in subsequent queries by calling the context_info() function. You should convert it with something like this.
convert(int, context_info())

Syntax Error near the word "VALUES" In login page

i'm trying to create a login page for a Project site for school, but the problem is when im trying to open Login.aspx an error pops up in VS Express for web 2013 saying: http://prntscr.com/fjll5v
This is the CS file of the login page, but the line with the word Values seems fine:
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
string user_name = Request.Form["user_name"];
string passwrd = Request.Form["passwrd"];
string RegStatus;
if ((user_name == "") || (passwrd == ""))
{
RegStatus = ("missing data or wrong data");
}
else
{
string selectQuery = "SELECT * FROM YoadUserLists (user_name, passwrd) VALUES ('";
selectQuery += user_name + "','" + passwrd + "')";
Response.Write(selectQuery);
MyAdoHelper.DoQuery(selectQuery);
RegStatus = ("Login was succeessful");
}
Response.Write(RegStatus);
Response.End();
}
}
i don't understand what the problem is
STOP! HOLD! Don't use string concatenation for SQL statements. This will make your code vulnerable for SQL injection.
Use this query with parameters (you are mistakenly making an insert instead of a select):
string selectQuery = "select * from YoadUserLists where user_name = #username and passwrd = #passwrd";
Add the parameters to your command like this:
var usernameParam = new SqlParameter("username");
usernameParam.Value = username;
command.Parameters.Add(usernameParam);
var passwrdParam = new SqlParameter("passwrd");
passwrdParam.Value = passwrd;
command.Parameters.Add(passwrdParam);
your SQL-Select seems to be a mix of an insert and select.
Because you say, that you want to make a Login i think you want to make a select.
May the required SQL-Request you want is:
SELECT user_name, passwrd FROM YoadUserLists WHERE ...?
Or do you want to make a Insert?
INSERT INTO YoadUserLists (user_name, passwrd) VALUES ...?
Please notice, that your SQL is not protected against SQLInjections because you concat the given values directly into the SQL-Query without escaping!
I think you mixed up some different things...
Your variable is named "selectQuery" and you also start writing select * from ... but then you continuing with the Syntax of an insert Statement.
With OleDbCommand it would work like the following:
...
using (var cmd = new OleDbCommand())
{
cmd.CommandText = "SELECT * FROM SOMETHING WHERE PARAMETER = ?";
cmd.Parameters.Add(new OleDbParameter("#PARAMETER", 1));
...
}
...
However, just concating a string for sqlstatements ist really bad and you should not do that.
I don`t know if this helps you, but if you want to perform a select to the database, then the Syntax of your Statement is wrong.

ASP.NET, C# How to Pass a StringQuery to a custom SQL Command

I have a slight issue, I have a ASP.NET Webforms application. I'm sending over a url?id=X were X is my database index or id.
I have a C# class file to run my SQL connection and query. Here is the code:
public DataTable ViewProduct(string id)
{
try
{
string cmdStr = "SELECT * Products WHERE Idx_ProductId = " + id;
DBOps dbops = new DBOps();
DataTable vpTbl = dbops.RetrieveTable(cmdStr, ConfigurationManager.ConnectionStrings["MyDatabase"].ConnectionString);
return vpTbl;
}
catch (Exception e)
{
return null;
}
}
So as you can see my problem lies within string cmdStr = "SQL Query" + variable;
I'm passing over my index or id through the URL then requesting it and turning it into a string then using ViewProduct(productId).
I don't know what syntax or how to add the id into my C# string sql query. I've tried:
string cmdStr = "SELECT * Products WHERE Idx_ProductId = #0" + id;
string cmdStr = "SELECT * Products WHERE Idx_ProductId = {0}" + id;
also what I have currently to no avail.
I was so sure this would be a duplicate of some canonical question about parameterized queries in C#, but apparently there isn't one (see this)!
You should parameterize your query - if you don't, you run the risk of a malicious piece of code injecting itself into your query. For example, if your current code could run against the database, it would be trivial to make that code do something like this:
// string id = "1 OR 1=1"
"SELECT * Products WHERE Idx_ProductId = 1 OR 1=1" // will return all product rows
// string id = "NULL; SELECT * FROM UserPasswords" - return contents of another table
// string id = "NULL; DROP TABLE Products" - uh oh
// etc....
ADO.NET provides very simple functionality to parameterize your queries, and your DBOps class most assuredly is not using it (you're passing in a built up command string). Instead you should do something like this:
public DataTable ViewProduct(string id)
{
try
{
string connStr = ConfigurationManager.ConnectionStrings["MyDatabase"].ConnectionString;
using (SqlConnection conn = new SqlConnection(connStr))
{
conn.Open();
using (SqlCommand cmd = conn.CreateCommand())
{
// #id is very important here!
// this should really be refactored - SELECT * is a bad idea
// someone might add or remove a column you expect, or change the order of columns at some point
cmd.CommandText = "SELECT * Products WHERE Idx_ProductId = #id";
// this will properly escape/prevent malicious versions of id
// use the correct type - if it's int, SqlDbType.Int, etc.
cmd.Parameters.Add("#id", SqlDbType.Varchar).Value = id;
using (SqlDataReader reader = cmd.ExecuteReader())
{
DataTable vpTbl = new DataTable();
vpTbl.Load(reader);
return vpTbl;
}
}
}
}
catch (Exception e)
{
// do some meaningful logging, possibly "throw;" exception - don't just return null!
// callers won't know why null got returned - because there are no rows? because the connection couldn't be made to the database? because of something else?
}
}
Now, if someone tries to pass "NULL; SELECT * FROM SensitiveData", it will be properly parameterized. ADO.NET/Sql Server will convert this to:
DECLARE #id VARCHAR(100) = 'NULL; SELECT * FROM SensitiveData';
SELECT * FROM PRoducts WHERE Idx_ProductId = #id;
which will return no results (unless you have a Idx_ProductId that actually is that string) instead of returning the results of the second SELECT.
Some additional reading:
https://security.stackexchange.com/questions/25684/how-can-i-explain-sql-injection-without-technical-jargon
Difference between Parameters.Add and Parameters.AddWithValue
SQL injection on INSERT
Avoiding SQL injection without parameters
How do I create a parameterized SQL query? Why Should I? (VB.NET)
How can I prevent SQL injection in PHP? (PHP specific, but many helpful points)
Is there a canonical question telling people why they should use SQL parameters?
What type Products.Idx_ProductId is?
Probably it is string, than you need to use quotes: "... = '" + id.Trim() + "'";

Categories