This application I'm developing will read in a bunch of records, validate / modify data, then send that data back to the DB.
I need to do this with an "All or None" approach; either update all of the rows, or don't modify any of the data in the DB.
I found the SqlBulkCopy class which has a method for writing all of the rows in a DataTable to a database but this is unacceptable for my purposes. I just wrote a quick app that'd write a few rows to the DB, populate a DataTable from that database, modify the data, then call WriteToServer(DataTable dt) again. Even though the DataTable has all of the Primary Key information, it simply added new rows with the data and new primary keys rather than updating the data for the rows that have matching primary keys.
So, how can I do bulk Update statements to an MSSQL database from a C# Winforms application?
I don't think this is really relevant, but I'll include my code anyways. I'm not great when it comes to interacting with databases, so it's very possible I'm just doing something dumb and wrong.
static void Main(string[] args)
{
using (SqlConnection conn = new SqlConnection("myConnectionInfo"))
{
//DataTable dt = MakeTable();
DataTable dt = FillDataTable(conn);
using (SqlBulkCopy sbc = new SqlBulkCopy(conn))
{
sbc.DestinationTableName = "TestTbl";
try
{
for (int i = 0; i < dt.Rows.Count; i++)
{
dt.Rows[i][1] = i;
dt.Rows[i][2] = i+1;
dt.Rows[i][3] = i+2;
}
sbc.WriteToServer(dt);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
Console.WriteLine("Press Any Key To Continue");
Console.ReadKey();
}
}
}
}
private static DataTable MakeTable()
{
DataTable newProducts = new DataTable("TestTbl");
DataColumn TestPk = new DataColumn();
TestPk.DataType = System.Type.GetType("System.Int32");
TestPk.ColumnName = "TestPk";
TestPk.AllowDBNull = false;
TestPk.AutoIncrement = true;
TestPk.Unique = true;
newProducts.Columns.Add(TestPk);
DataColumn Col1 = new DataColumn();
Col1.DataType = System.Type.GetType("System.String");
Col1.ColumnName = "Col1";
Col1.AllowDBNull = false;
newProducts.Columns.Add(Col1);
// Add 2 more columns like the above block
DataRow row = newProducts.NewRow();
row["Col1"] = "CC-101-WH";
row["Col2"] = "Cyclocomputer - White";
row["Col3"] = DBNull.Value;
newProducts.Rows.Add(row);
// Add 2 more rows like the above block
newProducts.AcceptChanges();
return newProducts;
}
private static DataTable FillDataTable(SqlConnection conn)
{
string query = "SELECT * FROM NYMS.dbo.TestTbl";
using (SqlCommand cmd = new SqlCommand(query, conn))
{
conn.Open();
DataTable dt = new DataTable();
dt.Load(cmd.ExecuteReader());
return dt;
}
}
The call to MakeTable() is commented out because I used it when I first ran the application to insert some data, then while trying to update that test data I use FillDataTable simply to populate it
I'm working on ASP.NET web aplication. I'm making it to be MVC and i have a problem with controler class. When i want to populate a drop down list with elements from my SQL database, class is working but only one instance of class with just one drop down list. When i use one class to populate 2 or more drop down lists it doesn't work and VS is not raising any errors. The controler class works, and it can populate drop down list, but just only one drop down list when page is loaded. So to work i have to make an instance of controler class for every drop down list. Please, can someone explain to me why won't work...
this is my DbBroker:
public DataTable VratiKategorije()
{
DataTable kategorije = new DataTable();
using (cn)
{
try
{
SqlDataAdapter adapter = new SqlDataAdapter("SELECT * FROM KategorijaLeka", cn);
adapter.Fill(kategorije);
}
catch (Exception err)
{
//
}
}
return kategorije;
}
And this is my Controler class which is using DbBroker:
public void VratiKategorije(DropDownList ddlKategorije){
try
{
ddlKategorije.DataSource = dbB.VratiKategorije();
ddlKategorije.DataTextField = "Naziv";
ddlKategorije.DataValueField = "ID_Kategorije";
ddlKategorije.DataBind();
}
catch (Exception err)
{
//Handle the err
}
ddlKategorije.Items.Insert(0, new ListItem("", ""));
}
And this is on Load_Page():
protected void Page_Load(object sender, EventArgs e)
{
KontrolerLeka kl = new KontrolerLeka();
kl.VratiKategorije(kategorijaLeka);
}
I found what was the problem. Ok, so when you use using() function with adapter there is no closing connection to database so when same function is called it can't populate another drop down list because the connection from last drop down list function is not closed. Insted of that code in DbBroker i used this and it worked! "pokreniDBtransakciju()" use cn (sql connection string) and after taking Record Set from database the connection is closed using "cn.Close()" which solved all problems.
DbBroker:
public DataTable Uzmi()
{
DataTable dt;
dt = new DataTable();
SqlCommand sc = new SqlCommand();
try
{
sc.CommandText = "SELECT * FROM KategorijaLeka";
sc.Connection = pokreniDBTransakciju();
SqlDataReader reader;
reader = sc.ExecuteReader();
dt.Load(reader);
cn.Close();
}
catch (SqlException e)
{
Console.WriteLine("GRESKA!!!" + e);
return null;
}
return dt;
}
Now I use method in C# to read table from SQLite database into DataTable,
but I want to send all table into other object.
So I think I have to use DataSet to combine all DataTable(s)
and send it to object as parameter.
Is there method that easy read all tables from SQLite database to DataSet?
Or I have to read all tables from SQLite database to DataTable each table
and combine to DataSet by hand?
The sql for listing all the tables is:
SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY 1
you could then get all the tables as databases seperately and then add them into a dataset - an example here: http://www.dotnetperls.com/dataset
so i guess the code would be something like:
Dataset d = new Dataset()
foreach (tableName in GetTables()){
d.Tables.Add(GetDataTable("select * from "+tableName);
}
code for GetTables and GetDataTable (i'll leave the piecing it together to you):
public ArrayList GetTables()
{
ArrayList list = new ArrayList();
// executes query that select names of all tables in master table of the database
String query = "SELECT name FROM sqlite_master " +
"WHERE type = 'table'" +
"ORDER BY 1";
try
{
DataTable table = GetDataTable(query);
// Return all table names in the ArrayList
foreach (DataRow row in table.Rows)
{
list.Add(row.ItemArray[0].ToString());
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
return list;
}
public DataTable GetDataTable(string sql)
{
try
{
DataTable dt = new DataTable();
using (var c = new SQLiteConnection(dbConnection))
{
c.Open();
using (SQLiteCommand cmd = new SQLiteCommand(sql, c))
{
using (SQLiteDataReader rdr = cmd.ExecuteReader())
{
dt.Load(rdr);
return dt;
}
}
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
return null;
}
}
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
I just want to to know if some data exists in the database.
Normaly I use SqlDataReader, take a loop SqlDataReader put variable in array or list,
and in Business layer again loop over the array or List and compare with the X data to see if it is in the list or array.
SqlDataReader readerOne = comm_SelectOne.ExecuteReader();
while (readerOne.Read())
{
...
}
I think this is not efficient, there are two loops (in Data Access layer to Collect and in Business layer to compare)
Is there another way to do this with a DataSet?
No there is'nt 'In' or 'Contains' function in DataSet because DataSet itself is a container of DataTable and data is saved in DataRow associated with any particular DataTable.
The simplest method to check if data exists in database on not, is to write an SQL Count statement e.g. SELECT COUNT(columnName) FROM tableName WHERE columnName = 'some value' . If 'sum value' doesn't exist in database it will return 0, return the count otherwise.
Basically DataSet is only a container of DataTable(s). If you want to find out about a particular data in DataTable instance inside DataSet instance, you can get DataTable instance from DataSet and there is an instance method called "Select" method (call it with parameter) to query specific data from DataTable instance.
i found on internet reference to:
Stack and
Find Data
My Bussines Layer:
public List<string> CompareInsee(string TheZone, List<object> InseList)
{
try
{
List<string> TempDict = new List<string>();
RempliClientInseExtracted(TheZone, ref NomTable);
DataTable TempDS = oClInse.Get_All_Inse(NomTable);
DataRow drFound;
DataColumn[] dcPk = new DataColumn[1];
// Set Primary Key
dcPk[0] = TempDS.Columns["NO_INSEE"];
TempDS.PrimaryKey = dcPk;
// Find the Row specified in txtFindArg
foreach (var oItem in InseList)
{
drFound = TempDS.Rows.Find(oItem);
if (drFound != null) TempDict.Add( oItem.ToString());
}
return TempDict;
}
catch (Exception excThrown)
{
if (!excThrown.Message.StartsWith("Err_")) { throw new Exception("Err_BL_ReadAllClientInsee", excThrown); }
else { throw new Exception(excThrown.Message, excThrown); }
}
}
Data Acces layer:
public DataTable Get_All_Inse(string NomTable)
{
try
{
using (var connectionWrapper = new Connexion())
{
var connectedConnection = connectionWrapper.GetConnected();
string sql_SelectAll = "SELECT * FROM " + NomTable;
SqlCommand comm_SelectAll = new SqlCommand(sql_SelectAll, connectionWrapper.conn);
SqlDataAdapter adapt_SelectAll = new SqlDataAdapter();
adapt_SelectAll.SelectCommand = comm_SelectAll;
DataTable dSet_SelectAll = new DataTable();
adapt_SelectAll.Fill(dSet_SelectAll);
dSet_SelectAll.Dispose();
adapt_SelectAll.Dispose();
return dSet_SelectAll;
}
}
catch (Exception excThrown)
{
if (!excThrown.Message.StartsWith("Err_")) { throw new Exception("Err_GetAllUsrClient", excThrown); }
else { throw new Exception(excThrown.Message, excThrown); }
}
}
So now i only have 1 loop --> just in my Bussines layer, NOT in DAL.
thanks you all