How to write: insert into table ALL except X, if statement - c#

Second question of all time on this community! I am a noob and my weakness are if statements within or amoungst loops and other if statements.
So here is my scenario. This method inserts anything into a database, but I want to validate something. Instead of adding anything into the database, I do not want anything entered in that begins with "LIFT", I want the method to skip over that line and proceed to the next one. Is there a way I can program this into this method? Or do I need to write a new method? Thanks a bunch!
public bool BatchInsert(string table, string[] values)
{
string statement = "INSERT INTO " + table + " VALUES(";
for (var i = 0; i < values.Length - 1; i++)
{
if(values[i].Contains("'")){
values[i] = values[i].Replace("'", "''");
}
statement += "'"+values[i]+"', ";
}
statement += "'" + values[values.Length - 1] + "');";
SqlCommand comm = new SqlCommand(statement, connectionPCICUSTOM);
try
{
comm.Connection.Open();
comm.ExecuteNonQuery();
}
catch (Exception e)
{
KaplanFTP.errorMsg = "Database error: " + e.Message;
}
finally
{
comm.Connection.Close();
}
return true;
}

A couple hints. Don't += string types as it slows down performance. I also prefer foreach loops as the code is cleaner and easier to read/less likely to mess up the index. Also make using of the using statement to ensure proper disposal.
Assuming you have a reference to System.Linq you can use the following. I didn't test it but it should work:
public bool BatchInsert(string table, IEnumerable<string> values)
{
var sql = new StringBuilder();
sql.Append("INSERT INTO " + table + " VALUES(");
var newValues = values.Where(x => !x.StartsWith("LIFT")).Select(x => string.Format("'{0}'", x.Replace("'", "''")));
sql.Append(string.Join("","", newValues.ToArray()));
sql.Append(")");
using (var comm = new SqlCommand(statement, connectionPCICUSTOM))
{
try
{
comm.Connection.Open();
comm.ExecuteNonQuery();
}
catch (Exception e)
{
KaplanFTP.errorMsg = "Database error: " + e.Message;
}
finally
{
comm.Connection.Close();
}
}
return true;
}

If your goal is to iterate through your collection of 'values', leaving values beginning with 'lift' and their corresponding columns untouched, you may have to revise the way your INSERT Query is constructed. You will add columns as needed, instead of assuming that each value will be accounted for. Basically, you will need to use the form:
INSERT INTO tablename (col1, col2...) VALUES (val1, val2...)
For example:
string statement = "INSERT INTO tablename ";
string columns = "(";
string values = "(";
for (var i = 0; i < values.Length - 1; i++)
{
//if values doesn't contain lift, add it to the statement
if(!values[i].contains("LIFT")){
//columnName is a collection of your db column names
columns += "'"+columnName[i]+"'";
values += "'"+values[i]+"'";
}
}
columns += ")";
values += ")";
statement += columns +" VALUES " + values;
Like some of the comments have stated, this approach opens you up to SQL injections. Use with caution.
EDIT : Sorry, I missed where you said 'starts with 'LIFT'. Revise the .contains() line to the following:
if(!values[i].StartsWith("LIFT")){

Related

What is the easiest way to update a batch of records using only one trip?

Normally you will do one update at a time inside a loop. If you have 100 records, you will have 100 trips to the server which is not desirable. How can I update a group of records with a single round trip to the database.
using System.Data.SqlClient;
for (int ii = 0; ii < ptList.Length; ii++) {
sql = #"update [CCVT].[dbo].[_tb_NCVT_Points] set PointDateTime = CONVERT(datetime, '"
+ ptList[ii]._dateDt.ToString("yyyy-MM-dd HH:mm:ss.FFF") + "', 121), PointStatus = '"
+ ptList[ii]._statStr + "', PointValue =" + ptList[ii]._valDoub.ToString()
+ " WHERE Pointkey = '" + ptList[ii]._pointName + "'; ";
theActiveConnection.Open();
SqlCommand cmd = new SqlCommand(sql, theActiveConnection);
try {
cmd.ExecuteNonQuery();
cmd.Dispose();
}
catch (Exception eX) {
//handle exceptions
}
}
Please do not down vote this question.
The question How can I update multiple rows in a table with SQL query?
did not ask for one trip, and their answer did not yield one trip!! Did you see the ExecuteNonQuery action is inside the loop?? That is not my question and that is not my answer!
foreach (DataGridViewRow row in dataGridView2.Rows)
{
cm.Parameters["#Qty"].Value = row.Cells[2].Value;
cm.Parameters["#Description"].Value = row.Cells[3].Value;
cm.Parameters["#Price"].Value = row.Cells[4].Value;
cm.ExecuteNonQuery();
}
cn.Close();
SqlCommand cmd = new SqlCommand("", theActiveConnection);
StringBuilder sql = new StringBuilder();
for (int ii = 0; ii < ptList.Length; ii++) {
sql.AppendLine("UPDATE [CCVT].[dbo].[_tb_NCVT_Points]");
sql.AppendLine($"SET PointDateTime = CONVERT(datetime, #PointDateTime{ii}, 121), PointStatus = #PointStatus{ii}, PointValue = #PointValue{ii}");
sql.AppendLine($"WHERE Pointkey = '#PointKey{ii};");
cmd.Parameters.AddWithValue($"#PointDateTime{ii}",ptList[ii]._dateDt.ToString("yyyy-MM-dd HH:mm:ss.FFF"));
cmd.Parameters.AddWithValue($"#PointStatus{ii}",ptList[ii]._statStr);
cmd.Parameters.AddWithValue($"#PointValue{ii}",ptList[ii]._valDoub.ToString());
cmd.Parameters.AddWithValue($"#Pointkey{ii}",ptList[ii]._pointName);
}
try {
cmd.CommandText = sql.ToString();
theActiveConnection.Open();
cmd.ExecuteNonQuery();
}
catch (Exception eX) {
//handle exceptions
}
finally {
cmd.Dispose();
theActiveConnection.Close();
}
There are many ways to handle this issue, depending on how close and how different the commands are. In your case I think this is best.
The best performing way is to use table valued parameters with a stored procedure
https://msdn.microsoft.com/en-us/library/bb675163(v=vs.110).aspx
The simple way is to just concat SQL Statements with semicolon using a StringBuilder or .... But be aware that this has limitations in length!!!
Why not use Dapper - a simple object mapper for .Net
var list = ptList.select(p => new{
DateTime = p._dateDt,
Status = p._statStr,
Value = p._valDoub,
Key = p._pointName
});
using(var connection = new SqlConnection...)
{
connection.open()
connection.Execute("update [CCVT].[dbo].[_tb_NCVT_Points] set PointDateTime = #DateTime, PointStatus = #Status, PointValue = #Value
WHERE Pointkey = #Key", list);
}
The straight answer was posted by Andrew, but it depends on where your data comes from.
If the list of points is already in the database, and you are querying it into a list (one SELECT with a loop to fill up the list), then calling the large number of updates, you should do directly an UPDATE FROM which combines the SELECT and UPDATE in a single statement. One single trip to the server, with no unneccessary rows over the network.

How to use Linq instead of SQL Injection query for custom search

I would like to use Linq instead of below hardcoded Sql Injection to search from SqlServer DATABASE TABLES. How to retrieve Dynamically generated web controls input texts in C# Linq and replace the entire Sql injection in Linq for searching.
My C# code:
protected void Search_Button_Click(object sender, EventArgs e)
{
try
{
Table maintable = Select.FindControl("dynamic_filter_table_id") as Table;
int rc = maintable.Rows.Count;
if (rc == 1)
{
DropDownList D1 = maintable.FindControl("MainDDL") as DropDownList;
if (D1.SelectedValue.Contains("decimal"))
{
TextBox T1 = maintable.FindControl("txtbox1") as TextBox;
TextBox T2 = maintable.FindControl("txtbox2") as TextBox;
SqlDataAdapter sql = new SqlDataAdapter("SELECT F.Col1,F.Col2,V.COL1, col2,col3, col4 , col5, cl6 FROM TABLE1 as V , TABL2 as F WHERE V.Col1 = F.Col1 AND " + DDL1.SelectedItem.Text + " >= " + T1.Text + " AND " + DDl1.SelectedItem.Text + " <= " + T2.Text, con);
DataSet data = new DataSet();
sql.Fill(data);
con.Close();
Session["DataforSearch_DDL"] = data.Tables[0];
}
}
}
catch
{
ImproperSearch();
}
}
Why don't you just rewrite your query to be SQL-injection safe? LINQ won't give you any benefit.
You can achieve that by doing two things.
The first is to secure the column names. That is accomplished by specifying which characters is allowed for column names (more secure than trying to figure out what characters is not allowed). In this case I remove everything but letters and digits. If you have column names which contains underscore, just add that to the check.
The next thing is to use parameterized queries. Each ADO.NET driver has built in support for that, so you just have to specify the value using cmd.Parameters.AddWithValue. By doing so the value isn't part of the query string and hence there is no potential SQL injection.
using (var con = yourConnectionFactory.Create())
{
using (var cmd = new SqlCommand(con))
{
var safeKey1 = OnlyLettersAndDigits(DDL1.SelectedItem.Text);
var safeKey2 = OnlyLettersAndDigits(DDL2.SelectedItem.Text);
cmd.CommandText = "SELECT F.Col1,F.Col2,V.COL1, col2,col3, col4 , col5, cl6 " +
" FROM TABLE1 as V , TABL2 as F WHERE V.Col1 = F.Col1 " +
" AND " + safeKey1 + " >= #text1 " +
" AND " + safeKey2 + " <= #text2 ";
cmd.Parameters.AddWithValue("text1", T1.Text);
cmd.Parameters.AddWithValue("text2", T2.Text);
var adapter = new SqlDataAdapter(cmd);
var data = new DataSet();
sql.Fill(data);
Session["DataforSearch_DDL"] = data.Tables[0];
}
}
public string OnlyLettersAndDigits(string value)
{
var stripped = "";
foreach (var ch in value)
{
if (char.IsLetterOrDigit(ch))
stripped += ch;
}
return stripped;
}
You can use store procedure for customer search, It will work for both ADO.Net and LINQ approach. Just create a SP and add it to your DBML file very simple.
Here is the link how you can use SP in LINQ
http://www.mssqltips.com/sqlservertip/1542/using-stored-procedures-with-linq-to-sql/

C# SQL Insert with a very large number of parameters

I have had a problem for a few days and nothing online seems to do it.
I have an SQL table that has 150 columns. I am reading data from an ODBC connection and I want to insert that data into the SQL table. Basically duplicate the ODBC table as SQL.
My problem is that if I put everything in a string and insert it I face a hell of a time with escape characters and exceptions that I can't figure out.
Is there a way to parametrize my insert values that doesn't involve me naming each and every on of them separatly.
This is what I have right now. Also, if anyone knows an easier way to move an ODBC table to an SQL form please let me know
private void fillTable(string tableName)
{
String query = "SELECT * FROM " + tableName;
OdbcCommand command = new OdbcCommand(query, Program.myConnection);
OdbcDataReader reader = command.ExecuteReader(CommandBehavior.SequentialAccess);
int columnCount = reader.FieldCount;
for (int i = 0; i < columnCount; i++)
{
SqlCommand sCommand = new SqlCommand("ALTER TABLE " + tableName + " ADD " + reader.GetName(i) + " varchar(MAX)", Program.myConnection2);
sCommand.ExecuteNonQuery();
}
string row="";
while (!reader.IsClosed)
{
try
{
row = "";
reader.Read();
for (int i = 0; i < columnCount; i++)
{
if (reader != null)
{
if (reader.GetString(i).Contains('\''))
{
Console.WriteLine("REPLACED QUOT");
String s = reader.GetString(i).Replace("\'", "A");
Console.WriteLine(s);
row += s;
}
else
{
row += "\'" + reader.GetString(i).Trim() + "\',";
}
// Console.WriteLine("FILLER: " + reader.GetString(i));
}
//Console.WriteLine(row);
}
//Console.WriteLine();
row = row.Substring(0, row.Length - 1);
SqlCommand insertCommand = new SqlCommand("INSERT INTO " + tableName + " VALUES(\'1\'," + row + ")", Program.myConnection2);
insertCommand.ExecuteNonQuery();
}
catch (SqlException exp)
{
Console.WriteLine(exp.StackTrace);
Console.WriteLine(row);
// this.Close();
}
}
Program.myConnection2.Close();
}
You can write a method that creats parameter names automatically, adds it to command, and returns the name so that you can use it in the query:
private int _paramCounter = 1;
private string CreateParameter(SqlCommand command, object value) {
string name = "#P" + _paramCounter.ToString();
_paramCounter++;
command.Parameters.AddWithValue(name, value);
return name;
}
Usage:
row += CreateParameter(insertCommand, reader.GetString(i).Trim()) + ",";
Note that you need to create the command object before you loop through the columns. Also, although not needed, you might want to reset the _paramCounter for each row, otherwise the parameter names get longer in the end.

Insert DateTime into Access

The problem:
I'm trying to insert a date time into an access database using the Oledb interface in C#.
Hacking solution: Generate my on insert string without using command.Properties
I can insert text into the database with no problem, but when trying datetime, I end up with this error: System.Data.OleDb.OleDbException {"Data type mismatch in criteria expression."}
There are several posts similar to this but alas with no working solution.
Here is my code:
void TransferData()
{
string instCmd = Get_InsertCommand(0); // hard coded table 0 for testing
Fill_ProductTable_ToInsert();
con.Open();
// It would be nice not to have to separate the date indexes
int[] textIndex = { 0, 1, 2, 3, 4, 7 };
int[] dateIndex = { 5, 6 };
try
{
foreach (DataRow row in DataToStore.Tables[0].Rows)
{
OleDbCommand command = new OleDbCommand();
command.Connection = con;
command.CommandText = instCmd;
foreach(int j in textIndex)
command.Parameters.AddWithValue("#" + j, row[j]);
foreach (int j in dateIndex)
{
// TESTING CODE
///////////////////////////////////////////////////////////////////////////
string input = "#\'" +DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss") +"\'#";
command.Parameters.AddWithValue("#" + j, input.ToString());
Program.WriteLine(input.ToString());
///////////////////////////////////////////////////////////////////////////
}
command.ExecuteNonQuery();
}
}
finally
{
con.Close();
}
}
string Get_InsertCommand(int i)
{
string sqlIns = "INSERT INTO " + DataToStore.Tables[0].TableName + " (";
string temp = "VALUES (";
for (int j = 0; j < expected_header[i].Length - 1; j++)
{
sqlIns += expected_header[i][j] + ", ";
temp += "#" + j + ", ";
}
int lastIndex = expected_header[i].Length -1;
sqlIns += expected_header[i][lastIndex] + ") ";
temp += "#" + lastIndex + ")";
sqlIns += temp;
return sqlIns;
}
Inside the area labeled testing code, I have tried every permutation of date time I could think of.
I tried every format with # and '
I tried these formats: yyyy-MM-dd, yyyyMMdd, yyyy\MM\dd, yyyy/MM/dd
I also tried ToOADate()
And ToString(), ToShortDateString()
I also tried setting the database to accept ANSI-92 Sql
I'm running out of ideas.
Note: This code is set up to deal with multiple tables from multiple databases, mind the loops...
Use parameters properly, and don't worry about the format of the datetime value that you concatenate in your query.
I don't understand why you want to convert the datetime value to a string value ?
DateTime theDate = new DateTime(2012,10,16);
var cmd = new OleDbCommand();
cmd.CommandText = "INSERT INTO sometable (column) VALUES (#p_bar)";
cmd.Parameters.Add ("#p_bar", OleDbType.DateTime).Value = theDate;
I was able to solve this issue by not using command properties. I generated my own sql input and set it to cmd.commandText. The text input for datetime to a data base is #yyyy-MM-dd#

adding a data source to the gridview dynamically in C#

Here i have tried to do bind the gridview as per the selection in the checkboxlist.
I'm getting only blank page when its executed. can any one specify what's the mistake?
protected void Button1_Click(object sender, EventArgs e)
{
String query = "Select Proj_id,Proj_name,Front_end,Back_end from Proj_details where Front_end = 'Android'";
String[] frontend ={ "Android", "Asp", "Asp.net", "C#.net", "J2EE", "Java", "Matlab", "NS2", "PHP", "VB", "VB.net" };
try
{
for (int i = 0; i <= CheckBoxList1.Items.Count; i++)
{
if (CheckBoxList1.Items[i].Selected)
query = query.Insert(query.Length, frontend[i] + "','");
}
con.Open();
query = query.Remove(query.Length - 1);
query = query.Remove(query.Length - 1);
query = query.Insert(query.Length, ")");
SqlDataAdapter sqlada = new SqlDataAdapter(query, con);
DataSet ds = new DataSet();
sqlada.Fill(ds, "Proj_details");
gv_search_project.DataSource = ds.Tables[0];
gv_search_project.DataBind();
}
catch (Exception ex)
{
}
finally
{
Label1.Text = query.ToString();
}
}
You're going out of the array here:
for (int i = 0; i <= CheckBoxList1.Items.Count; i++)
{
if (CheckBoxList1.Items[i].Selected)
query = query.Insert(query.Length, frontend[i] + "','");
}
You need to replace:
i <= CheckBoxList1.Items.Count
With:
i < CheckBoxList1.Items.Count
gv_search_project.DataSource = ds.Tables["Proj_details"];
You are building a query by appending additional Front_end values, but the base query you are appending to has a where clause like this:
where Front_end = 'Android'
So (if I read your code correctly) the resulting query is (for instance):
Select Proj_id,Proj_name,Front_end,Back_end
from Proj_details
where Front_end = 'Android'Android','C#.net','Matlab','VB')
To start with the query i simply broken. And secondly I think what you intended was to have an IN clause:
String query = "Select Proj_id,Proj_name,Front_end,Back_end from Proj_details where Front_end in ('Android','";
This, coupled with Amiram Korach's note about < vs <= should make the code work. But it will always include 'Android' (which you initial attempt also did).
Do change you empty catch clause though:
catch (Exception ex)
{
}
If you hadn't been swallowing all exceptions you would probably have found the problems yourself.

Categories

Resources