I am attempting to write to a Paradox database file using c# and oledb in Windows 8. I am able to drop the table, create the table and write the first line before I get the error "operation must use an updateable query".
I have gone through the following to try and work it out:
1) Running as administrator
2) Updating the permissions within the application to make sure I don't have readonly or archive properties on the file
3) Adding the running user permissions to read / write / modify directories where the db files sit
4) Changing the query multiple times in case I am doing something weird with the query
If I was unable to write or insert at all then the above steps would have made sense but the initial insert works and any additional inserts fail.
The code below shows the current way I am trying to perform the actions and I have left in the commented out sections so that you can see what else I have tried.
public void OverwriteData(string fileName, DataTable dataToWrite)
{
//split up the filename
string path = Path.GetDirectoryName(fileName);
string file = Path.GetFileName(fileName);
//create the string for creating the table
string strTempCreate = "";
//string strTempInsert = "";
foreach (DataColumn column in dataToWrite.Columns)
{
if (strTempCreate != "")
{
strTempCreate = strTempCreate + ", ";
}
strTempCreate = strTempCreate + "[" + column.ColumnName + "]" + " char(30)";
/*if (strTempInsert != "")
{
strTempInsert = strTempInsert + ", ";
}
strTempInsert = strTempInsert + column.ColumnName;*/
}
string createTableStr = "CREATE TABLE " + file + " (" + strTempCreate + ")";
string dropTableStr = "DROP TABLE " + file;
//build the sql insert command
//string insertSql = "insert into " + file + " values ";
/*foreach (DataRow row in dataToWrite.Rows)
{
insertSql = insertSql + row.Field<string>;
}*/
string connectionString = #"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + path + #";Extended Properties=Paradox 5.x;";
//DataTable results = new DataTable();
using (OleDbConnection conn = new OleDbConnection(connectionString))
{
conn.Open();
OleDbCommand dbCommand = new OleDbCommand();
dbCommand.Connection = conn;
dbCommand.CommandText = dropTableStr;
try
{
dbCommand.ExecuteNonQuery();
}
catch { }
dbCommand.CommandText = createTableStr;
dbCommand.ExecuteNonQuery();
//try to do the insert
StringBuilder sb = new StringBuilder();
//make sure that the database is not readonly
FileAttributes attributes = File.GetAttributes(fileName);
if ((attributes & FileAttributes.Archive) == FileAttributes.Archive)
{
attributes = attributes & ~FileAttributes.Archive;
File.SetAttributes(fileName, attributes);
}
//then we want to try and connect to this database to put data into it
string selectSQL = "Select * from " + file;
using (var adapter = new OleDbDataAdapter(selectSQL, conn))
{
using (var builder = new OleDbCommandBuilder(adapter))
{
var destinationTable = new DataTable();
adapter.Fill(destinationTable);
destinationTable.Merge(dataToWrite,true,MissingSchemaAction.Ignore);
destinationTable.AcceptChanges();
foreach (DataRow row in destinationTable.Rows)
{
row.SetAdded();
}
builder.QuotePrefix = "[";
builder.QuoteSuffix = "]";
builder.GetInsertCommand();
adapter.Update(destinationTable);
}
}
/*foreach (DataRow row in dataToWrite.Rows)
{
sb.Clear();
sb.Append("insert into " + file + " values ('");
IEnumerable<string> fields = row.ItemArray.Select(field => field.ToString());
sb.Append(string.Join("','", fields));
sb.Append("')");
dbCommand.CommandText = sb.ToString();
dbCommand.ExecuteNonQuery();
}*/
/*sb.Clear();
sb.Append("insert into " + file);
foreach (DataRow row in dataToWrite.Rows)
{
sb.Append(" values ('");
IEnumerable<string> fields = row.ItemArray.Select(field => field.ToString());
sb.Append(string.Join("','", fields));
sb.Append("'),");
}
sb.Remove(sb.Length - 1, 1);
sb.Append(";");
dbCommand.CommandText = sb.ToString();
dbCommand.ExecuteNonQuery();
*/
}
}
This article solved my problem: https://msdn.microsoft.com/en-us/library/ms715421(v=vs.85).aspx
According to Microsoft: A table is not updatable by the Paradox driver under two conditions:
When a unique index is not defined on the table. This is not true for
an empty table, which can be updated with a single row even if a
unique index is not defined on the table. If a single row is inserted
in an empty table that does not have a unique index, an application
cannot create a unique index or insert additional data after the
single row has been inserted.
If the Borland Database Engine is not implemented.
Related
I have an Excel spreadsheet that I receive that I need to import into a table in our database. I have previously asked about pulling a single cell of data from a spreadsheet (Read a single cell from Excel to a string using C# and ASP.NET) and I am attempting to build off of this in order to move an entire spreadsheet into the database.
The format of the information is Column 1 = Name, Column 2 = Wage, Column 3 = Department
The existing code is as follows:
#region Initialize Connection
var Class_Connection = new SQL_Connection();
var sql = new SQL_Statements();
Class_Connection.cnn.Close();
#endregion
string properties = String.Format(#"Provider = Microsoft.ACE.OLEDB.12.0; Data Source = \\Hera\Public\LumberPrices\lbr_ems.xls; Extended Properties = 'Excel 8.0; HDR = NO;'");
string Price = "";
using (OleDbConnection conn = new OleDbConnection(properties))
{
string sqlpull = "SELECT * FROM [" + worksheet + "$" + Cell1 + ":" + Cell2 + "]";
conn.Open();
DataSet ds = new DataSet();
//string columns = String.Join(",", columnNames.ToArray());
using (OleDbDataAdapter da = new OleDbDataAdapter(sqlpull, properties))
{
string temp2 = "";
var dt = new DataTable();
da.Fill(dt);
for (int i = 0; i < dt.Columns.Count; i++)
{
temp2 = dt.Columns[i].ColumnName.ToString();
}
foreach (DataRow dr in dt.Rows)
{
Price = dr[temp2].ToString();
}
}
}
int Freight = GetFreight(LumbDesc);
int price1 = Convert.ToInt32(Price);
int addon = Convert.ToInt32(AddTo);
int Total = price1 + addon + Freight;
//TODO: insert into TLumberprice
string sqlStatement = "insert table([LumberCode], [Price], [PriceDate], [UserName], [Factor], [Op])" +
"values ('" + LumbDesc + "', '" + Total + "', '" + DateTime.Now + "', '" + LoginSession.userName.Remove(LoginSession.userName.Length - 1) + "', '" + Factor + "', '" + OP + "')";
#region Insert Lumber Prices
Class_Connection.cnn.Open();
SqlCommand InsertLumberPrices = new SqlCommand(sqlStatement, Class_Connection.cnn);
InsertLumberPrices.ExecuteNonQuery();
Class_Connection.cnn.Close();
#endregion
This works as intended. but I have questions.
Can I read the DataTable(dt) straight into the database, or do I have to use something like the following?
for (int i = 0; i < dt.Columns.Count; i++)
{
SName = dt.Columns[0].ColumnName.ToString();
SWage = dt.Columns[1].ColumnName.ToString();
SDept = dt.Columns[2].ColumnName.ToString();
}
foreach (DataRow dr in dt.Rows)
{
Name = dr[SName].tostring();
Wage = dr[SWage].tostring();
Dept = dr[SDept].tostring();
}
There is a way of importing the data from a data table into the SQL Table.
You need to use SqlBulkCopy
var sqlBulkCopy = new SqlBulkCopy(conn);
You can even map which data table column goes to which SQL Table column as well.
Here is an example.
using(var sqlBulkCopy = new SqlBulkCopy(conn))
{
sqlBulkCopy.ColumnMappings.Add(new SqlBulkCopyColumnMapping("Column 1", "Name"));
sqlBulkCopy.ColumnMappings.Add(new SqlBulkCopyColumnMapping("Column 2", "Wage"));
sqlBulkCopy.ColumnMappings.Add(new SqlBulkCopyColumnMapping("Column 3", "Department"));
sqlBulkCopy.DestinationTableName = "table" // table name in SQL
conn.Open();
sqlBulkCopy.WriteToServer(dt);
conn.Close();
}
Where conn is your SqlConnection.
If you don't want to use the data table, you could go directly to the BulkImport.
Just change the OleDbDataAdapter to OleDbCommand and put the value in an IDataReader variable, which then you can use in the code above instead of dt.
With the last part, I only did it in the past between 2 SQL servers that did not have a link to each other. I'm not sure 100% sure if it will work with OleDb.
Having imported data from an excel spreadsheet into datagridview, I would like to create a table (determined by the user in Windows forms) and insert it with the column names into the new table from the datagridview.
The user can use the "program" to insert other excel sheets into other databases as well.
How do I add column names dynamically that is not predefined and is defined by how many columns is in the excel spreadsheet?
My goal here was to create an import application to import excel spreadsheets into a SQL server database. The problem that I faced is to get the number columns that I needed to insert into the database and to dynamically name them according to the excel spreadsheet.
The application will be used on various spreadsheets and the column names must not be hardcoded. This is the code I used that managed the last phase of the application where all the data from the spreadsheet is inserted into to the database in SQL.
//Import Button
private void button5_Click(object sender, EventArgs e)
{
string createColumns = "";
string columns = "";
string rows = "";
var grid = (DataTable)dataGridView3.DataSource;
for (int i = 0; i < grid.Columns.Count; i++)
{
if (i == grid.Columns.Count - 1)
{
createColumns += "[" + grid.Columns[i].ToString() + "] varchar(200) NULL";
columns += "[" + grid.Columns[i].ToString() + "]";
}
else
{
columns += "[" + grid.Columns[i].ToString() + "],";
createColumns += "[" + grid.Columns[i].ToString() + "] varchar(200) NULL,";
}
}
string createTable = string.Format("Create table [{0}] ({1})", textBox1.Text, createColumns);
rows = string.Format("Insert Into[{0}]({1})", textBox1.Text, columns);
for (int i = 0; i < grid.Rows.Count; i++)
{
string row = "";
for (int c = 0; c < grid.Columns.Count; c++)
{
if (c == grid.Columns.Count - 1)
row += "'" + grid.Rows[i][c].ToString() + "'";
else
row += "'" + grid.Rows[i][c].ToString() + "', ";
}
if (i == grid.Rows.Count - 1)
rows += string.Format(" ({0});", row);
else
{
if (i == 0)
{
rows += " Values";
}
rows += string.Format(" ({0}),", row);
}
}
string s = "Integrated Security = SSPI;Persist Security Info = False;Data Source = " +
ServerName.Text + "; Initial Catalog = " +
Databases.Text;
SqlConnection conn = new SqlConnection(s);
SqlCommand cmd = new SqlCommand();
cmd.CommandText = createTable;
cmd.Connection = conn;
conn.Open();
cmd.ExecuteNonQuery();
conn.Close();
SqlCommand cmd2 = new SqlCommand(rows, conn);
cmd2.CommandType = CommandType.Text;
conn.Open();
cmd2.ExecuteNonQuery();
conn.Close();
Application.Exit();
}
So basically I have a C# project, that iterates through every student (300 students) within a table called Student within a Microsoft Access 2016 database. In a single iteration for a single student by using other tables like Mathematics, Reading that have a 1-to-1 relationship with the Student table, to grab the data that belongs to that student.
try
{
OleDbCommand allStudents = new OleDbCommand("SELECT [NSN]"
+ " FROM [Student]; ");
allStudents.Connection = conn;
OleDbDataAdapter allData = new OleDbDataAdapter(allStudents);
DataTable allTable = new DataTable();
allData.Fill(allTable);
foreach (DataRow dr in allTable.Rows)
{
string NSN = dr["NSN"].ToString();
OleDbCommand cmd = new OleDbCommand("SELECT * "
+ "FROM (((([Student] s "
+ "INNER JOIN [Student Extra] se ON se.[NSN] = s.[NSN]) "
+ "INNER JOIN [Reading] r ON r.[NSN] = s.[NSN])"
+ "INNER JOIN [Writing] w ON w.[NSN] = s.[NSN])"
+ "INNER JOIN [Mathematics] m ON m.[NSN] = s.[NSN]) "
+ "WHERE s.[NSN] = '" + NSN + "'; ");
cmd.Connection = conn;
OleDbDataAdapter daa = new OleDbDataAdapter(cmd);
DataTable dtt = new DataTable();
daa.Fill(dtt);
foreach (DataRow drr in dtt.Rows)
{
firstName = drr["Preferred Name"].ToString();
gender = drr["Gender"].ToString();
room = drr["Room Number"].ToString();
NSAchieve = drr["National Standard Achieve"].ToString();
NSProgress = drr["National Standard Progress"].ToString();
The above code is only a snippet of the code I have, but this is basically where the function will start.
By using this data, I want to be able to go through several SELECT statements for other tables and compare them and produce a calculated value.
Dictionary<string, OleDbCommand> d = new Dictionary<string, OleDbCommand>();
cmd = new OleDbCommand("SELECT [Achievement Statement]"
+ " FROM [National Standard Codes]"
+ " WHERE [National Standard Code] = '" + readingNSAchievementCode + "'; ");
d["readingNSAchievementOTJ"] = cmd;
cmd = new OleDbCommand("SELECT [" + NSAchieve + "]"
+ " FROM [Reading National Standards]"
+ " WHERE [Assessment] = '" + readingFinalAssessment + "'; ");
d["readingNSAchievementComp"] = cmd;
cmd = new OleDbCommand("SELECT [Timeframe]"
+ " FROM [Reading Statements]"
+ " WHERE [Year Code] = '" + NSProgress + "'; ");
d["readingNSProgressTimeframe"] = cmd;
There are several more commands, (approx <150). I use a Dictionary to store my Commands, and then execute the commands in a FOREACH loop.
foreach(KeyValuePair<string, OleDbCommand> pair in d)
{
try
{
string v = pair.Key;
OleDbCommand dbCmd = pair.Value;
dbCmd.Connection = conn;
OleDbDataReader reader = dbCmd.ExecuteReader();
reader.Read();
readingDict[v] = reader.GetString(0);
}
catch (Exception e)
{
MessageBox.Show("Error at " + pair.Key + "\n\n Here is message " + e);
}
}
After executing and getting my value, I want to store my data into another table called Calculated.
string insert1 = "INSERT INTO [Calculated] (";
int i = 0;
Dictionary<string, string> dict = createDictionary(NSN);
int len = dict.Count / 2;
foreach (KeyValuePair<string, string> pair in dict)
{
string field = pair.Key;
string value = pair.Value;
if (i == (len - 1))
{
insert1 += "[" + field + "])";
break;
}
else
{
insert1 += "[" + field + "], ";
}
i++;
}
insert1 += " VALUES (";
i = 0;
foreach (KeyValuePair<string, string> pair in dict)
{
string field = pair.Key;
string value = pair.Value;
if (i == len - 1)
{
insert1 += "'" + value + "')";
break;
}
else
{
insert1 += "'" + value + "', ";
}
i++;
}
I build my INSERT INTO query, and then I execute using an OleDbCommand. This needs to repeat 300 times, but for development purposes currently I only have 5 students in my Student table. However when executing after the 4th student it will always consistently give me an error System Resources Exceeded always at a specific OleDbCommand. I have tested each command separately, so there is no issue with the way the OleDbCommands are written.
I have tried searching on here, and tried to encase the first code snippet in a using statement, using using (OleDbConnection conn = new OleDbConnection(connectionStr)) but as I am still a novice at C#, I am unable to produce a solution.
How do I insert a DataTable contents into Oracle8i Database Table?
1) This is how I did it. I want to know if this is would work:
I made this foreach loop to update the Oracle 8i Database table TABLE using the data from DataTable dt. TABLE and dt has the exact same columns:
Basically, what I did is to fetch the column names of the data table and use that in the insert query and doing the insert row by row:
below is the code:
DataTable dt;
int intColCtr1 = 1;
int intColCtr2 = 1;
strBTConn = strProvider + strUID + strPWD + strDataSource + strPersistSecurityInfo;
OLEDBconn.Open();
foreach (DataRow row in dt.Rows)
{
strSQL = "insert into TABLE ( ";
foreach (DataColumn column in dt.Columns)
{
if (intColCtr1<=dt.Columns.Count)
{
strSQL = " " + column.ColumnName.ToString() + ", ";
}
else
{
strSQL = " " + column.ColumnName.ToString() + " ";
intColCtr1++;
}
}
strSQL = " ) values ";
foreach (DataColumn column in dtUpdatedLACTM00.Columns)
{
if (intColCtr2<=dt.Columns.Count)
{
strSQL = " " + column.ToString() + ", ";
}
else
{
strSQL = " " + column.ToString() + " ";
intColCtr2++;
}
}
using (OleDbCommand OLEDBcmd = new OleDbCommand(strSQLCmd, OLEDBconn))
{
OLEDBcmd.ExecuteNonQuery();
}
}
[edit]: The above code failed because the data in the data table contained oracle and c# escape characters (single quotes and backslashes)
2) Also, if you have better ideas to update the database TABLE using data from DataTable dt, please tell me.
I m using the following function to create an access table:
public static void createtable(string path, string tablename, string[] columnnames)
{
try
{
string connectionstring = creadteconnectionstring(path);
OleDbConnection myConnection = new OleDbConnection(connectionstring);
myConnection.Open();
OleDbCommand myCommand = new OleDbCommand();
myCommand.Connection = myConnection;
string columnam = "[" + columnnames[0] + "] Text";
for (int i = 1; i < columnnames.Length; i++)
{
columnam = columnam + ", [" + columnnames[i] + "] Text";
}
myCommand.CommandText = "CREATE TABLE [" + tablename + "](" + columnam + ")";
myCommand.ExecuteNonQuery();
myCommand.Connection.Close();
Console.WriteLine("Access table " + tablename + " created.");
}
catch
{
Console.WriteLine("Access table " + tablename + " already exists.");
return;
}
}
However if I open the access table in MS access the data type is Memo, not Text. Other data types link specified by MS dont seem to work at all.
Any ideas are welcome? Thanks!
The Access DDL TEXT data type behaves differently depending on the context where you execute your statement.
From DAO in Access, this creates bar as a text field. But from ADO in Access, bar will be memo type.
CREATE TABLE tblFoo (bar TEXT)
Executing that statement from an OleDb connection produces the same result as with ADO in Access.
If you want bar to be an actual text field, include a field length <= 255.
CREATE TABLE tblFoo (bar TEXT(255))