Insert new DataRow with autoincrement Primary Key with C# (OleDB) - c#

I' m trying to insert a new DataRow (locally stored in a DataSet) to a Access table with C#. The table is also created by this app and has 3 columns:
ID (integer, primary key, Required is set to No though I've set it as primary key)
Name (string)
Money (int)
My current code looks like that:
OleDbConnection con = new OleDbConnection();
OleDbCommand cmd = new OleDbCommand();
OleDbDataAdapter da;
DataSet ds = new DataSet();
DataView dv = new DataView();
DataRow row;
string Con = #"Provider=Microsoft.Jet.OLEDB.4.0;" + "Data Source=";
string path = "V:\\ProjectProgress\\Test.mdb";
con.ConnectionString = Con + path;
if (con.State == ConnectionState.Closed)
{
con.Open();
}
cmd.Connection = con;
cmd.CommandText = "SELECT ID, Name, Money FROM Test";
da = new OleDbDataAdapter(cmd);
da.TableMappings.Add("Table", "Test");
da.Fill(ds, "Test");
ds.Tables["Test"].Columns[0].AutoIncrement = true;
ds.Tables["Test"].Columns[0].AutoIncrementSeed = -1;
ds.Tables["Test"].Columns[0].AutoIncrementStep = -1;
dv.Table = ds.Tables["Test"];
row = ds.Tables["Test"].NewRow();
row["Name"] = "Huber";
row["Money"] = 100;
ds.Tables["Test"].Rows.Add(row);
string strOLE = "INSERT INTO Test ([Name], [Money]) Values(#Name, #Money)";
OleDbCommand cmdi = new OleDbCommand(strOLE, con);
cmdi.Parameters.Add("#Name", OleDbType.VarChar, 25, "Name");
cmdi.Parameters.Add("#Money", OleDbType.Integer, 4, "Money");
da.InsertCommand = cmdi;
da.Update(ds.Tables["Test"]);
con.Close();
When updating I'm always getting a
Index or primary key cannot contain a Null value
error.
Setting the Required value of the ID column to Yes, will throw a
Index or Primary Key Cannot Contain a Null Value
error.
How can I let Access assign the right primary key and how do I get the new value back into my dataset?
Using SCOPE_IDENTITY() is not possible in Access as far as I know and found in some forums.
(Working with Visual C# Express 2010, Access 2003)

The following is complete working test code to illustrate the procedure. All we need to do is provide an OleDbDataAdapter.SelectCommand that includes the primary key and the columns we want to update, and the OleDbCommandBuilder object will create the INSERT statement for us:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.OleDb;
namespace oleDbTest
{
class Program
{
static void Main(string[] args)
{
string myConnectionString;
myConnectionString =
#"Provider=Microsoft.ACE.OLEDB.12.0;" +
#"Data Source=C:\Users\Public\Database1.accdb;";
using (OleDbConnection con = new OleDbConnection())
{
con.ConnectionString = myConnectionString;
con.Open();
using (OleDbDataAdapter da = new OleDbDataAdapter())
using (OleDbCommandBuilder bld = new OleDbCommandBuilder(da))
{
bld.QuotePrefix = "["; // these are
bld.QuoteSuffix = "]"; // important!
da.SelectCommand = new OleDbCommand(
"SELECT [ID], [Name], [Money] " +
"FROM [Test] " +
"WHERE False",
con);
using (System.Data.DataTable dt = new System.Data.DataTable("Test"))
{
// create an empty DataTable with the correct structure
da.Fill(dt);
System.Data.DataRow dr = dt.NewRow();
dr["Name"] = "Huber";
dr["Money"] = 100;
dt.Rows.Add(dr);
da.Update(dt); // write new row back to database
}
}
con.Close();
}
Console.WriteLine();
Console.WriteLine("Done.");
}
}
}

My first thought is that your problem might be that both AutoIncrementSeed and AutoIncrementStep are set to a negative value of -1. Try setting both to a positive value.
[EDIT]
Second thought, you might want to try the OleDbCommandBuilder (MSDN documentation here). It creates your INSERT, UPDATE, and DELETE statements automatically using the column information of your DataSet.
At my former employer we used OleDbCommandBuilder all the time when working with Access and it worked like a charm, even with auto increment fields.

Related

C# WinForms: create automatically if statement connected to access database

I am new to C# and need some guidance!
short summary:
I have an application with few forms that is connected to the Access database. Everything is working fine! The user can select the needed item from the combo box that is shown in the label on the next form. Additionally the elements of the table are shown in the datagridview2 on the same form. Every item from the combo box is connected to a different table in the Access database:
private void frmData_Load(object sender, EventArgs e)
{
lblItem.Text = Item;
string connectionString = null;
OleDbConnection con;
connectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=\".\\Database_Example.accdb\"";
con = new OleDbConnection(connectionString);
if (lblItem.Text == "X")
{
OleDbCommand cmd2 = new OleDbCommand("SELECT ID, Column1, Column2 FROM X", con);
OleDbDataAdapter sda = new OleDbDataAdapter(cmd2);
DataTable td = new DataTable();
sda.Fill(td);
dataGridView2.DataSource = td;
}
if (lblItem.Text == "Y")
{
OleDbCommand cmd = new OleDbCommand("SELECT ID, Column1, Column2 FROM Y", con);
OleDbDataAdapter sda = new OleDbDataAdapter(cmd);
DataTable td = new DataTable();
sda.Fill(td);
dataGridView2.DataSource = td;
}
}
Now I want to use my application to add new tables to the database. Therefore there are bunch of different approaches on the web.
Problem: I´m looking for an option to automatically create new if-statements once I´ve added new tables in the database. For example for items "Z"; "A"; "B"; ...
Is there a way to do it? Or do I need a different approach?
Greetings :)
We get this question a few times a month and the answer is always the same:
You really need ONE table, with an additional column for your Z, A, B, etc. Make this the first field of the primary key.
Now this:
if (lblItem.Text == "X")
{
OleDbCommand cmd2 = new OleDbCommand("SELECT ID, Column1, Column2 FROM X", con);
OleDbDataAdapter sda = new OleDbDataAdapter(cmd2);
DataTable td = new DataTable();
sda.Fill(td);
dataGridView2.DataSource = td;
}
// etc
Becomes this:
var td = new DataTable();
using (var con = new OleDbConnection("connection string here"))
using (var cmd = new OleDbCommand("SELECT ID, Column1, Column2 FROM MyTable WHERE Key = ? ", con))
using (var sda = new OleDbDataAdapter(cmd2))
{
cmd.Parameters.Add("?", OleDbType.NChar, 3).Value = lblItem.Text;
sda.Fill(td);
}
No if statement required.
Additionally, do NOT try to re-use your connection object throughout a form instance like that. It really is more efficient to create a new connection for each call to the database.

ms access database connection in a datagridview

What is the error of this code
connect = new OleDbConnection(coo);
connect.Open();
command.Connection = connect;
DataTable dt = new DataTable();
OleDbDataAdapter ODA = new OleDbDataAdapter("SELECT * FROM Items where itemno = '" + textBox1.Text + "'", connect);
ODA.Fill(dt);
dataGridView1.DataSource = dt;
after I run it, this is what happened
"Data type mismatch in criteria expression"
What should I do?
itemno is integer, that is why you are getting the error, remove the single quotes around the value. But, More importantly, Use Parameters with your query. You are prone to SQL Injection
using (var connect = new OleDbConnection(coo))
{
using (OleDbCommand command = new OleDbCommand("SELECT * FROM Items where itemno = ?", connect))
{
command.Parameters.Add(new OleDbParameter("#p1", OleDbType.Integer)
{
Value = textBox1.Text
});
DataTable dt = new DataTable();
OleDbDataAdapter ODA = new OleDbDataAdapter(command);
ODA.Fill(dt);
dataGridView1.DataSource = dt;
}
}
Couple of things to add:
Enclose your Command and Connection object in using statement.
You don't have to explicitly open a connection with DataAdapter. It will open the connection to the database.
OleDB uses positional parameter instead of named parameter

How to Insert Rows to Table Object Inside an Excel Sheet?

I have difficulties trying to insert rows into an existing table object. Here is my code snippet:
string connectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + #"C:\myExcelFile.xlsx" + ";Extended Properties=\"Excel 12.0;ReadOnly=False;HDR=Yes;\"";
using (OleDbConnection conn = new OleDbConnection(connectionString))
{
conn.Open();
OleDbCommand cmd = new OleDbCommand();
cmd.Connection = conn;
string insertQuery = String.Format("Insert into [{0}$] (ID, Title,NTV_DB, Type ) values(7959, 8,'e','Type1')", TabDisplayName);
cmd.CommandText = insertQuery;
cmd.ExecuteNonQuery();
cmd = null;
conn.Close();
}
As a result I get my rows inserted below a ready-made table object:
I've also tried inserting data inside a table object like so:
string insertQuery = String.Format("Insert into [{0}$].[MyTable] (ID, Title,NTV_DB, Type ) values(7959, 8,'e','Type1')", TabDisplayName);
But I get an error:
The Microsoft Access database engine could not find the object 'MyTable'. Make sure the object exists and that you spell its name and the path name correctly. If 'MyTable' is not a local object, check your network connection or contact the server administrator.
As you can see, table with a name MyTable does exist. I would be very grateful if someone can shed some light on this mystery.
If you are using the Microsoft.ACE.OLEDB provider, then be aware that it doesn't support a named range. You need to provide the name of the sheet [Sheet1$] or the name of the sheet followed by the range [Sheet1$A1:P7928].
If the range is not provided, it will then define the table as the used range, which may contains empty rows.
One way to deal with empty rows would be to delete them, but the driver doesn't support the DELETE operation.
Another way is to first count the number of rows with a non empty Id and then use the result to define the range of the table for the INSERT statement:
using (OleDbConnection conn = new OleDbConnection(connectionString)) {
conn.Open();
string SheetName = "Sheet1";
string TableRange = "A1:P{0}";
// count the number of non empty rows
using (var cmd1 = new OleDbCommand(null, conn)) {
cmd1.CommandText = String.Format(
"SELECT COUNT(*) FROM [{0}$] WHERE ID IS NOT NULL;"
, SheetName);
TableRange = string.Format(TableRange, (int)cmd1.ExecuteScalar() + 1);
}
// insert a new record
using (var cmd2 = new OleDbCommand(null, conn)) {
cmd2.CommandText = String.Format(
"INSERT INTO [{0}${1}] (ID, Title, NTV_DB, Type) VALUES(7959, 8,'e','Type1');"
, SheetName, TableRange);
cmd2.ExecuteNonQuery();
}
}
If you execute this code:
var contents = new DataTable();
using (OleDbDataAdapter adapter = new OleDbDataAdapter(string.Format("Select * From [{0}$]", TabDisplayName), conn))
{
adapter.Fill(contents);
}
Console.WriteLine(contents.Rows.Count);//7938
you will see 7938 (last row number on your screenshot). And when you insert new row, it inserted at 7939 position. Empty content in (7929, 7930, ...) rows are ignored, because excel knows that last number is 7938.
Solutions:
You must delete all rows after 7928 in excel file.
You must insert on specific position.
I'm not sure Access C# works the same as Excel, but this worked on a spreadsheet for me. Maybe it could help you?
Table3.ListRows[1].Range.Insert(Excel.XlInsertShiftDirection.xlShiftDown);
Try this
private void GetExcelSheets(string FilePath, string Extension, string isHDR)
{
string conStr="";
switch (Extension)
{
case ".xls": //Excel 97-03
conStr = ConfigurationManager.ConnectionStrings["Excel03ConString"]
.ConnectionString;
break;
case ".xlsx": //Excel 07
conStr = ConfigurationManager.ConnectionStrings["Excel07ConString"]
.ConnectionString;
break;
}
//Get the Sheets in Excel WorkBoo
conStr = String.Format(conStr, FilePath, isHDR);
OleDbConnection connExcel = new OleDbConnection(conStr);
OleDbCommand cmdExcel = new OleDbCommand();
OleDbDataAdapter oda = new OleDbDataAdapter();
cmdExcel.Connection = connExcel;
connExcel.Open();
//Bind the Sheets to DropDownList
ddlSheets.Items.Clear();
ddlSheets.Items.Add(new ListItem("--Select Sheet--", ""));
ddlSheets.DataSource=connExcel
.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
ddlSheets.DataTextField = "TABLE_NAME";
ddlSheets.DataValueField = "TABLE_NAME";
ddlSheets.DataBind();
connExcel.Close();
txtTable.Text = "";
lblFileName.Text = Path.GetFileName(FilePath);
Panel2.Visible = true;
Panel1.Visible = false;
}

C# DataTable update Access Database

How can I save a DataTable to a file. accdb (Access) existing one? I've used the following code and it does not work:
using (OleDbConnection oledbConnection = new OleDbConnection(connection))
{
oledbConnection.Open();
string query = "SELECT * FROM Student";
using (OleDbCommand oledbCommand = new OleDbCommand(query, oledbConnection))
{
using (OleDbDataAdapter oledbDataAdapter = new OleDbDataAdapter(oledbCommand))
{
using (OleDbCommandBuilder oledbCommandBuilder = new OleDbCommandBuilder(oledbDataAdapter))
{
oledbDataAdapter.DeleteCommand = oledbCommandBuilder.GetDeleteCommand(true);
oledbDataAdapter.InsertCommand = oledbCommandBuilder.GetInsertCommand(true);
oledbDataAdapter.UpdateCommand = oledbCommandBuilder.GetUpdateCommand(true);
oledbDataAdapter.Update(dataTable);
}
}
}
oledbConnection.Close();
}
The variable dataTable is initialized with the original contents of the file, then it was modified by adding a row and now I have to update the table in the database.
I tried using the following code, but that does not work :(
OleDbDataAdapter da = new OleDbDataAdapter("SELECT * FROM Student", connection);
OleDbCommandBuilder cmdBuilder = new OleDbCommandBuilder(da);
da.InsertCommand = cmdBuilder.GetInsertCommand(true);
// create and insert row in the DataTable
da.Update(dataTable);
Assuming that you have made some changes in the datatable, then you could pass the generated update/insert/delete command to the adapter in this way
oledbDataAdapter.DeleteCommand = oledbCommandBuilder.GetDeleteCommand();
oledbDataAdapter.InsertCommand = oledbCommandBuilder.GetInsertCommand();
oledbDataAdapter.UpdateCommand = oledbCommandBuilder.GetUpdateCommand();
oledbDataAdapter.Update(datatable);
Now the adapter knows how to update your table

Retrieving value from sql ExecuteScalar()

I have the following:
String sql = "SELECT * FROM Temp WHERE Temp.collection = '" + Program.collection + "'";
SqlConnection conn = new SqlConnection(connString);
SqlCommand cmd = new SqlCommand(sql, conn);
Program.defaultCollection = (String)cmd.ExecuteScalar();
And I want to get the second column after executing the statement. I know it will return only one row with two columns
I have read online that I will have to read each row of the result, is there any other way?
ExecuteScalar gets the first column from the first row of the result set. If you need access to more than that you'll need to take a different approach. Like this:
DataTable dt = new DataTable();
SqlDataAdapater sda = new SqlDataAdapter(sql, conn);
sda.Fill(dt);
Program.defaultCollection = dt.Rows[0]["defaultCollection"];
Now, I realize that the field name may not be defaultCollection, but you can fill that in.
From the MSDN documentation for ExecuteScalar:
Executes the query, and returns the first column of the first row in the result set returned by the query. Additional columns or rows are ignored.
Now, as a final bit of advice, please wrap all ADO.NET objects in a using statement. Like this:
using (SqlConnection conn = new SqlConnection(connString))
using (SqlDataAdapter sda = new SqlDataAdapter(sql, conn))
{
DataTable dt = new DataTable();
sda.Fill(dt);
// do something with `dt`
}
this will ensure they are properly disposed.
And I want to get the second column after executing the statement
It is not possible with execute scalar.
is there any other way
You have 2 options here either to use SqlDataAdapter or SqlDataReader.
For you using DataReader is a recommended approach as you don't need offline data or do other worh
by using SqlDataAdapter
using (SqlConnection c = new SqlConnection(
youconnectionstring))
{
c.Open();
/
using (SqlDataAdapter a = new SqlDataAdapter(sql, c))
{
DataTable t = new DataTable();
a.Fill(t);
if(t.Rows.Count > 0)
{
string text = t.Rows[0]["yourColumn"].ToString();
}
}
}
by using DataREader
using (SqlConnection connection =
new SqlConnection(connectionString))
{
SqlCommand command =
new SqlCommand(sql, connection);
connection.Open();
SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
//read data here
string text = reader.GetString(1)
}
reader.Close();
}
SqlCommand.ExecuteScalar() can be used only when the result have just one row and one column.
If you need more than one column to be returned, you should use something like this:
String sql = "SELECT * FROM Temp WHERE Temp.collection = '" + Program.collection + "'";
SqlConnection conn = new SqlConnection(connString);
using(SqlCommand cmd = new SqlCommand(sql, conn))
{
using(SqlDataReader rdr = cmd.ExecuteReader())
{
if(rdr.Read())
{
Program.defaultCollection = (String)rdr["Column1"];
Program.someOtherVar = (String)rdr["Column2"];
}
}
rdr.Close();
}
That will be the fastest way.
You can use a DataReader and read only the first column like:
IDataReader cReader = cmd.ExecuteReader();
if(cReader.Read())
{
string cText = cReader.GetString(1); // Second Column
}
ExecuteScalar only returns one value. You have to make sure your query only returns that value.
String sql = "SELECT temp.defaultCollection FROM Temp WHERE Temp.collection = '" + Program.collection + "'";
On a side note, read on SqlParameter. You don't want to concatenate values like that, you'll have a problem when the collection property contains a quote.

Categories