SQL inserting variables from different statements - c#

StringBuilder sb = new StringBuilder();
sb.Append("DECLARE #ControlPaneliD int");
sb.Append(" SET #ControlPaneliD=(SELECT ControlPanelID");
sb.Append(" FROM ControlPanelID");
sb.Append(" WHERE Name=#Name)");
sb.Append("DECLARE #UserName UniqueIdentifier");
sb.Append(" SET #UserName=(SELECT Name");
sb.Append(" FROM UsersID");
sb.Append(" WHERE UsersID=#UserID)");
sb.Append("INSERT INTO dbo.CP_Comments (ControlPanelID,Comments,Commentator)");
sb.Append(" VALUES(#ControlPaneliD,#Comment,#UserName)");
MembershipUser CurrentUser = Membership.GetUser();
Guid id = (Guid)CurrentUser.ProviderUserKey;
string myConnectionString = AllQuestionsPresented.connectionString;
using (SqlConnection conn = new SqlConnection(AllQuestionsPresented.connectionString))
{
conn.Open();
SqlCommand cmd = new SqlCommand(sb.ToString(), conn);
cmd.Parameters.Add("UserID", SqlDbType.UniqueIdentifier).Value = id;
cmd.Parameters.Add("Comment", SqlDbType.NVarChar).Value = TextBox1.Text;
cmd.Parameters.Add("Name", SqlDbType.NVarChar).Value = name; //string variable from my code
cmd.ExecuteNonQuery();
}
I am trying to do two select statements, put their results in their variables and insert their variables plus another variable into another insert statement..
I am not sure if I am doing it right, I would appreciate your help if you gave me some advice and some criticism on what I composed

In insert statement you inserting 3 parameters into 2 columns.
I think you should write stored procedure and pass parameters for it. You always "generating" sql code with string builder. And to change some functionality in stored procedure is much easier than find statement in code, change it and rebuild app.
SELECT ControlPanelID FROM ControlPanelID WHERE Name=#Name and SELECT Name FROM UsersID WHERE UsersID=#UserID looks strange. Do you really have tables with names ControlPanelID and UsersID?

Your method looks OK but it is recommended that you use stored procedure instead of passing query

This is really vulnerable to SQL-injection, especially this part:
cmd.Parameters.Add("Comment", SqlDbType.NVarChar).Value = TextBox1.Text;
This is very, very dangerous. I cannot imagine you haven't heard about this, but just in case, check out this wiki article: http://en.wikipedia.org/wiki/SQL_injection
If you really want/ need to execute raw SQL, always escape the values.
Have you considered using a strongly typed DAL like LINQ2SQL, DataEntities etc?

I would rather suggest putting that code in a stored procedure. Its not a good idea to put that in a string builder and execute it.

Related

Query updates all of my data instead of only the one I want

How do I make it so that my query only update the data I want?
Here's the current code
string query = string.Format("update Customer set title='{0}',[Name]='{1}'",titleComboBox2.Text,nameTextBox2.Text,"where ID="+idTextBox+"");
Apparently the last part of the query isn't working. Why it is that?
Because you didn't use any index argument as {2} for your third argument which is WHERE part.
That's why your query will be contain only update Customer set title='{0}',[Name]='{1}' part this will be update for your all rows since it doesn't have any filter.
Fun fact, you could see this as query if you would debug your code.
But more important
You should always use parameterized queries. This kind of string concatenations are open for SQL Injection attacks.
Let's assume you use ADO.NET;
using(var con = new SqlConnection(conString))
using(var cmd = con.CreateCommand())
{
cmd.CommandText = #"update Customer set title = #title, [Name] = #name
where ID = #id";
cmd.Paramter.Add("#title", SqlDbType.NVarChar).Value = titleComboBox2.Text;
cmd.Paramter.Add("#name", SqlDbType.NVarChar).Value = nameTextBox2.Text;
cmd.Paramter.Add("#id", SqlDbType.Int).Value = int.Parse(idTextBox.Text);
// I assumed your column types.
con.Open();
cmd.ExecuteNonQuery();
}
Currently your query does not use WHERE clause, because it is ignored by string.Format. You have 3 placeholder parameters, and you are using only {0} and {1}, so WHERE part is never added to the SQL query. Change your query to include WHERE clause, e.g. like this:
string query = string.Format("update Customer set title='{0}',[Name]='{1}' {2}",titleComboBox2.Text,nameTextBox2.Text,"where ID="+idTextBox.Text+"");
However, there is one very serious flaw in your code - it is vulnerable to SQL injection attack. There are hundreds of articles about it online, make sure to read about what that is and how to update your code accordingly (hint - parametrize queries)

Convert C# SQL Loop to Linq

I have a list Called ListTypes that holds 10 types of products. Below the store procedure loops and gets every record with the product that is looping and it stores it in the list ListIds. This is killing my sql box since I have over 200 users executing this constantly all day.
I know is not a good architecture to loop a sql statement, but this the only way I made it work. Any ideas how I can make this without looping? Maybe a Linq statement, I never used Linq with this magnitude. Thank you.
protected void GetIds(string Type, string Sub)
{
LinkedIds.Clear();
using (SqlConnection cs = new SqlConnection(connstr))
{
for (int x = 0; x < ListTypes.Count; x++)
{
cs.Open();
SqlCommand select = new SqlCommand("spUI_LinkedIds", cs);
select.CommandType = System.Data.CommandType.StoredProcedure;
select.Parameters.AddWithValue("#Type", Type);
select.Parameters.AddWithValue("#Sub", Sub);
select.Parameters.AddWithValue("#TransId", ListTypes[x]);
SqlDataReader dr = select.ExecuteReader();
while (dr.Read())
{
ListIds.Add(Convert.ToInt32(dr["LinkedId"]));
}
cs.Close();
}
}
}
Not a full answer, but this wouldn't fit in a comment. You can at least update your existing code to be more efficient like this:
protected List<int> GetIds(string Type, string Sub, IEnumerable<int> types)
{
var result = new List<int>();
using (SqlConnection cs = new SqlConnection(connstr))
using (SqlCommand select = new SqlCommand("spUI_LinkedIds", cs))
{
select.CommandType = System.Data.CommandType.StoredProcedure;
//Don't use AddWithValue! Be explicit about your DB types
// I had to guess here. Replace with the actual types from your database
select.Parameters.Add("#Type", SqlDBType.VarChar, 10).Value = Type;
select.Parameters.Add("#Sub", SqlDbType.VarChar, 10).Value = Sub;
var TransID = select.Parameters.Add("#TransId", SqlDbType.Int);
cs.Open();
foreach(int type in types)
{
TransID.Value = type;
SqlDataReader dr = select.ExecuteReader();
while (dr.Read())
{
result.Add((int)dr["LinkedId"]);
}
}
}
return result;
}
Note that this way you only open and close the connection once. Normally in ADO.Net it's better to use a new connection and re-open it for each query. The exception is in a tight loop like this. Also, the only thing that changes inside the loop this way is the one parameter value. Finally, it's better to design methods that don't rely on other class state. This method no longer needs to know about the ListTypes and ListIds class variables, which makes it possible to (among other things) do better unit testing on the method.
Again, this isn't a full answer; it's just an incremental improvement. What you really need to do is write another stored procedure that accepts a table valued parameter, and build on the query from your existing stored procedure to JOIN with the table valued parameter, so that all of this will fit into a single SQL statement. But until you share your stored procedure code, this is about as much help as I can give you.
Besides the improvements others wrote.
You could insert your ID's into a temp table and then make one
SELECT * from WhatEverTable WHERE transid in (select transid from #tempTable)
On a MSSQL this works really fast.
When you're not using a MSSQL it could be possible that one great SQL-Select with joins is faster than a SELECT IN. You have to test these cases by your own on your DBMS.
According to your comment:
The idea is lets say I have a table and I have to get all records from the table that has this 10 types of products. How can I get all of this products? But this number is dynamic.
So... why use a stored procedure at all? Why not query the table?
//If [Type] and [Sub] arguments are external inputs - as in, they come from a user request or something - they should be sanitized. (remove or escape '\' and apostrophe signs)
//create connection
string queryTmpl = "SELECT LinkedId FROM [yourTable] WHERE [TYPE] = '{0}' AND [SUB] = '{1}' AND [TRANSID] IN ({2})";
string query = string.Format(queryTmpl, Type, Sub, string.Join(", ", ListTypes);
SqlCommand select = new SqlCommand(query, cs);
//and so forth
To use Linq-to-SQL you would need to map the table to a class. This would make the query simpler to perform.

how to prevent an SQL Injection Attack?

Currently, I am creating an SQL Query by doing something like
string SQLQuery = "SELECT * FROM table WHERE ";
foreach(word in allTheseWords)
{
SQLQuery = SQLQuery + " column1 = '" + word + "' AND";
}
I understand that this can lead to an SQL Injection attack. I don't know how to pass an array as a parameter
where report in #allTheseWords
===========
I am using SQL Server 2012
Unfortunately, you cannot pass an array as a parameter without adding a user-defined type for table-valued parameters. The simplest way around this restriction is to create individually named parameters for each element of the array in a loop, and then bind the values to each of these elements:
string SQLQuery = "SELECT * FROM table WHERE column1 in (";
for(int i = 0 ; i != words.Count ; i++) {
if (i != 0) SQLQuery += ",";
SQLQuery += "#word"+i;
}
...
for(int i = 0 ; i != words.Count ; i++) {
command.Parameters.Add("#word"+i, DbType.String).Value = words[i];
}
You can also create a temporary table, insert individual words in it, and then do a query that inner-joins with the temp table of words.
Here is the recommendation from Microsoft:
Use Code Analysis to detect areas in your Visual Studio projects that are prone to sql injection;
Refer to the article on how to reduce risk of attack:
On short they talk about:
using a stored procedure.
using a parameterized command string.
validating the user input for both type and content before you build the command string.
Btw, you can enable static analysis as part of your build process and configure it so that when a security rule is broken, the build also breaks. Great way to make sure your team writes secure code!
Using ADO you can do it with the help of params
SqlConnection Con = new SqlConnection(conString);
SqlCommand Com = new SqlCommand();
string SQLQuery = "SELECT * FROM table WHERE ";
int i=1;
foreach(word in words)
{
Com.Parameters.Add("#word"+i.ToString(),SqlDbType.Text).Value = word;
SQLQuery = SQLQuery + " column1 = '#word"+i.ToString()+"' AND ";
i++;
}
Com.CommandText =SQLQuery;
For SQL Server, you'd use a Table-Valued Parameter. SQL has one structure that represents a collection of multiple items of the same type. It's called a table. It doesn't have arrays.
Of course, your supposed updated query:
where report in #allTheseWords
Isn't equivalent to your original query, but may be closer to the intent. In the query constructed using AND, you're saying that the same column, in the same row has to be equal to multiple different words. Unless all of the words are equal, this will never return any rows. The updated query answers whether any of the words match, rather than all.
You need to use prepared statements. The way those are handled is that you write your query and put placeholders for the values you want to use. Here's an example:
SELECT * FROM table WHERE column1 = #word
You then have to go through a prepare phase where the SQL engine knows it will need to bind parameters to the query. You can then execute the query. The SQL engine should know when and how to interpret the parameters you bind to your query.
Here's some code to do that:
SqlCommand command = new SqlCommand(null, rConn);
// Create and prepare an SQL statement.
command.CommandText = "SELECT * FROM table WHERE column1 = #word";
command.Parameters.Add ("#word", word);
command.Prepare();
command.ExecuteNonQuery();
I combine the use of params with HtmlEncoding(to get rid of special characters where not needed). Give that a shot.
using (SqlConnection conn = new SqlConnection(conString))
{
string sql = "SELECT * FROM table WHERE id = #id";
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
cmd.paramaters.AddWithValue("#id", System.Net.WebUtility.HtmlEncode(id));
conn.Open();
using (SqlDataReader rdr = cmd.ExecuteReader())
{
}
}
}

How to insert an integer into a database through command prompt

I am trying to insert a integer into a database in C# using the code below, but everytime I run the compiler informs me that my integer is not a valid column "Invalid Column Name UserID"
Does anyone have any insight on this? Thanks.
Console.WriteLine("Please enter a new User Id");
string line = Console.ReadLine();
int UserID;
if (int.TryParse(line, out UserID))
{
Console.WriteLine(UserID);
Console.ReadLine();
}
//Prepare the command string
string insertString = #"INSERT INTO tb_User(ID,f_Name, l_Name) VALUES (UserID,'Ted','Turner')";
First things first, I would get into the habit of using parameterised queries, if you are not planning to use stored procedures. In your example, I would:
using (var command = new SqlCommand("INSERT INTO tb_User(ID, f_Name, l_Name) VALUES (#id, #forename, #surname)", conn))
{
command.Parameters.AddWithValue("id", id);
command.Parameters.AddWithValue("forename", forename);
command.Parameters.AddWithValue("surname", surname);
command.ExecuteNonQuery();
}
Where id, forename, surname are the appropriate variables. Notice I am also using using blocks, this ensures that my objects are cleaned up after it has completed.
it is because the 'UserID' within your insertString : ..."VALUES (UserID"... is invalid.
you need to pass a value for the UserID such as: ..."VALUES ('myUserIDGoesHere'"...
Your string is not dynamically reading the variables. Use something like this:
string insertString =
string.Format(#"INSERT INTO
tb_User(ID,f_Name, l_Name) VALUES
({0},'{1}','{2}')", UserId, "Ted",
"Turner");
There are better ways depending on what kind of data access you're using, but this is just to make the point of how to correct the string.
The problem is the first argument in VALUES - it simply isn't defined. If this is meant to be the value the user has entered, then you need to add a parameter to the command and use that parameter in the SQL; for example:
cmd.Parameters.AddWithValue("#id", UserID);
An then use
VALUES(#id, ...
in the TSQL.
Also, generally you might want to have the system generate the unique id. A the simplest level this could have an IDENTITY defined (an automatic sequence).
Use a parameterized query:
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
using (var insertCommand = new SqlCommand(
#"INSERT INTO tb_User (ID, f_Name, l_Name)
VALUES (#ID, 'Ted', 'Turner')", connection))
{
insertCommand.Parameters.AddWithValue("#ID", userID);
insertCommand.ExecuteNonQuery();
}
}
To answer your question regardless of your approach, try:
string insertString = #"INSERT INTO tb_User(ID,f_Name, l_Name) VALUES ("
+ UserID + ",'Ted','Turner')";

i'm lost: what is wrong with this ado.net code?

well, the question is clear i hope, the code is this:
string sql = "delete from #tabelnaam";
SqlCommand sc = new SqlCommand();
sc.Connection = getConnection();
sc.CommandType = CommandType.Text;
sc.CommandText = sql;
SqlParameter param = new SqlParameter();
param.Direction = ParameterDirection.Input;
param.ParameterName = "#tabelnaam";
param.Value = tableName;
sc.Parameters.Add(param);
OpenConnection(sc);
sc.ExecuteScalar();
tableName is supplied to this function.
I get the exception:
Must declare the table variable #tabelnaam
IIRC, you cant use a substitute the table name for a parameter.
Rather build the SQL string containing the correct table name.
Make to changes
rather than using paramter use this
string sql = string.format( "delete from {0}",tableName);
make use of executenonquery intead of ExecuteScalar
sc.ExecuteNonQuery();
As mentioned by others, you can't parameterise the table name.
However, as you rightly mention in comments on other answers, using simple string manipulation potentialy introduces a SQL injection risk:
If your table name input is fro an untrusted source, such as user input, then using this:
string sql = string.format( "DELETE FROM {0}",tableName);
leaves you open to the table name "myTable; DROP DATABASE MyDb" being inserted, to give you:
DELETE FROM myDb; DROP DATABASE MyDB
The way round this is to delimit the table name doing something such as this:
string sql = string.format("DELETE FROM dbo.[{0}]", tableName);
in combination with checking that the input does not contain either '[' or ']'; you should probably check it also doesn't contain any other characters that can't be used as a table name, such as period and quotes.
I dont think you can parameterize the table name. From what I have read you can do it via Dynamic sql and calling sp_ExecuteSQL.
Your SQL is incorrect, you are deleting from a table variable yet you haven't defined that variable.
Update: as someone has pointed out, you are trying to dynamically build a query string but have inadvertantly used SQL parameters (these do not act as place holders for string literals).
More here:
Parameterise table name in .NET/SQL?
You cannot parameterise the table name, you have to inject it into the command text.
What you can and should do is protect yourself against SQL injection by delimiting the name thus:
public static string Delimit(string name) {
return "[" + name.Replace("]", "]]") + "]";
}
// Construct the command...
sc.CommandType = CommandType.Text;
sc.CommandText = "delete from " + Delimit(tableName);
sc.ExecuteNonQuery();
See here and here for more background info.

Categories