I've data in DataTable with 2 rows and 3 columns. I want to insert that data into Oracle table.
How can I insert? please give me with some example.
And also
How can I pass datatable to storedprocedure in ORACLE...
I pass datatable in below mensioned manner, but datatable type problem is comming. how can I solve this?
cmd.Parameters.Add("#Details",dtSupplier);
(OR)
cmd.Parameters.Add("Details", DbType.Single).Value = dtSupplier.ToString();
want to insert dataset or a datatable into ORACLE,
create an ORACLE data adapter.
create a command object for insertion,
set the CommandType to StoredProcedure.
Update command of the data adapter,
pass the dataset or datatable as parameter.
like this:
OracleDataAdapter da = new OracleDataAdapter();
OracleCommand cmdOra = new OracleCommand(StoredProcedureName, Connection);
cmdOra.CommandType = CommandType.StoredProcedure;
da.InsertCommand = cmdOra;
da.Update(dsDataSet);
OR
if above dont work than pass datatable as xml prameter than than process it
For details check : ADO.NET DataTable as XML parameter to an Oracle/SQL Server Database Stored Procedure
OR
Check this thread on Oracle site : Thread: Pass data table to Oracle stored procedure
Check existing answer : How to Pass datatable as input to procedure in C#?
I'm very late for this answer, but I elaborated a bit to have some more readable (I hope) code, and to avoid all those .ToString() for the values so nulls and other less common values can be handled; here it is:
public void Copy(String tableName, DataTable dataTable)
{
var insert = $"insert into {tableName} ({GetColumnNames(dataTable)}) values ({GetParamPlaceholders(dataTable)})";
using (var connection = /*a method to get a new open connection*/)
{
for (var row = 0; row < dataTable.Rows.Count; row++)
{
InsertRow(dataTable, insert, connection, row);
}
}
}
private static void InsertRow(DataTable dataTable, String insert, OracleConnection connection, Int32 row)
{
using (var command = new OracleCommand(insert, connection))
{
AssembleParameters(dataTable, command, row);
command.ExecuteNonQuery();
}
}
private static void AssembleParameters(DataTable dataTable, OracleCommand command, Int32 row)
{
for (var col = 0; col < dataTable.Columns.Count; col++)
{
command.Parameters.Add(ParameterFor(dataTable, row, col));
}
}
private static OracleParameter ParameterFor(DataTable dataTable, Int32 row, Int32 col)
{
return new OracleParameter(GetParamName(dataTable.Columns[col]), dataTable.Rows[row].ItemArray.GetValue(col));
}
private static String GetColumnNames(DataTable data) => (from DataColumn column in data.Columns select column.ColumnName).StringJoin(", ");
private static String GetParamPlaceholders(DataTable data) => (from DataColumn column in data.Columns select GetParamName(column)).StringJoin(", ");
private static String GetParamName(DataColumn column) => $":{column.ColumnName}_param";
Hope this can be still useful to somebody
The best idea would be follow the step mentioned below
Create a transaction
Begin the transaction
Loop through you data table
call your procedure
If no error occurred commit transaction
else roll back transaction
Regarding this part of your question:
cmd.Parameters.Add("#Details",dtSupplier);
(OR)
cmd.Parameters.Add("Details", DbType.Single).Value = dtSupplier.ToString();
What is the type of the "Details" parameter? Is it a Single? Then you would have to pick one (1) value from your DataTable and pass it to your parameter, something like dtSupplier.Rows[0]["col"].
If you use dtSupplier.ToString() you are just making a string of the entire DataTable (which i guess will always be the type name of DataTable).
First of all, you need to add Oracle.DataAccess.dll as reference in Visual Studio. In most cases, you can find this dll in the directory C:\ProgramData\Oracle11g\product\11.2.0\client_1\ODP.NET\bin\2.x\Oracle.DataAccess.dll
If just you need to insert the records from DataTable to Oracle table, then you can call the below function. Consider that your DataTable name is dt.
string error = "";
int noOfInserts = DataTableToTable(dt,out error);
1. Without using Oracle Parameters(special character non-safe)
The definition of the function is given below. Here, we are just making the query dynamic for passing this as a sql statement to the InsertWithQuery function.
public int DataTableToTable(DataTable dt,out string error)
{
error = "";
for (int i = 0; i < dt.Rows.Count; i++)
{
finalSql = "INSERT INTO TABLENAME SELECT ";
for (int j = 0; j < dt.Columns.Count; j++)
{
colValue += "'" + dt.Rows[i][j].ToString() + "',";
}
colValue = colValue.Remove(colValue.Length - 1, 1);
finalSql += colValue + " FROM DUAL";
InsertWithQuery(finalSql, out error);
if (error != "")
return error;
inserts++;
colValue = "";
}
}
The code for InsertWithQuery function is given below. Here, in the connection string you have to place you database details like Host,Username,Password etc.
public int InsertWithQuery(string query, out string error)
{
error = "";
int rowsInserted = 0;
if (error == "")
{
OracleConnection con = new OracleConnection("Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=)(PORT=)))(CONNECT_DATA=(SERVER=DEDICATED)(SID=)));User Id=;Password=");
OracleTransaction trans = con.BeginTransaction();
try
{
error = "";
OracleCommand cmd = new OracleCommand();
cmd.Transaction = trans;
cmd.Connection = con;
cmd.CommandText = query;
rowsInserted = cmd.ExecuteNonQuery();
trans.Commit();
con.Dispose();
return rowsInserted;
}
catch (Exception ex)
{
trans.Rollback();
error = ex.Message;
rowsInserted = 0;
}
finally
{
con.Dispose();
}
}
return rowsInserted;
}
2. With using Oracle Parameters(special character safe)
This can handle special characters like single quotes like scenarios in the column values.
public int DataTableToTable(DataTable dt,out string error)
{
error = "";
string finalSql = "";
List<string> colValue = new List<string>();
List<string> cols = new List<string>() {"COLUMN1","COLUMN2","COLUMN3"};
for (int i = 0; i < dt.Rows.Count; i++)
{
finalSql = "INSERT INTO TABLENAME(COLUMN1,COLUMN2,COLUMN3) VALUES(:COLUMN1,:COLUMN2,:COLUMN3) ";
for (int j = 0; j < dt.Columns.Count; j++)
{
colValue.Add(dt.Rows[i][j].ToString());
}
objDAL.InsertWithParams(finalSql,colValue,cols, out error);
if (error != "")
return error;
inserts++;
colValue.Clear();
}
}
And the InsertWithParams is given below
public string InsertWithParams(string sql, List<string> colValue, List<string> cols, out string error)
{
error = "";
try
{
OracleConnection con = new OracleConnection("Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=)(PORT=)))(CONNECT_DATA=(SERVER=DEDICATED)(SID=)));User Id=;Password=");
OracleCommand command = new OracleCommand(sql, con);
for (int i = 0; i < colValue.Count; i++)
{
command.Parameters.Add(new OracleParameter(cols[i], colValue[i]));
}
command.ExecuteNonQuery();
command.Connection.Close();
}
catch (Exception ex)
{
error = ex.Message;
}
return null;
}
try {
//Suppose you have DataTable dt
string connectionString = #"Provider=Microsoft.ACE.OLEDB.12.0;" +
#"Data Source='Give path of your access database file here';Persist Security Info=False";
OleDbConnection dbConn = new OleDbConnection(connectionString);
dbConn.Open();
using (dbConn)
{
int j = 0;
for (int i = 0; i < 2; i++)
{
OleDbCommand cmd = new OleDbCommand(
"INSERT INTO Participant_Profile ([column1], [column2] , [column3] ) VALUES (#c1 , #c2 , #c3 )", dbConn);
cmd.Parameters.AddWithValue("#c1", dt.rows[i][j].ToString());
cmd.Parameters.AddWithValue("#c2", dt.rows[i][j].ToString());
cmd.Parameters.AddWithValue("#c3", dt.rows[i][j].ToString());
cmd.ExecuteNonQuery();
j++;
}
}
}
catch (OleDbException exception)
{
Console.WriteLine("SQL Error occured: " + exception);
}
I know it's been a big WHILE upon the matter, but the same need: "to insert data from a datatable to an Oracle table" has happened to me. I found this thread. I also tried the answers and came to the conclusion that executing a
...
cmd.ExecuteNonQuery();
...
in a loop, is bad. Reeaaally bad. The first thing that is bad is performance, the second is unnecessary complexity, the third is unnecessary Oracle Objects (stored proc). The time it takes to complete, lets say 200 rows, is almost 1 minute and that's me rounding it down. So in the hope that someone else will find this helpful here's my experience.
I got stubborn and searched some more, so I found out this, true it's from 2018. But I'm in 2021 myself...
So the base code is:
using Oracle.ManagedDataAccess.Client; // you don't need other dll, just install this from nuget gallery
using System.Data;
public static void Datatable2Oracle(string tableName, DataTable dataTable)
{
string connString = "connection string";
OracleBulkCopy copy= new(connString, OracleBulkCopyOptions.UseInternalTransaction /*I don't know what this option does*/);
copy.DestinationTableName = tableName;
copy.WriteToServer(dataTable);
copy.Dispose();
}
This should match a raw oracle DDL performance:
create table table_name as select * from other_table_name
Related
I have an error doing either a DELETE or INSERT using a C# SqlCommand; database is SQL server.
The SQL statement is
DELETE FROM [table_name]
WHERE id = 'some_id';
which is pretty simple.
id is a column name with datatype varchar(15) that can be NULL; some_id has 15 characters.
First, to verify that only 1 row with that ID exists, this SELECT query was made that returns no errors
SELECT *
FROM [table_name]
WHERE id = 'some_id';
returns 1 row
The error on the DELETE is
System.Data.SqlClient.SqlException (0x80131904): Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
This error can happen when more than 1 row satisfy the criteria of the query; clearly not the case here
What is puzzling is that doing the same DELETE query using the C ODBC API there is no error; so it seems the problem is in the C# code
C# code that calls DELETE:
public int execute(string sql)
{
SqlConnection conn = new SqlConnection(conn_str);
try
{
conn.Open();
SqlCommand command = new SqlCommand(sql, conn);
command.ExecuteNonQuery();
conn.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
return -1;
}
return 0;
}
C# code that calls SELECT:
public List<List<string>> select(string sql)
{
Stopwatch sw = new Stopwatch();
sw.Start();
List<List<string>> rows = null;
SqlConnection conn = new SqlConnection(conn_str);
try
{
conn.Open();
SqlDataReader reader = null;
SqlCommand command = new SqlCommand(sql, conn);
reader = command.ExecuteReader();
int nbr_cols = reader.FieldCount;
int nbr_rows = 0;
rows = new List<List<string>>();
while (reader.Read())
{
nbr_rows++;
List<string> col = new List<string>();
for (int idx_col = 0; idx_col < nbr_cols; idx_col++)
{
string str = reader[idx_col].ToString();
col.Add(str);
}
rows.Add(col);
}
reader.Close();
conn.Close();
sw.Stop();
Console.WriteLine("Time to get {0} rows: {1} msec", nbr_rows, sw.ElapsedMilliseconds);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
return rows;
}
C++ DELETE code with no error
std::string sql = "DELETE FROM [some_table] WHERE ID='some_id';";
if (query.exec_direct(sql) < 0)
{
std::cout << sql;
}
where the function exec_direct is here
https://github.com/pedro-vicente/lib_odbc/blob/master/odbc.cc
thanks
EDIT
adding link to DELETE documentation
https://learn.microsoft.com/en-us/sql/t-sql/statements/delete-transact-sql?view=sql-server-2017
This question already has answers here:
Passing a varchar full of comma delimited values to a SQL Server IN function
(27 answers)
Closed 5 years ago.
I have a stored procedure that uses the IN statement in the select condition.
SELECT *
FROM vwCashTransactions
WHERE TransactionTimeStamp BETWEEN '2017-01-30 ' AND '2017-12-01'
AND Country IN ('MY', 'BD')
ORDER BY TransactionTimeStamp DESC
I need to pass the country string from backend code.
This is the code I have written
if (manageCountries != null && manageCountries.Trim().Length > 0)
{
string[] words = manageCountries.Split(',');
string queryManageString = "";
int i = 0;
foreach (string word in words)
{
if (i != 0)
{
queryManageString += "','";
}
i++;
queryManageString += "'" + word + "'";
}
_DataTable = Global.DatabaseServices.GetTransactionReport("", startPeriod, endPeriod, queryManageString);
Somehow I am not getting the values. I am sure the issue is with the querymanageString. The way it is built is missing something. Can someone give an idea how I can achieve it?
Here's the code for calling the database:
public DataTable GetTransactionReport(string AccountCode, DateTime FromDate, DateTime ToDate, string ManagedCountry)
{
DataTable dataTable = new DataTable();
SqlCommand sqlCommand = new SqlCommand();
sqlCommand.CommandText = "[GetTransactionReport]";
sqlCommand.CommandType = CommandType.StoredProcedure;
sqlCommand.Parameters.AddWithValue("#AccountCode", AccountCode);
sqlCommand.Parameters.AddWithValue("#FromDate", FromDate);
sqlCommand.Parameters.AddWithValue("#ToDate", ToDate);
sqlCommand.Parameters.AddWithValue("#ManagedCountry", ManagedCountry);
sqlCommand.CommandTimeout = 300;
ExecuteQuery(dataTable, sqlCommand);
sqlCommand.Dispose();
return dataTable;
}
public int ExecuteQuery(DataTable dt, SqlCommand cmd)
{
int rowCount = 0;
SqlDataAdapter da = null;
try
{
if (cmd.Connection == null)
cmd.Connection = GetSqlConnection();
da = new SqlDataAdapter();
da.SelectCommand = cmd;
rowCount = da.Fill(dt);
}
catch (Exception ex)
{
throw new DatabaseException(ex);
}
finally
{
cmd.Connection.Close();
cmd.Connection.Dispose();
cmd.Connection = null;
da.Dispose();
}
return rowCount;
}
It's not very clear how you pass the parameters, but it seems that you pass a delimited string. This will not work. Your procedure needs a list of country ids, not a string with a delimiter.
You can either do some magic in the stored procedure, splitting string and stuff like that, or create your own type.
Try something like this:
CREATE TYPE [dbo].[StringList] AS TABLE
([StringValue] [varchar](200) NULL)
Then your stored procedure has a parameter of type StringList, which can be used just like a normal table:
ALTER PROCEDURE [dbo].[MySproc]
#ids AS dbo.StringList READONLY
AS
BEGIN
SET NOCOUNT ON;
...etc..
And, finally, in your code use a DataTable for the values:
DataTable idsDT = new DataTable();
idsDT.Columns.Add("StringValue", typeof(string));
// fill datatable here
And the command parameter should be SqlDbType.Structured
var cmd = new SqlCommand(....)
SqlParameter countryParam = cmd.Parameter.AddWithValue("ids", idsDT);
countryParam.SqlDbType = SqlDbType.Structured;
It seems, there is something wrong in your for loop where you create comma seperated single quote string. Update for loop with below:
string[] words = manageCountries.Split(',');
string queryManageString = "";
int i = 0;
foreach (string word in words)
{
if (i != 0)
{
queryManageString += ",'" + word + "'";
}
else
{
queryManageString += "'" + word + "'";
}
i++;
}
OR if you don't want to go with for loop, here is one line solution
queryManageString = string.Join(",", words.Select(x => string.Format("'{0}'", x)));
in my code the user can upload an excel document wish contains it's phone contact list.Me as a developer should read that excel file turn it into a dataTable and insert it into the database .
The Problem is that some clients have a huge amount of contacts like saying 5000 and more contacts and when i am trying to insert this amount of data into the database it's crashing and giving me a timeout exception.
What would be the best way to avoid this kind of exception and is their any code that can reduce the time of the insert statement so the user don't wait too long ?
the code
public SqlConnection connection = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString);
public void Insert(string InsertQuery)
{
SqlDataAdapter adp = new SqlDataAdapter();
adp.InsertCommand = new SqlCommand(InsertQuery, connection);
if (connection.State == System.Data.ConnectionState.Closed)
{
connection.Open();
}
adp.InsertCommand.ExecuteNonQuery();
connection.Close();
}
protected void submit_Click(object sender, EventArgs e)
{
string UploadFolder = "Savedfiles/";
if (Upload.HasFile) {
string fileName = Upload.PostedFile.FileName;
string path=Server.MapPath(UploadFolder+fileName);
Upload.SaveAs(path);
Msg.Text = "successfully uploaded";
DataTable ValuesDt = new DataTable();
ValuesDt = ConvertExcelFileToDataTable(path);
Session["valuesdt"] = ValuesDt;
Excel_grd.DataSource = ValuesDt;
Excel_grd.DataBind();
}
}
protected void SendToServer_Click(object sender, EventArgs e)
{
DataTable Values = Session["valuesdt"] as DataTable ;
if(Values.Rows.Count>0)
{
DataTable dv = Values.DefaultView.ToTable(true, "Mobile1", "Mobile2", "Tel", "Category");
double Mobile1,Mobile2,Tel;string Category="";
for (int i = 0; i < Values.Rows.Count; i++)
{
Mobile1 =Values.Rows[i]["Mobile1"].ToString()==""?0: double.Parse(Values.Rows[i]["Mobile1"].ToString());
Mobile2 = Values.Rows[i]["Mobile2"].ToString() == "" ? 0 : double.Parse(Values.Rows[i]["Mobile2"].ToString());
Tel = Values.Rows[i]["Tel"].ToString() == "" ? 0 : double.Parse(Values.Rows[i]["Tel"].ToString());
Category = Values.Rows[i]["Category"].ToString();
Insert("INSERT INTO client(Mobile1,Mobile2,Tel,Category) VALUES(" + Mobile1 + "," + Mobile2 + "," + Tel + ",'" + Category + "')");
Msg.Text = "Submitied successfully to the server ";
}
}
}
You can try SqlBulkCopy to insert Datatable to Database Table
Something like this,
using (SqlBulkCopy bulkCopy = new SqlBulkCopy(sqlConnection, SqlBulkCopyOptions.KeepIdentity))
{
bulkCopy.DestinationTableName = DestTableName;
string[] DtColumnName = YourDataTableColumns;
foreach (string dbcol in DbColumnName)//To map Column of Datatable to that of DataBase tabele
{
foreach (string dtcol in DtColumnName)
{
if (dbcol.ToLower() == dtcol.ToLower())
{
SqlBulkCopyColumnMapping mapID = new SqlBulkCopyColumnMapping(dtcol, dbcol);
bulkCopy.ColumnMappings.Add(mapID);
break;
}
}
}
bulkCopy.WriteToServer(YourDataTableName.CreateDataReader());
bulkCopy.Close();
}
For more Read http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlbulkcopy.aspx
You are inserting 1 row at a time, which is very expensive for this amount of data
In those cases you should use bulk insert, so the round trip to DB will be only once, if you need to roll back - all is the same transaction
You can use SqlBulkCopy which is more work, or you can use the batch update feature of the SqlAdpater. Instead of creating your own insert statement, then building a sqladapter, and then manually executing it, create a dataset, fill it, create one sqldataadpater, set the number of inserts in a batch, then execute the adapter once.
I could repeat the code, but this article shows exactly how to do it: http://msdn.microsoft.com/en-us/library/kbbwt18a%28v=vs.80%29.aspx
protected void SendToServer_Click(object sender, EventArgs e)
{
DataTable Values = Session["valuesdt"] as DataTable ;
if(Values.Rows.Count>0)
{
DataTable dv = Values.DefaultView.ToTable(true, "Mobile1", "Mobile2", "Tel", "Category");
//Fix up default values
for (int i = 0; i < Values.Rows.Count; i++)
{
Values.Rows[i]["Mobile1"] =Values.Rows[i]["Mobile1"].ToString()==""?0: double.Parse(Values.Rows[i]["Mobile1"].ToString());
Values.Rows[i]["Mobile2"] = Values.Rows[i]["Mobile2"].ToString() == "" ? 0 : double.Parse(Values.Rows[i]["Mobile2"].ToString());
Values.Rows[i]["Tel"] = Values.Rows[i]["Tel"].ToString() == "" ? 0 : double.Parse(Values.Rows[i]["Tel"].ToString());
Values.Rows[i]["Category"] = Values.Rows[i]["Category"].ToString();
}
BatchUpdate(dv,1000);
}
}
public static void BatchUpdate(DataTable dataTable,Int32 batchSize)
{
// Assumes GetConnectionString() returns a valid connection string.
string connectionString = GetConnectionString();
// Connect to the database.
using (SqlConnection connection = new SqlConnection(connectionString))
{
// Create a SqlDataAdapter.
SqlDataAdapter adapter = new SqlDataAdapter();
// Set the INSERT command and parameter.
adapter.InsertCommand = new SqlCommand(
"INSERT INTO client(Mobile1,Mobile2,Tel,Category) VALUES(#Mobile1,#Mobile2,#Tel,#Category);", connection);
adapter.InsertCommand.Parameters.Add("#Mobile1",
SqlDbType.Float);
adapter.InsertCommand.Parameters.Add("#Mobile2",
SqlDbType.Float);
adapter.InsertCommand.Parameters.Add("#Tel",
SqlDbType.Float);
adapter.InsertCommand.Parameters.Add("#Category",
SqlDbType.NVarchar, 50);
adapter.InsertCommand.UpdatedRowSource = UpdateRowSource.None;
// Set the batch size.
adapter.UpdateBatchSize = batchSize;
// Execute the update.
adapter.Update(dataTable);
}
}
I know this is a super old post, but you should not need to use the bulk operations explained in the existing answers for 5000 inserts. Your performance is suffering so much because you close and reopen the connection for each row insert. Here is some code I have used in the past that keeps one connection open and executes as many commands as needed to push all the data to the DB:
public static class DataWorker
{
public static Func<IEnumerable<T>, Task> GetStoredProcedureWorker<T>(Func<SqlConnection> connectionSource, string storedProcedureName, Func<T, IEnumerable<(string paramName, object paramValue)>> parameterizer)
{
if (connectionSource is null) throw new ArgumentNullException(nameof(connectionSource));
SqlConnection openConnection()
{
var conn = connectionSource() ?? throw new ArgumentNullException(nameof(connectionSource), $"Connection from {nameof(connectionSource)} cannot be null");
var connState = conn.State;
if (connState != ConnectionState.Open)
{
conn.Open();
}
return conn;
}
async Task DoStoredProcedureWork(IEnumerable<T> workData)
{
using (var connection = openConnection())
using (var command = connection.CreateCommand())
{
command.CommandType = CommandType.StoredProcedure;
command.CommandText = storedProcedureName;
command.Prepare();
foreach (var thing in workData)
{
command.Parameters.Clear();
foreach (var (paramName, paramValue) in parameterizer(thing))
{
command.Parameters.AddWithValue(paramName, paramValue ?? DBNull.Value);
}
await command.ExecuteNonQueryAsync().ConfigureAwait(false);
}
}
}
return DoStoredProcedureWork;
}
}
This was actually from a project where I was gathering emails for a restriction list, so kind of relevant example of what a parameterizer argument might look like and how to use the above code:
IEnumerable<(string,object)> RestrictionToParameter(EmailRestriction emailRestriction)
{
yield return ("#emailAddress", emailRestriction.Email);
yield return ("#reason", emailRestriction.Reason);
yield return ("#restrictionType", emailRestriction.RestrictionType);
yield return ("#dateTime", emailRestriction.Date);
}
var worker = DataWorker.GetStoredProcedureWorker<EmailRestriction>(ConnectionFactory, #"[emaildata].[AddRestrictedEmail]", RestrictionToParameter);
await worker(emailRestrictions).ConfigureAwait(false);
I am trying to insert multiple records into a table in one query using the MySqlCommand object in C# (using the MySQL Connector library).
The only way I know how to do this is by dynamically constructing the query myself and setting command.CommandType = CommandType.Text;
The problem with this method is that the fields are not escaped for quotes and such. I could write a function to escape the values myself I guess, but every article or question I have read on the internet appears to frown upon this, and says use command.Parameters as this more efficient and thorough.
My problem is that I don't know how to set the parameters for multiple rows. How can I do that?
Edit: This is for a commercial service which runs 24/7, so I need to find the most efficient way to do this. I'm not using stored procedures - is this is the only way or is there another?
public static string MySqlEscape(object value)
{
string val = value.ToString();
if (val.Contains("'"))
return val.Replace("'", "' + NCHAR(96) + '");
else
return val;
}
public void InsertProcessedData(long unprocessedID, long pagerID, long firmwareRelativeProtocolID, DataTable processedData)
{
using(processedData)
{
string paramColNames = string.Empty;
for(int i =1;i<=processedData.Columns.Count;i+=1)
{
paramColNames+=string.Format("Param{0}",i);
if(i!=processedData.Columns.Count)
paramColNames+=",";
}
string SQL = "INSERT INTO gprs_data_processed (#UnprocessedID,#PagerID,#FirmwareRelativeProtocolID,"+paramColNames+") VALUES ";
for (int i = 0; i < processedData.Rows.Count;i+=1)
{
SQL += string.Format("({0},{1},{2},", unprocessedID, pagerID, firmwareRelativeProtocolID);
for (int c = 0; c < processedData.Columns.Count; c += 1)
{
SQL += string.Format("'{0}'", MySqlEscape(processedData.Rows[i][c]));
if (i != processedData.Columns.Count)
SQL += ",";
}
SQL+=")";
if (i + 1 != processedData.Rows.Count)
SQL += ",";
else
SQL += ";";
}
using (MySqlConnection connection = new MySqlConnection(_connection))
{
connection.Open();
using (MySqlCommand command = connection.CreateCommand())
{
command.CommandType = CommandType.Text;
command.CommandText = SQL;
command.ExecuteNonQuery();
}
connection.Close();
}
}
}
I'm not sure on how to create a single command. What I do is create a method that uses parameters and then pass in the values that I want to run one at a time.
My method:
public void Insert(string strSQL, string[,] parameterValue)
{
//open connection
if (this.OpenConnection() == true)
{
//create command and assign the query and connection from the constructor
MySqlCommand cmd = new MySqlCommand(strSQL, connection);
//add parameters
for (int i = 0; i < (parameterValue.Length / 2); i++)
{
cmd.Parameters.AddWithValue(parameterValue[i, 0], parameterValue[i, 1]);
}
//Execute command
cmd.ExecuteNonQuery();
//close connection
this.CloseConnection();
}
}
I have ended up just using the function I have written already as it does it all in one command, and use:
public static string MySqlEscape(object value)
{
string val = value.ToString();
if (val.Contains("'"))
return val.Replace("'", "''");
else
return val;
}
It works fine so I will see how things go.
I've got a short piece of code that originally created an SqlDataAdapter object over and over.
Trying to streamline my calls a little bit, I replaced the SqlDataAdapter with an SqlCommand and moved the SqlConnection outside of the loop.
Now, whenever I try to edit rows of data returned to my DataTable, I get a ReadOnlyException thrown that was not thrown before.
NOTE: I have a custom function that retrieves the employee's full name based on their ID. For simplicity here, I used "John Doe" in my example code below to demonstrate my point.
ExampleQueryOld works with the SqlDataAdapter; ExampleQueryNew fails with the ReadOnlyException whenever I try to write to an element of the DataRow:
ExampleQueryOld
This works and has no issues:
public static DataTable ExampleQueryOld(string targetItem, string[] sqlQueryStrings) {
DataTable bigTable = new DataTable();
for (int i = 0; i < sqlQueryStrings.Length; i++) {
string sqlText = sqlQueryStrings[i];
DataTable data = new DataTable(targetItem);
using (SqlDataAdapter da = new SqlDataAdapter(sqlText, Global.Data.Connection)) {
try {
da.Fill(data);
} catch (Exception err) {
Global.LogError(_CODEFILE, err);
}
}
int rowCount = data.Rows.Count;
if (0 < rowCount) {
int index = data.Columns.IndexOf(GSTR.Employee);
for (int j = 0; j < rowCount; j++) {
DataRow row = data.Rows[j];
row[index] = "John Doe"; // This Version Works
}
bigTable.Merge(data);
}
}
return bigTable;
}
ExampleQueryNew
This example throws the ReadOnlyException:
public static DataTable ExampleQueryNew(string targetItem, string[] sqlQueryStrings) {
DataTable bigTable = new DataTable();
using (SqlConnection conn = Global.Data.Connection) {
for (int i = 0; i < sqlQueryStrings.Length; i++) {
string sqlText = sqlQueryStrings[i];
using (SqlCommand cmd = new SqlCommand(sqlText, conn)) {
DataTable data = new DataTable(targetItem);
try {
if (cmd.Connection.State == ConnectionState.Closed) {
cmd.Connection.Open();
}
using (SqlDataReader reader = cmd.ExecuteReader()) {
data.Load(reader);
}
} catch (Exception err) {
Global.LogError(_CODEFILE, err);
} finally {
if ((cmd.Connection.State & ConnectionState.Open) != 0) {
cmd.Connection.Close();
}
}
int rowCount = data.Rows.Count;
if (0 < rowCount) {
int index = data.Columns.IndexOf(GSTR.Employee);
for (int j = 0; j < rowCount; j++) {
DataRow row = data.Rows[j];
try {
// ReadOnlyException thrown below: "Column 'index' is read only."
row[index] = "John Doe";
} catch (ReadOnlyException roErr) {
Console.WriteLine(roErr.Message);
}
}
bigTable.Merge(data);
}
}
}
}
return bigTable;
}
Why can I write to the DataRow element in one case, but not in the other?
Is it because the SqlConnection is still open or is the SqlDataAdapter doing something behind the scene?
using DataAdapter.Fill does not load the database schema, which includes whether a column is a primary key or not, and whether a column is read-only or not. To load the database schema, use DataAdapter.FillSchema, but then that's not your questions.
using DataReader to fill a table loads the schema. So, the index column is read-only (probably because it's the primary key) and that information is loaded into the DataTable. Thereby preventing you from modifying the data in the table.
I think #k3b got it right; by setting ReadOnly = false, you should be able to write to the data table.
foreach (System.Data.DataColumn col in tab.Columns) col.ReadOnly = false;
I kept getting the same exception while trying different approaches. What finally worked for me was to set the column's ReadOnly property to false and change the value of the Expression column instead of row[index] = "new value";
In VB, don't pass a read-only DataRow Item by reference
The likelihood that you'll run into this is low, but I was working on some VB.NET code and got the ReadOnlyException.
I ran into this issue because the code was passing the DataRow Item to a Sub ByRef. Just the act of passing-byref triggers the exception.
Sub Main()
Dim dt As New DataTable()
dt.Columns.Add(New DataColumn With {
.ReadOnly = True,
.ColumnName = "Name",
.DataType = GetType(Integer)
})
dt.Rows.Add(4)
Try
DoNothing(dt.Rows(0).Item("Name"))
Console.WriteLine("All good")
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End Sub
Sub DoNothing(ByRef item As Object)
End Sub
Output
Column 'Name' is read only
C-sharp
You can't even write code like this in C# . DoNothing(ref dt.Rows[0].Item["Name"]) gives you a compile time error.
open the yourdataset.xsd file of your data set. click on the table or object and click on the specific column which readonly property need to be changed.
its simple solutions.