I am creating a method to select the id from any table by passing a search field.
private int SelectId(string tabela, string campo, string valor)
{
int id = 0;
using (command = new MySqlCommand())
{
command.Connection = conn;
command.Parameters.Add("#tabela", MySqlDbType.).Value = tabela;
command.Parameters.Add("#campo", MySqlDbType.Text).Value = campo;
command.Parameters.Add("#valor", MySqlDbType.VarChar).Value = valor;
command.CommandText = "SELECT `id` FROM #tabela WHERE #campo=#valor;";
try
{
id = (int)command.ExecuteScalar();
}
catch (MySqlException ex)
{
MessageBox.Show(ex.Number + " : " + ex.Message + command.CommandText);
}
catch (Exception)
{
throw;
}
}
return id;
}
But I get an MySqlException about syntax error. When i look at the Exception message, it shows me the query with the quoted table!
How do I pass the table as parameter without quotes?
Most databases won't let you specify table or column names via parameters. Parameters are meant to be for values. If you really, really need this to be dynamic, you should validate the input (it should be a known table name, with known column names within that table) and then include that in the SQL.
I agree with Jon. Here is a sample of your code with the table name inserted directly into the script, instead of as a parameter. Notice that you'll still want to validate the table and column name to prevent SQL injection. I have not included that here, but I have put in comment stubs for you.
private int SelectId(string tabela, string campo, string valor)
{
int id = 0;
using (command = new MySqlCommand())
{
command.Connection = conn;
command.Parameters.Add("#campo", MySqlDbType.Text).Value = campo;
command.Parameters.Add("#valor", MySqlDbType.VarChar).Value = valor;
// TODO: Validate table name for parameter 'tabela' to prevent SQL injection
// TODO: Validate column name for parameter 'campo' to prevent SQL injection
command.CommandText = "SELECT `id` FROM " + tabela + " WHERE #campo=#valor;";
try
{
id = (int)command.ExecuteScalar();
}
catch (MySqlException ex)
{
MessageBox.Show(ex.Number + " : " + ex.Message + command.CommandText);
}
catch (Exception)
{
throw;
}
}
return id;
}
Related
I want to pass an user input to a where clause in a method.
The method has sql query and it uses parameter, but it seems like the parameter is not passed to the query. (I debugged and saw it does not go into the while loop.
My code is below:
Console.WriteLine("Enter your name: ");
string name = Console.ReadLine();
string prm = "\"" + name + "\""; // Doublequote a string
//execute method
CheckCustomer(prm);
private static string CheckCustomer(string cusName)
{
string cust = "null";
try
{
Console.WriteLine("\nChecking custoemr...\n");
// Sql Select Query
string sql = "SELECT * FROM Customer WHERE CustomerName = #CusName";
SqlCommand cmd = new SqlCommand(sql, sqlConnection);
cmd.Parameters.AddWithValue("#CusName", cusName);
SqlDataReader dr;
dr = cmd.ExecuteReader();
string strCusname = "Customer Name Found";
Console.WriteLine("{0}", strCusname.PadRight(25));
Console.WriteLine("==============================");
while (dr.Read())
{
////reading from the datareader
cust = dr["CustomerName"].ToString();
}
dr.Close();
return cust;
}
catch (SqlException ex)
{
// Display error
Console.WriteLine("Error: " + ex.ToString());
return null;
}
}
When I execute CheckCustomer() without the where clause, it works perfect.
However, once I add a parameter, does not go inside while loop; it goes to dr.Close(); directly.
What is wrong with this code?
To check for nulls in SQL server you use "is null" instead of "where field = null"
if you tried the query in sql server management studio u will not get any result
since string cust = "null"; that means ur code checks for customerName = null, but as i stated that this is not the right way to check for null and this query will not return any result, and since there is no result that means dr.Read() will evaluate to false and the while loop won't be executed
You don't need to wrap the string value in quote. You can remove this line, since SqlParameter will handle that for you.
string prm = "\"" + name + "\""; // Doublequote a string
Also, if you want your query to support optional null values (i.e. where NULL implies that you DO NOT want to filter on customer name then you can simpy do:
SELECT * FROM Customer WHERE CustomerName = ISNULL(#CusName, CustomerName)
In your parameter section you can do something like:
cmd.Parameters.AddWithValue("#CusName", string.IsNullOrWhiteSpace(cusName) ? DbNull.Value: cusName);
If you don't want to allow nulls then you can leave the SQL query as-is as a throw a new ArgumentNullException at the top of your query method (i.e. add a guard clause):
if (string.IsNullOrWhiteSpace(CustomerName)) throw new ArgumentNullException(nameof(CustomerName));
Your query appears to be searching for the first customer with matching name. In that case you should probably add a "TOP 1" to avoid needless overhead:
SELECT TOP 1 * FROM Customer WHERE CustomerName = ISNULL(#CusName, CustomerName)
Console.WriteLine("Enter your name: ");
string name = Console.ReadLine();
string prm = "\"" + name + "\""; // Doublequote a string
//execute method
CheckCustomer(prm);
private static string CheckCustomer(string cusName)
{
string cust = "null";
try
{
Console.WriteLine("\nChecking custoemr...\n");
// Sql Select Query
string sql = "SELECT * FROM Customer WHERE CustomerName = #CusName";
SqlCommand cmd = new SqlCommand(sql, sqlConnection);
cmd.Parameters.AddWithValue("#CusName", cusName);
SqlDataReader dr;
dr = cmd.ExecuteReader();
string strCusname = "Customer Name Found";
Console.WriteLine("{0}", strCusname.PadRight(25));
Console.WriteLine("==============================");
while (dr.Read())
{
////reading from the datareader
cust = dr["CustomerName"].ToString();
}
dr.Close();
return cust;
}
catch (SqlException ex)
{
// Display error
Console.WriteLine("Error: " + ex.ToString());
return null;
}
}
try this.
In My AddProduct Method there is something wrong with my code. I get a message saying Object cannot be cast from DBNull to other Types when I test my code. Anybody have any ideas where this problem is coming from?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data;
using System.Data.SqlClient;
namespace ProductMaintenance
{
class ProductDB
{
public static Product GetProduct(string code)
{
SqlConnection connection = MMABooksDB.GetConnection();
string select = "SELECT ProductCode, Description, UnitPrice "
+ "FROM Products "
+ "WHERE ProductCode = #ProductCode";
SqlCommand selectCommand = new SqlCommand(select, connection);
selectCommand.Parameters.AddWithValue("#ProductCode", code);
try
{
connection.Open();
SqlDataReader prodReader = selectCommand.ExecuteReader(CommandBehavior.SingleRow);
if (prodReader.Read())
{
Product product = new Product();
product.Code = prodReader["ProductCode"].ToString(); ;
product.Description = prodReader["Description"].ToString();
product.Price = ((decimal)prodReader["Price"]);
return product;
}
else
{
return null;
}
}
catch (SqlException ex)
{
throw ex;
}
finally
{
connection.Close();
}
}
public static bool UpdateProduct(Product oldProduct, Product newProduct)
{
SqlConnection connection = MMABooksDB.GetConnection();
string updateStatement = "UPDATE Products SET " + "Description = #NewDescription, " + "UnitPrice = #NewUnitPrice, " + "WHERE ProductCode = #oldProductCode " + "AND Description = #OldDescription " + "AND UnitPrice = #OldUnitPrice";
SqlCommand updateCommand =
new SqlCommand(updateStatement, connection);
updateCommand.Parameters.AddWithValue(
"#NewDescription", newProduct.Description);
updateCommand.Parameters.AddWithValue(
"#NewUnitPrice", newProduct.Price);
updateCommand.Parameters.AddWithValue(
"#OldProductCode", oldProduct.Code);
updateCommand.Parameters.AddWithValue(
"#OldDescription", oldProduct.Description);
updateCommand.Parameters.AddWithValue(
"#OldUnitPrice", oldProduct.Price);
try
{
connection.Open();
int count = updateCommand.ExecuteNonQuery();
if(count > 0)
return true;
else
return false;
}
catch(SqlException ex)
{
throw ex;
}
finally
{
connection.Close();
}
}
public static int AddProduct(Product product)
{
SqlConnection connection = MMABooksDB.GetConnection();
string insertStatement = "INSERT Products " + "(ProductCode, Description, UnitPrice) " + "VALUES (#ProductCode, #Description, #UnitPrice)";
SqlCommand insertCommand = new SqlCommand(insertStatement, connection);
insertCommand.Parameters.AddWithValue("#ProductCode", product.Code);
insertCommand.Parameters.AddWithValue("#Description", product.Description);
insertCommand.Parameters.AddWithValue("#UnitPrice", product.Price);
try
{
connection.Open();
insertCommand.ExecuteNonQuery();
string selectStatement = "SELECT IDENT_CURRENT('Products') FROM Products";
SqlCommand selectCommand = new SqlCommand(selectStatement, connection);
int productC = Convert.ToInt32(selectCommand.ExecuteScalar());
return productC;
}
catch (SqlException ex)
{
throw ex;
}
finally
{
connection.Close();
}
}
}
}
You're selecting IDENT_CURRENT which can possibly return null.
See this documentation.
Returns NULL on error or if a caller does not have permission to view
the object.
In SQL Server, a user can only view the metadata of
securables that the user owns or on which the user has been granted
permission. This means that metadata-emitting, built-in functions such
as IDENT_CURRENT may return NULL if the user does not have any
permission on the object.
Based on the error you're describing, this appears to be where it's failing. Your AddProduct method wouldn't be trying to cast a null value when it does the INSERT so it is likely a null value when you try to retrieve the identity at
int productC = Convert.ToInt32(selectCommand.ExecuteScalar());
Since the call to IDENT_CURRENT is the only statement in that SQL command, that all but rules out a syntax or other error in the SQL, which would narrow this down to permissions.
There's a bit of conjecture and deduction there but I suspect that's where it's at.
(If the error is thrown from AddProduct method there's no need to post everything else. Many people - including myself - are going to jump to the conclusion that the error is when you access the SqlDataReader since you aren't checking for nulls there. But as you said, that's not the method throwing the exception. It's just easy to miss when there's so much irrelevant code.)
I'm relatively new but I've been researching this issue for over 2 days, so I think I've done my due diligence ... however if this has already been answered before I apologize.
My basic issue is I'm trying to create some dependent combo boxes. The wrinkle is the displayed value is typically not the lookup value for the next query/Combo box (I'm using an OLEDB compliant data base)
For example: Table1 (T1) contains ID (int) & NM (string), Table2 (T2) contains ID (int) & STATUS (string). I run Query1 (Q1) to display T1.NM in Combobox1 (CB1), when selected I run Query1a to lookup/get the selected Table1.ID to pass to Query2 that populates Combobox2. The connection string and Q1 work fine, CB1 displays properly, but once I select this error is thrown:
"OleDbException .. SQL Passthru expression ... using equals (=) has components that are of different data types"
// ** Initial connection & populate CB1 - This works fine **
public void comboboxLoad()
{
string conn3str = <Connection String >;
string query1 = "select NM from Table1 where REFVALUE=1 ; ";
OleDbConnection conn3 = new OleDbConnection(conn3str);
OleDbCommand tblRow1 = new OleDbCommand(query1, conn3);
OleDbDataReader rdRow1;
try
{
conn3.Open();
lblConnState.Text = "Connection Successful";
rdRow1 = tblRow1.ExecuteReader();
while (rdRow1.Read())
{
int colindx1 = rdRow1.GetOrdinal("NM");
string sItbl = rdRow1.GetString(colindx1);
CB1.Items.Add(sItbl);
}
}
catch (Exception ex)
{
MessageBox.Show("Error " + ex);
}
}
// ** Get value from CB1, create query to populate CB2 **
private void CB1_SelectedIndexChanged(object sender, EventArgs e)
{
string conn3str = <Connection String >;
OleDbConnection conn3 = new OleDbConnection(conn3str);
conn3.Open();
// Pass the selected value from CB1 (string) equal to Table1.NM (string)
string query1a = "select ID from Table1 where NM = '" + CB1.Text + "' ; ";
OleDbCommand TabID = new OleDbCommand(query1a, conn3);
int TabId2 = Convert.ToInt32(TabID.ExecuteScalar());
// Pass the variable TabId2 (int) equal to Table2.ID (int)
string query2 = "select STATUS from Table2 where ID = '" + TabId2 + "'; ";
OleDbCommand tblRow2 = new OleDbCommand(query2, conn3);
// OleDbDataReader rdTabID;
// OleDbDataReader rdRow2;
try
{
OleDbDataReader rdRow2 = TabID.ExecuteReader();
OleDbDataReader rdTabID = tblRow2.ExecuteReader(); // ** Error points to this line **
while (rdRow2.Read())
{
int TabIdidx = rdTabID.GetOrdinal("ID");
string TabIDVal = rdTabID.GetString(TabIdidx);
// Pass reference ID to label on form
lblBTableID.Text = TabId2.ToString();
int colindx1 = rdRow2.GetOrdinal("STATUS");
string sIntVal = rdRow2.GetString(colindx1);
cmbLowLvl.Items.Add(sIntVal);
}
}
catch (Exception ex)
{
MessageBox.Show("Error " + ex);
}
}
Are you positive you're getting a value back on this line int TabId2 = Convert.ToInt32(TabID.ExecuteScalar());?
Convert.ToInt32 doesn't throw a ArgumentNullException like int.Parse does so it's possible that the variable is not getting set.
Also you may want to consider changing your queries to use parameterized SQL rather than concatenation for security purposes.
https://msdn.microsoft.com/en-us/library/system.data.oledb.oledbcommand.parameters(v=vs.110).aspx
I've been able to figure out the problem. I'm really not sure why it didn't work originally, but I think it was a reader mismatch, since I was only looking for a single value back from the query ExecuteScalar() seemed to do the trick and I didn't need the 'while' loop. The working code is below.
Next I'll need to pass this return value (ID) in my next query to populate CB2. Thanks #
private void CB1_SelectedIndexChanged(object sender, EventArgs e)
{
string conn3str = <Connection String >;
OleDbConnection conn3 = new OleDbConnection(conn3str);
// Pass the selected value from CB1 (string) equal to Table1.NM (string) but return the int ID.
OleDbCommand tblRow2 = new OleDbCommand("select ID from Table1 where NM= '"+ CB1.Text +"' ;" , conn3);
try
{
conn3.Open();
string r2 = Convert.ToString(tblRow2.ExecuteScalar());
MessageBox.Show(r2);
lblBTableID.Text = "ID Code= " + r2;
conn3.Close();
}
catch (Exception ex)
{
MessageBox.Show("Error " + ex);
}
}
I have a windows form and I'm inserting values in the button click event like this
Candidate CanObj = new Candidate(txtName.Text);
if (new CandidateOP().saveCandidate(CanObj))
{
MessageBox.Show("NEW candidate details added");
}
this is my business layer method.
public Boolean saveCandidate(Candidate CanObj)
{
string query6 = "EXEC insertToCand01'" + CanObj.NIC + "'";
return (new DataAccessLayer().executeNonQueries(query6));
}
This is my data access layer method
public Boolean executeNonQueries(string query02)
{
Boolean flag = false;
SqlConnection con = null;
SqlCommand com = null;
try
{
con = new SqlConnection(DBConnect.makeConnection());
con.Open();
com = new SqlCommand(query02, con);
com.ExecuteNonQuery();
flag = true;
}
catch (Exception ex)
{
flag = false;
throw ex;
}
finally
{
com.Dispose();
con.Close();
}
return flag;
}
This is the query inside my stored procedure to insert.
In my table the ID is set to auto increment.
INSERT INTO Candidate (User_Name) VALUES (#Uname);
Now I want to display the inserted ID to be displayed when it's inserted.
So I changed the query like this.
INSERT INTO Candidate (User_Name) OUTPUT INSERTED.User_ID VALUES (#Uname);
I want to change my data access layer and business layer to get the value back
How to change my data access layer to achieve this?
Thanks in advance.
Just a quick but important note: you should really use parameterized queries to avoid SQL injection problems, and also using a proper ORM system.
About your concrete question: call your procedure with ExecuteScalar, instead of ExecuteNonQuery, and return the generated id from your stored procedure.
You don't actually need an SP, you can just do a select scope_identity() for example. Or you could use an output parameter in your SP. But just returning a scalar is the simplest way.
Something like this:
Candidate CanObj = new Candidate(txtName.Text);
int id = new CandidateOP().saveCandidate(CanObj);
/* You have **id** here, and you can use it. */
if (id >= 0)
{
MessageBox.Show("NEW candidate details added");
}
Business layer:
public Boolean saveCandidate(Candidate CanObj)
{
string query6 = "EXEC insertToCand01'" + CanObj.NIC + "'";
return new DataAccessLayer().executeNonQueries(query6);
}
and your access layer:
public int executeNonQueries(string query02)
{
long id = -1;
SqlConnection con = null;
SqlCommand com = null;
try
{
con = new SqlConnection(DBConnect.makeConnection());
con.Open();
com = new SqlCommand(query02, con);
SqlParameter returnParameter = com.Parameters.Add("RetVal", SqlDbType.Int);
returnParameter.Direction = ParameterDirection.ReturnValue;
com.ExecuteNonQuery();
id = (int) returnParameter.Value;
}
catch (Exception ex)
{
id = -1;
throw ex;
}
finally
{
com.Dispose();
con.Close();
}
return id;
}
I'm getting a SqlException in my code:
Incorrect syntax near 'MatricNO'
Here is the code:
public static StudentDetail GetStudent(string MatricNO)
{
//Calling on the connection class and get connection method
SqlConnection connection = ConnectionClass.GetConnection();
//Sql select statement that reads from the database
string selectStatement = "SELECT MatricNO,Faculty,Department,Course,FirstName,MiddleName,LastName" +
"FROM StudentInfo" +
"WHERE MatricNO=#MatricNO";
SqlCommand selectCommand=new SqlCommand(selectStatement,connection);
selectCommand.Parameters.AddWithValue("#MatricNO", MatricNO);
try
{
connection.Open();
SqlDataReader reader = selectCommand.ExecuteReader(CommandBehavior.SingleRow);
if(reader.Read())
{
//Read the database information into the StudentDetail Class
StudentDetail studentDetail=new StudentDetail();
studentDetail.Studentmatricno = reader["MatricNO"].ToString();
studentDetail.Faculty = reader["Faculty"].ToString();
studentDetail.Dept = reader["Department"].ToString();
studentDetail.Course = reader["Course"].ToString();
studentDetail.Firstname = reader["FirstName"].ToString();
studentDetail.Middlename = reader["MiddleName"].ToString();
studentDetail.Surname = reader["LastName"].ToString();
return studentDetail; //return all that has been read to the student detail class
}
else
{
// return null if queried record does not exist
return null;
}
}
catch (SqlException ex)
{
throw ex;
}
finally
{
connection.Close();
}
}
Can anyone help me resolve this issue?
You need spaces between FROM and SELECT, and FROM and WHERE clause
string selectStatement = "SELECT MatricNO,Faculty,Department,Course,FirstName,MiddleName,LastName" +
" FROM StudentInfo" +
" WHERE MatricNO=#MatricNO";
Its always better to look at the generated SQL from string concatenation and trying it directly on DB.
Your SQL query needs spaces after the field list and table name:
string selectStatement =
"SELECT MatricNO,Faculty,Department,Course,FirstName,MiddleName,LastName " +
"FROM StudentInfo " +
"WHERE MatricNO=#MatricNO";
You could also use a verbatim string literal:
string selectStatement =
#"SELECT MatricNO,Faculty,Department,Course,FirstName,MiddleName,LastName
FROM StudentInfo
WHERE MatricNO=#MatricNO";