How to insert a datatable to an access database in C# - c#

I have 2 table in an access database
now I want to select from one table and insert them into another one.
this is my code but it shows an exception in line Cmd.ExecuteNonQuery();
{"Syntax error (missing operator) in query expression 'System.Object[]'."}
the code is :
public static void SetSelectedFeedIntoDB(Form2 frm2)
{
string StrCon = System.Configuration.ConfigurationManager.ConnectionStrings["FeedLibraryConnectionString"].ConnectionString;
OleDbConnection Connection = new OleDbConnection(StrCon);
OleDbDataAdapter DataA = new OleDbDataAdapter("Select * from FeedLibrary where ID=" + frm2.FeedSelectListBox.SelectedValue, Connection);
DataTable DTable = new DataTable();
DataA.Fill(DTable);
OleDbCommand Cmd = new OleDbCommand();
Cmd.Connection = Connection;
Connection.Open();
foreach (DataRow DR in DTable.Rows)
{
Cmd.CommandText = "insert into SelectedFeeds Values(" + DR.ItemArray + ")";
Cmd.ExecuteNonQuery();
}
Connection.Close();
}
what should I do to fix this?

Your error is caused by the fact that you are concatenating the ItemArray property of a DataRow to a string. In this case the ItemArray (that is an instance of an object[]) has no method that automatically produces a string from its values and thus returns the class name as a string "object[]" but of course this produces the meaningless sql string
"insert into SelectedFeeds Values(object[])";
But you could simply build a SELECT .... INTO statement that will do everything for you without using DataTables and Adapters
string cmdText = #"SELECT FeedLibrary.* INTO [SelectedFeeds]
FROM FeedLibrary
where ID=#id";
using(OleDbConnection Connection = new OleDbConnection(StrCon))
using(OleDbCommand cmd = new OleDbCommand(cmdText, Connection))
{
Connection.Open();
cmd.Parameters.Add("#id", OleDbType.Integer).Value = Convert.ToInt32( frm2.FeedSelectListBox.SelectedValue);
cmd.ExecuteNonQuery();
}
However, the SELECT ... INTO statement creates the target table but gives error if the target table already exists. To solve this problem we need to discover if the target exists. If it doesn't exist we use the first SELECT ... INTO query, otherwise we use a INSERT INTO ..... SELECT
// First query, this creates the target SelectedFeeds but fail if it exists
string createText = #"SELECT FeedLibrary.* INTO [SelectedFeeds]
FROM FeedLibrary
where ID=#id";
// Second query, it appends to SelectedFeeds but it should exists
string appendText = #"INSERT INTO SelectedFeeds
SELECT * FROM FeedLibrary
WHERE FeedLibrary.ID=#id";
using(OleDbConnection Connection = new OleDbConnection(StrCon))
using(OleDbCommand cmd = new OleDbCommand("", Connection))
{
Connection.Open();
// Get info about the SelectedFeeds table....
var schema = Connection.GetSchema("Tables",
new string[] { null, null, "SelectedFeeds", null});
// Choose which command to execute....
cmd.CommandText = schema.Rows.Count > 0 ? appendText : createText;
// Parameter #id is the same for both queries
cmd.Parameters.Add("#id", OleDbType.Integer).Value = Convert.ToInt32( frm2.FeedSelectListBox.SelectedValue);
cmd.ExecuteNonQuery();
}
Here we have two different queries, the first one create the SelectedFeeds table as before, the second one appends into that table.
To discover if the target table has already been created I call Connection.GetSchema to retrieve a datatable (schema) where there is a row if the table SelectedFeeds exists or no row if there is no such table.
At this point I set the OleDbCommand with the correct statement to execute.

Related

How does sanitize the sql parameter

I write these code all are working fine but there is a warning coming that sanitize the sql parameter.
private DataSet ExcelToDataSet(string fileData)
{
DataSet ds = new DataSet();
string connectionString = GetConnectionString(fileData);
using (OleDbConnection conn = new OleDbConnection(connectionString))
{
conn.Open();
OleDbCommand cmd = new OleDbCommand();
cmd.Connection = conn;
// Get all Sheets in Excel File
DataTable dtSheet = conn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
// Loop through all Sheets to get data
foreach (DataRow dr in dtSheet.Rows)
{
string sheetName = dr["TABLE_NAME"].ToString();
if (!sheetName.EndsWith("$"))
continue;
// Get all rows from the Sheet
cmd.CommandText = "SELECT * FROM [" + sheetName + "]";
DataTable dt = new DataTable();
dt.TableName = sheetName;
OleDbDataAdapter da = new OleDbDataAdapter(cmd);
da.Fill(dt);
ds.Tables.Add(dt);
}
cmd = null;
conn.Close();
}
return (ds);
}
I have to sanitize the following line
cmd.CommandText = "SELECT * FROM [" + sheetName + "]";
Usually, when writing SQL Statements, you need to use parameters to pass the data from user input into the sql statement, to prevent SQL Injection attacks. That's why you get this warning. However, there is nothing you can do about it since it's impossible to parameterize identifiers in SQL, and you don't need to do it because you are not concatenating user input, and you are not running this query on a database, so even if you could use SQL injection, the worst you can do is corrupt a single file
UPDATE: I did not notice this was a OleDbConnection, the database you are connecting to may not have the same functionality to quote an identifier. I am leaving this answer here in case someone comes across this question and needs the same thing but for a SQL connection.
As the others have said, there is no need to worry about the warning in this case as the data is not coming from user data.
However everyone is wrong about the fact you cannot parameterize an identifier. You need to build the query dynamically server side and use the QUOTENAME function but it is possible.
foreach (DataRow dr in dtSheet.Rows)
{
string sheetName = dr["TABLE_NAME"].ToString();
if (!sheetName.EndsWith("$"))
continue;
// Get all rows from the Sheet
cmd.CommandText = #"
declare #sql nvarchar(114);
set #sql = N'select * from ' + quotename(#sheetname)
exec sp_executesql #sql
";
cmd.Parameters.Clear();
cmd.Parameters.Add("#sheetname", SqlDbType.NVarChar, 100).Value = sheetName;
DataTable dt = new DataTable();
dt.TableName = sheetName;
OleDbDataAdapter da = new OleDbDataAdapter(cmd);
da.Fill(dt);
ds.Tables.Add(dt);
}
This will generate a dynamic query that will safely escape the name of the table.

"Data type mismatch in criteria expression" when trying to delete row from database

OleDbCommand system = new OleDbCommand();
system.CommandType = CommandType.Text;
system.CommandText = "DELETE FROM Student WHERE(ID= '" +
txtStudentIDnumber.Text + "')";
system.Connection = mydatabase;
mydatabase.Open();
system.ExecuteNonQuery();
dataGridView1.Update();
this.tableAdapterManager.UpdateAll(csharrpfinalprojectDataSet);
mydatabase.Close();
MessageBox.Show("Student Record Deleted.", "deleting record...");
In your command text you need to remove single quotes (') around the txtStudentIDnumber.Text as it appears ID is of type integer and you are passing it as string. Following should fix the error.
system.CommandText = "DELETE FROM Student WHERE(ID= " + txtStudentIDnumber.Text + ")";
EDIT: With respect to #mdb comments, you should always use Parameters in your query so that you can avoid SQL Injection. Consider the following:
OleDbCommand system = new OleDbCommand();
system.CommandType = CommandType.Text;
system.CommandText = "DELETE FROM Student WHERE ID = ?";
OleDbParameter parameter = new OleDbParameter("ID", txtStudentIDnumber.Text);
system.Parameters.Add(parameter);
system.Connection = mydatabase;
mydatabase.Open();
system.ExecuteNonQuery();
dataGridView1.Update();
OleDbCommand system = new OleDbCommand();
system.CommandType = CommandType.Text;
system.CommandText = "DELETE FROM Student WHERE ID=#ID";
system.Parameters.AddWithValue("#ID", txtStudentIDnumber.Text);
system.Connection = mydatabase;
mydatabase.Open();
system.ExecuteNonQuery();
dataGridView1.Update();
this.tableAdapterManager.UpdateAll(csharrpfinalprojectDataSet);
mydatabase.Close();
MessageBox.Show("Student Record Deleted.", "deleting record...");
What will happen when user input for txtStudentIDNumber is,
1 or 1=1
In that case hardcoded SQL string will be,
DELETE FROM Student WHERE(ID=1 or 1=1)
So prefer parameterized sql statement instead of hard-coded string.
using(OleDbConnection cn=new OleDbConnection(cnStr))
{
using(OleDbCommand cmd=new OleDbCommand())
{
cmd.CommandText="DELETE FROM Student WHERE ID=#ID";
cmd.Connection=cn;
cmd.Parameters.Add("#ID",SqlDbType.Int).Value=txtStudentIDnumber.Text;
cn.Open();
cmd.ExecuteNonQuery();
cn.Close();
}
}

Insert data into SQL Server from C# code

I have a table student (id, name). Then I have one textbox, for entering the name, when click on submit button, it inserts the data into the database. So how can I insert only to name, not id because id is auto increment?
I tried this
insert into student(id, name) values(,name)
but it is not insert to my table.
This is my code :
protected void Button1_Click(object sender, EventArgs e)
{
string test = txtName.Text;
SqlConnection conn = new SqlConnection(#"Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\Person.mdf;Integrated Security=True;User Instance=True");
string sql = "insert into student(name) values ('test')";
try
{
conn.Open();
SqlCommand cmd = new SqlCommand(sql, conn);
cmd.ExecuteNonQuery();
}
catch (System.Data.SqlClient.SqlException ex)
{
string msg = "Insert Error:";
msg += ex.Message;
}
finally
{
conn.Close();
}
}
INSERT INTO student (name) values ('name')
Omit the id column altogether, it will be populated automatically. To use your variable, you should parameterise your SQL query.
string sql = "INSERT INTO student (name) values (#name)";
conn.Open();
SqlCommand cmd = new SqlCommand(sql, conn);
cmd.Parameters.Add("#name", SqlDbType.VarChar);
cmd.Parameters["#name"].Value = test;
cmd.ExecuteNonQuery();
You should never attempt to do this by constructing a SQL string containing the input value, as this can expose your code to SQL injection vulnerabilities.
You better use parameters when you insert data.
try
{
string sql = "insert into student(name) values (#name)";
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
cmd.Parameters.AddWithValue("#name", test); // assign value to parameter
cmd.ExecuteNonQuery();
}
}
}
catch (SqlException ex)
{
string msg = "Insert Error:";
msg += ex.Message;
}
You don't need to mention the ID in first part.
insert into student(name) values('name')
I was facing this problem and after trying various solution found at stack overflow, i could summarize the experience as follows:
commands executed in command shell of mssql like:
insert into table_name (val1,val2,val3,val4) VALUES ("val1","val2",0,"val4")
go
or
insert into table_name VALUES ("val1","val2",0,"val4")
go
work when typed directly in the mssql database prompt,
But when it is required to use the the insert statement from c#, it is required to be kept in mind that string needs to be surrounded by an additional pair of single quites, around the strings, like in:
SqlConnection cnn;
string connetionString = "Data Source=server_name;Initial Catalog=database_name;User ID=User_ID;Password=Pass_word";
cnn = new SqlConnection(connetionString);
SqlCommand myCommand = new SqlCommand("insert into table_name (val1,val2,val3,val4) VALUES ('val1','val2',0,'val4');", cnn);
//or
//SqlCommand myCommand = new SqlCommand(insert into table_name VALUES ('val1','val2',0,'val4');", cnn);
cnn.Open();
myCommand.ExecuteNonQuery();
cnn.Close();
the problem here is that most people, like myself, try to use <\"> in the place of double quotes <">that is implemented as in the above command line case, and SQL executor fails to understand the meaning of this.
Even in cases where a string needs to be replace, ensure that strings are surrounded by single quotation, where a string concatination looks like a feasible solution, like in:
SqlCommand myCommand = new SqlCommand("insert into table_name (val1,val2,val3,val4) VALUES ('"+val1+"','val2',0,'val4');", cnn);
string sql = "INSERT INTO student (name) values (#name)";
conn.Open();
SqlCommand cmd = new SqlCommand(sql, conn);
cmd.Parameters.Add("#name", SqlDbType.VarChar);
cmd.Parameters["#name"].Value = test;
cmd.ExecuteNonQuery();
Try the following query,
insert into student(name) values(name)
SQL Server internally auto increments the id column when u insert the data since u said it is auto increment. If it is not working, the u have to check the identity column in the db.
use the key word "identity" to auto increment the id column
Refer : http://technet.microsoft.com/en-us/library/aa933196(v=sql.80).aspx
create table table_name( id int PRIMARY KEY IDENTITY )
and you no need to mention the "id" in the insert query

Dynamically passing a value inside a query?

I have two columns syntax and query in my table Table1. Syntax contains data called po and a query called select * from po_pomas_pur_order_hdr where pomas_pono =. I got this query value by using
SqlDataAdapter da = new SqlDataAdapter("select query from Table1 where syntax = '" + textBox1.Text + "'", conn);
And my problem is that I need to dynamically pass another value inside the query which I retrived using dataadapter like this:
SqlDataAdapter da1 = new SqlDataAdapter(da.tostring() +"'"+ textBox1.Text +"'", conn)
The resulting query should be like this:
select * from po_pomas_pur_order_hdr where pomas_pono = '2PO/000002/09-10'
But it is not possible. How to get a query like this? Any suggestion?
SqlDataAdapter is used to fill datasets and datatables. You cannot obtain the result of a query with ToString(). I think you want to use SqlCommand to execute your first query to retrieve the actual query to run from the database like this:
string query = null;
using (var command = new SqlCommand("select query from Table1 where syntax = #Syntax", conn))
{
command.Parameters.AddWithValue("#Syntax", textBox1.Text);
query = command.ExecuteScalar(); // this assumes only one query result is returned
}
Then you can use the data adapter to fill it:
SqlDataAdapter da1 = new SqlDataAdapter(query +"'"+ textBox1.Text +"'", conn);
Although I would suggest to use parameters for that as well.
in this way is more safe: dotnetperls
He check the "'" and the "\", check the type of the fields etc...
Code from the example above (is the same for insert delete and update):
using (SqlCommand command = new SqlCommand("SELECT * FROM Dogs1 WHERE Name LIKE #Name", connection))
{
//
// Add new SqlParameter to the command.
//
command.Parameters.Add(new SqlParameter("Name", dogName));
//
// Read in the SELECT results.
//
SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
int weight = reader.GetInt32(0);
string name = reader.GetString(1);
string breed = reader.GetString(2);
Console.WriteLine("Weight = {0}, Name = {1}, Breed = {2}", weight, name, breed);
}
}
I suggest you to use SqlParameters. Here is example how to use DataAdapter and parameters.
Provided that you have a DataSet you intend to fill using the adapter and that you adjust the queries to use parameters in order to avoid sql injection you should be able to use something like this:
string query;
using(var sqlCommand = new SqlCommand(
"select query from Table1 where syntax=#syntax", conn))
{
sqlCommand.Parameters.AddWithValue("syntax", textBox1.Text);
query = (string)sqlCommand.ExecuteScalar();
}
using(var dataAdapter = new SqlDataAdapter())
using(var dataCommand = new SqlCommand(query, conn))
{
dataCommand.Parameters.AddWithValue("parameter", poNumber);
dataAdapter.SelectCommand = dataCommand;
dataAdapter.Fill(myDataSet);
}

error 'there is already an open datareader associated with this command which must be closed first'

runtime error 'there is already an open datareader associated with this command which must be closed first'
objCommand = new SqlCommand("SELECT field1, field2 FROM sourcetable", objConn);
objDataReader = objCommand.ExecuteReader();
while (objDataReader.Read())
{
objInsertCommand = new SqlCommand("INSERT INTO tablename (field1, field2) VALUES (3, '" + objDataReader[0] + "')", objConn);
objInsertCommand.ExecuteNonQuery();//Here is the error
}
objDataReader.Close();
I cannot define any stored procedure here.
Any help would we appreciated.
No need to do all that, just turn on MARS and your problem will get solved. In your connection string just add MultipleActiveResultSets=True;
You can't perform an action on that connection while it's still working on reading the contents of a data reader - the error is pretty descriptive.
Your alternatives are:
1) Retrieve all your data first, either with a DataSet or use the reader to populate some other collection, then run them all at once after the initial select is done.
2) Use a different connection for your insert statements.
How about pulling the data into a DataSet via Fill and then iterate through that to perform your insert via NonQuery?
IDbDataAdapter da;
IDbCommand selectCommand = connection.CreateCommand();
selectCommand.CommandType = CommandType.Text;
selectCommand.CommandText = "SELECT field1, field2 FROM sourcetable";
connection.Open();
DataSet selectResults= new DataSet();
da.Fill(selectResults); // get dataset
selectCommand.Dispose();
IDbCommand insertCommand;
foreach(DataRow row in selectResults.Tables[0].Rows)
{
insertCommand = connection.CreateCommand();
insertCommand.CommandType = CommandType.Text;
insertCommand.CommandText = "INSERT INTO tablename (field1, field2) VALUES (3, '" + row["columnName"].ToString() + "'";
}
insertCommand.Dispose();
connection.Close();
Your best bet would be to read the information you need into a list and then iterating the list to perform your inserts like so:
List<String> values = new List<String>();
using(SqlCommand objCommand = new SqlCommand("SELECT field1, field2 FROM sourcetable", objConn)) {
using(SqlDataReader objDataReader = objCommand.ExecuteReader()) {
while(objDataReader.Read()) {
values.Add(objDataReader[0].ToString());
}
}
}
foreach(String value in values) {
using(SqlCommand objInsertCommand = new SqlCommand("INSERT INTO tablename (field1, field2) VALUES (3, '" + value + "')", objConn)) {
objInsertCommand.ExecuteNonQuery();
}
}
INSERT INTO tablename (field1, field2)
SELECT 3, field1 FROM sourcetable
A single SQL statement instead of one per insert. Not sure if this will work for your real-life problem, but for the example you provided, this is a much better query than doing them one at a time.
On a side note, make sure your code uses parameterized queries instead of accepting strings as-is inside the SQL statement - your sample is open to SQL injection.
Several suggestions have been given which work great, along with recommendations for improving the implementation. I hit the MARS limit due to existing code not cleaning up a reader so I wanted to put together a more respectable sample:
const string connectionString = #"server=.\sqlexpress;database=adventureworkslt;integrated security=true";
const bool useMARS = false;
using (var objConn = new System.Data.SqlClient.SqlConnection(connectionString + (useMARS ? ";MultipleActiveResultSets=True" : String.Empty)))
using (var objInsertConn = useMARS ? null : new System.Data.SqlClient.SqlConnection(connectionString))
{
objConn.Open();
if (objInsertConn != null)
{
objInsertConn.Open();
}
using (var testCmd = new System.Data.SqlClient.SqlCommand())
{
testCmd.Connection = objConn;
testCmd.CommandText = #"if not exists(select 1 from information_schema.tables where table_name = 'sourcetable')
begin
create table sourcetable (field1 int, field2 varchar(5))
insert into sourcetable values (1, 'one')
create table tablename (field1 int, field2 varchar(5))
end";
testCmd.ExecuteNonQuery();
}
using (var objCommand = new System.Data.SqlClient.SqlCommand("SELECT field1, field2 FROM sourcetable", objConn))
using (var objDataReader = objCommand.ExecuteReader())
using (var objInsertCommand = new System.Data.SqlClient.SqlCommand("INSERT INTO tablename (field1, field2) VALUES (3, #field2)", objInsertConn ?? objConn))
{
objInsertCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("field2", String.Empty));
while (objDataReader.Read())
{
objInsertCommand.Parameters[0].Value = objDataReader[0];
objInsertCommand.ExecuteNonQuery();
}
}
}
Option 1:
Must execute query and load data before running another query.
Option 2:
Add MultipleActiveResultSets=true to the provider part of your connection string. See the example below:
<add name="DbContext" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=dbName;Persist Security Info=True;User ID=userName;Password=password;MultipleActiveResultSets=True" providerName="System.Data.SqlClient" />
What version of SQL Server are you using? The problem might be with this:
(from http://msdn.microsoft.com/en-us/library/9kcbe65k.aspx)
When you use versions of SQL Server before SQL Server 2005, while the SqlDataReader is being used, the associated SqlConnection is busy serving the SqlDataReader. While in this state, no other operations can be performed on the SqlConnection other than closing it. This is the case until the Close method of the SqlDataReader is called.
So, if this is what's causing your problem, you should first read all the data, then close the SqlDataReader, and only after that execute your inserts.
Something like:
objCommand = new SqlCommand("SELECT field1, field2 FROM sourcetable", objConn);
objDataReader = objCommand.ExecuteReader();
List<object> values = new List<object>();
while (objDataReader.Read())
{
values.Add(objDataReader[0]);
}
objDataReader.Close();
foreach (object value in values)
{
objInsertCommand = new SqlCommand("INSERT INTO tablename (field1, field2) VALUES (3, '" + value + "')", objConn);
objInsertCommand.ExecuteNonQuery();
}
Adding this to connection string should fix the problem.
MultipleActiveResultSets=true
Try something like this:
//Add a second connection based on the first one
SqlConnection objConn2= new SqlConnection(objConn.connectionString))
SqlCommand objInsertCommand= new SqlCommand();
objInsertCommand.CommandType = CommandType.Text;
objInsertCommand.Connection = objConn2;
while (objDataReader.Read())
{
objInsertCommand.CommandText = "INSERT INTO tablename (field1, field2) VALUES (3, '" + objDataReader[0] + "')";
objInsertCommand.ExecuteNonQuery();
}
Best Solution:
There is only problem with your "CommandText" value. Let it be SP or normal Sql Query.
Check 1: The parameter value which you are passing in your Sql Query
is not changing and going same again and again in your ExecuteReader.
Check 2: Sql Query string is wrongly formed.
Check 3: Please create simplest code as follows.
string ID = "C8CA7EE2";
string myQuery = "select * from ContactBase where contactid=" + "'" + ID + "'";
string connectionString = ConfigurationManager.ConnectionStrings["CRM_SQL_CONN_UAT"].ToString();
SqlConnection con = new SqlConnection(connectionString);
con.Open();
SqlCommand cmd = new SqlCommand(myQuery, con);
DataTable dt = new DataTable();
dt.Load(cmd.ExecuteReader());
con.Close();
In order for it to be disposed easily i use the following coding-template :
`using (SqlConnection connection = new SqlConnection("your connection string"))
{
connection.Open();
using (SqlCommand cmd = connection.CreateCommand())
{
cmd.CommandText = "Select * from SomeTable";
using (SqlDataReader reader = cmd.ExecuteReader())
{
if(reader.HasRows)
{
while(reader.Read()){
// assuming that we've a 1-column(Id) table
int id = int.Parse(reader[0].ToString());
}
}
}
}
connection.Close()
}`

Categories