My Global Class :
class Global
{
public static OleDbDataAdapter adapter;
public static DataTable dt;
}
The procedure I'm using to fill my DataGridView :
OleDbConnection connection;
OleDbCommandBuilder builder;
void gridfill()
{
connection = new OleDbConnection("Provider=MSDAORA;Data Source=XXX;"
+ "user id=XXX;password=XXX;"
+ "persist security info=false;");
Global.adapter = new OleDbDataAdapter("select \"Id\", \"UserComputer\", \"GuralID\", \"Type\", \"CreatedOn\", \"State\" from COMPUTERS", connection);
builder = new OleDbCommandBuilder(Global.adapter);
Global.dt = new DataTable();
Global.adapter.Fill(Global.dt);
dataGridView1.DataSource = Global.dt;
dataGridView1.ReadOnly = true;
}
The procedure I'm using to update a field in a row in my Oracle DB :
private void button1_Click(object sender, EventArgs e)
{
try
{
if (comboBox1.Text == "New")
{
Global.dt.Rows[rowId]["State"] = 0;
}
else if (comboBox1.Text == "Old")
{
Global.dt.Rows[rowId]["State"] = 1;
}
else if (comboBox1.Text == "Junk")
{
Global.dt.Rows[rowId]["State"] = 2;
}
Global.adapter.Update(Global.dt);
this.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
When I run, I get my DataGridView filled. That part is okay. And then I double click on a row and another form shows up. That form gets values of the selected row. There's an update button on it. I use it to change the value of a field of the selected row. 3rd code I shared with you is the one to do that. But I get ORA-00904: "STATE" invalid identifier.
I debugged it. The error comes in this line :
Global.adapter.Update(Global.dt);
TIPS:
Tables are actually created by ORM classes.
As I know this is something about double quotes.
Ex : 'Select State from COMPUTERS' does not work but 'Select "State" from COMPUTERS' does.
I used '\' prefix in my SQL query after having the same issue when filling DataGridView. The problem solved.
But I cannot use it when trying to assign a new value to the field. And I need a way to do that.
I guess the problem is here :
Global.dt.Rows[rowId]["State"] = 0;
What can I do? Thanks.
Try setting the QuotePrefix and QuoteSuffix on your OleDbCommandBuilder object to ".
By default, the OleDbCommandBuilder doesn't know what quotation system the database system it's talking to uses.
Related
I'm currently working on a project where I've been asked to work on a WPF with the following requirements:
Allow the user to be able to add more textboxes in a stack panel
Save the user input (as string/nvarchar) in each textbox as a new record in
the database
The idea behind this is that if we receive multiple packages, each shipment is typically split up into multiple locations and we'd need to located where each component is.
I've included a sample of my code below, its barebones and will probably have more than just the one 'Records' field things will be inserted into. I've set the RecordID field to being an identity field so I'm not worried about declaring the ID field. It's not included in the sample but each record is linked to a ShipmentID.
I'd appreciate any help as I've been having trouble implementing it (and have hit a wall with my research) as my code runs into a:
'System.InvalidOperationException' occurred in System.Data.dll' with the CommandText Property not initialized error, when I try I try to save the record. I've unfortunately found only vague tidbits how to solve my issue (or maybe I'm just terrible at research).
private void StackAddTB(object sender, RoutedEventArgs e)
{
TextBox NewBox = new TextBox() { Margin = new Thickness(0, 10, 0, 0), Width = 100, Height = 20 };
StackBoxes.Children.Add(NewBox);
}
private void SaveMulti(object sender, RoutedEventArgs e)
{
string CStr = Manifesting.Properties.Settings.Default.PSIOpsSurfaceCS;
SqlConnection Connection = new SqlConnection(CStr);
string Query = "INSERT INTO TestLoop (Record), Values (Record)";
SqlCommand Command = new SqlCommand(Query, Connection)
foreach (TextBox TestTB in StackBoxes.Children.OfType<TextBox>())
{
try
{
Connection.Open();
Command = Connection.CreateCommand();
Command.Parameters.AddWithValue("#Record", TestTB.Text );
Command.ExecuteNonQuery();
}
catch (SqlException ex)
{
MessageBox.Show(ex.ToString());
}
finally
{
Connection.Close();
}
}
Edit: I've checked through the Looping Through Textbox question (as per mr. reds comment), and while I've found some things useful from the other post it does not go into detail in how those values are saved in a db.
To start with, this is how I would structure your code.
private void SaveMulti(object sender, RoutedEventArgs e)
{
string CStr = Manifesting.Properties.Settings.Default.PSIOpsSurfaceCS;
string Query = "INSERT INTO TestLoop (Record) Values (#Record)";
string Record = StackBoxes.Children.OfType<TextBox>().ToString();
using(var conn = new SqlConnection(CStr))
{
foreach (TextBox TestTB in StackBoxes.Children.OfType<TextBox>())
{
using(var cmd = new SqlCommand(Query, conn))
{
try
{
//Removed as pointed out by EJoshuaS
//cmd = conn.CreateCommand();
cmd.Parameters.AddWithValue("#Record", TestTB.Text);
cmd.ExecuteNonQuery();
}
catch (SqlException ex)
{
MessageBox.Show(ex.ToString());
}
}
}
}
}
Next, what is the purpose of this line?
string Record = StackBoxes.Children.OfType<TextBox>().ToString();
When you do this:
Command = Connection.CreateCommand();
Command.Parameters.AddWithValue("#Record", TestTB.Text );
Command.ExecuteNonQuery();
You "clobber" whatever was in Command before, so you never specify anywhere what the actual query is. That's why it gives you the exception - you literally give it no query to run.
Note that this line:
SqlCommand Command = new SqlCommand(Query, Connection);
currently makes no difference whatsoever.
I use OleDbDataAdapter and OleDbCommandBuilder to fill DataSet object with database contents, and then update database according to a changes that I made in the DataSet. The problem is that I get the exception: "Concurrency violation: the UpdateCommand affected 0 of the expected 1 records". I've found an explanation of this error:
Because a record could have been modified after it was returned from
the SELECT statement, but before the UPDATE or DELETE statement is
issued, the automatically generated UPDATE or DELETE statement
contains a WHERE clause, specifying that a row is only updated if it
contains all original values and has not been deleted from the data
source. Where an automatically generated update attempts to update a
row that has been deleted or that does not contain the original values
found in the DataSet, the command does not affect any records, and a
DBConcurrencyException is thrown.
That means that auto generated UPDATE command affected 0 rows in the database. I work with paradox(db-file) database and no one changes it except for me. I guess that my program changes the same row two times somewhere. I wanted to debug my program by executing all generated queries manually and finding which one doesn't affect any row(because actually I'm pretty sure that all changes are made only once and the bug is somewhere else))). Is it possible to run auto generated commands manually?
My code is too big and complicated to post it here but generally it works like this(I made a working project and took it from there)
using System;
using System.Data;
using System.Windows.Forms;
using System.Data.OleDb;
namespace OleDBCommandBuilder
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
string cs = #"Provider=Microsoft.Jet.OLEDB.4.0;";
cs += #"Data Source=C:\FOLDER\1\SPR_KMZ\;";
cs += #"Extended Properties=Paradox 5.x;";
OleDbConnection Connection = new OleDbConnection();
Connection.ConnectionString = cs;
try
{ Connection.Open(); }
catch (Exception ex)
{ MessageBox.Show("Error openning database! " + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); Environment.Exit(0); }
string SQLQuery = "SELECT * FROM SPR_KMZ WHERE REZ<>0";
DataSet SPR_KMZ = new DataSet();
OleDbDataAdapter DataAdapter = new OleDbDataAdapter();
DataAdapter.SelectCommand = new OleDbCommand(SQLQuery, Connection);
OleDbCommandBuilder builder = new OleDbCommandBuilder(DataAdapter);
try
{
DataAdapter.Fill(SPR_KMZ);
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show(String.Format("Error \n{0}\n{1}", ex.Message, SQLQuery));
Environment.Exit(0);
}
DataRow[] SPR_KMZ_rows = SPR_KMZ.Tables[0].Select("Fkmz=10000912 AND REZ=1");
foreach (DataRow SPR_KMZ_row in SPR_KMZ_rows)
{
SPR_KMZ_row["DN"] = Convert.ToDateTime("30.12.1899");//26.12.2008
SPR_KMZ_row["Price"] = Convert.ToDouble(0);//168,92
}
DataAdapter.Update(SPR_KMZ);
System.Windows.Forms.MessageBox.Show("Success!");
Environment.Exit(0);
}
}
}
P.S. Previously it updated the database without concurrency exception, but after a lot of changes(I commented out the line "DataAdapter.Update(SPR_KMZ);" for a long time for debugging reason, so I don't know when exactly this error started to throw)
P.S.S. there are no INSERTs or DELETEs in my code, only UPDATEs...
<<UPDATE>>
I've found what was the problem: if "DN" field has NULL value then after changing it, the auto-generated UPDATE Statement don't affect anything, obviously because "DN" is contained in a primary key and command builder didn't expect for primary key field to have NULL values(who ever would))), no surprise this engine is called "Paradox")))
that's why in
CommandBuilder.GetUpdateCommand().CommandText
in where clause for "DN" field there was this kind of pattern:
... WHERE ((REZ = ?) AND (DN = ?) AND ...
while nullable fields are described like this:
... AND ((? = 1 AND Price IS NULL) OR (Price = ?)) AND ((? = 1 AND Nmed IS NULL) OR (Nmed = ?)) AND ...
P.S.S.S. Hey, I can try to set UpdateCommand manually to fix this!)))
Here is how I've managed to set the UpdateCommand manually and even get SQL code for every UPDATE command that is being executed!(more or less)). It is very helpful while debugging - I can see what sql query failed to execute during DataAdapter.Update(DBDataSet) command.
public void Update(DataSet DBDataSet)
{
DataAdapter.RowUpdating += before_update;
DataAdapter.Update(DBDataSet);
}
public void before_update(object sender, EventArgs e)
{
//Convert EventArgs to OleDbRowUpdatingEventArgs to be able to use OleDbCommand property
System.Data.OleDb.OleDbRowUpdatingEventArgs oledb_e = (System.Data.OleDb.OleDbRowUpdatingEventArgs) e;
//Get query template
string cmd_txt = oledb_e.Command.CommandText;
//Modify query template here to fix it
//cmd_txt = cmd_txt.Replace("table_name", "\"table_name\"");
//fill tamplate with values
string cmd_txt_filled = cmd_txt;
foreach(System.Data.OleDb.OleDbParameter par in oledb_e.Command.Parameters)
{
string par_type = par.DbType.ToString();
string string_to_replace_with = "";
if (par.Value.GetType().Name == "DBNull")
{
string_to_replace_with = "NULL";
}
else
{
if (par_type == "Int32")
{
par.Size = 4;
string_to_replace_with=Convert.ToInt32(par.Value).ToString();
}
else if (par_type == "Double")
{
par.Size = 8;
string_to_replace_with=Convert.ToDouble(par.Value).ToString().Replace(",",".");
}
else if (par_type == "DateTime")
{
par.Size = 8;
/* In Paradox SQL queries you can't just specify the date as a string,
* it will result in incompatible types, you have to count the days
* between 30.12.1899 and the required date and specify that number
*/
string_to_replace_with = DateToParadoxDays(Convert.ToDateTime(par.Value).ToString("dd.MM.yyyy"));
}
else if (par_type == "String")
{
string_to_replace_with = '"' + Convert.ToString(par.Value) + '"';
}
else
{
//Break execution if the field has a type that is not handled here
System.Diagnostics.Debugger.Break();
}
}
cmd_txt_filled = ReplaceFirst(cmd_txt_filled, "?", string_to_replace_with);
}
cmd_txt_filled = cmd_txt_filled.Replace("= NULL", "IS NULL");
//Get query text here to test it in Database Manager
//System.Diagnostics.Debug.WriteLine(cmd_txt_filled);
//Uncomment this to apply modified query template
//oledb_e.Command.CommandText = cmd_txt;
//Uncomment this to simply run the prepared update command
//oledb_e.Command.CommandText = cmd_txt_filled;
}
public string ReplaceFirst(string text, string search, string replace)
{
int pos = text.IndexOf(search);
if (pos < 0)
{
return text;
}
return text.Substring(0, pos) + replace + text.Substring(pos + search.Length);
}
private static string DateToParadoxDays(string date)
{
return (Convert.ToDateTime(date) - Convert.ToDateTime("30.12.1899")).TotalDays.ToString();
}
I want to add new values to MySql database from dataGridView1. The code itself appears to be correct, no errors in Visual Studio 2012, but there is no data inserted in my db.
Here's the code I'm using:
private void button2_Click(object sender, EventArgs e)
{
confirm exec = new confirm();
}
public class confirm
{
public void method(DataGridViewCellEventArgs f)
{
DataGridView dataGridView1 = new DataGridView();
Label label1 = new Label(); // contains User ID which is used for payer_code
Label label6 = new Label(); // contains current dayTime
foreach (DataGridViewRow row in dataGridView1.Rows)
{
if ((bool)dataGridView1.Rows[f.RowIndex].Cells["paidDataGridViewTextBoxColumn"].Value == true)
{
try
{
string MyConnectionString = "Server=localhost; Database=contractsdb; Uid=root; Pwd=";
MySqlConnection connection = new MySqlConnection(MyConnectionString);
MySqlCommand cmd = new MySqlCommand();
cmd = connection.CreateCommand();
connection.Open();
cmd.CommandText = "INSERT INTO payments(pay_name, pay_code, payer_code, pay_sum, pay_date)VALUES(#pay_name, #pay_code, #payer_code, #pay_sum, #pay_date)";
cmd.Parameters.AddWithValue("#pay_name", dataGridView1.Rows[f.RowIndex].Cells["contractnameDataGridViewTextBoxColumn"].Value);
cmd.Parameters.AddWithValue("#pay_code", dataGridView1.Rows[f.RowIndex].Cells["contractcodeDataGridViewTextBoxColumn"].Value);
cmd.Parameters.AddWithValue("#payer_code", label1.Text);
cmd.Parameters.AddWithValue("#pay_sum", dataGridView1.Rows[f.RowIndex].Cells["sumDataGridViewTextBoxColumn"].Value);
cmd.Parameters.AddWithValue("#pay_date", label6.Text);
cmd.ExecuteNonQuery();
connection.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
}
}
I think you are misunderstanding something about OOP. Do it like this:
your confirm class method should also have the reference of datagridview1 (you are creating an empty datagridview so it never goes even into the foreach loop)
public void method(DataGridView datagridview1) //remove your first argument, you don't need it anymore
{
//delete the line "DataGridView dataGridView1 = new DataGridView();"
//and keep the rest of the code as it is
foreach (DataGridViewRow row in dataGridView1.Rows)
{
if(row.Cells["paidDataGridViewTextBoxColumn"].Value == true) //it will check every row, and you don't need "DataGridViewCellEventArgs" argument now
{
try
{
//your code, it will be same here
}
}
}
for calling the method:
(use the same button_click event as you were doing)
private void button2_Click(object sender, EventArgs e)
{
confirm exec = new confirm();
exec.method(datagridview1); //pass "datagridview1" reference
}
It will pass the reference of your original datagridview1 to the confirm class.
I have a form with a read only datagrid view. As the user moves the cursor up and down the datagrid view lines I would like to display a graph that is related to the highlighted line. I tried to use DataGridView1_SelectionChanged event, but it never gets executed.
dataGridView1_CellContentClick_1 does the trick, but requires the user to click which I would like to avoid.
public partial class conf_results : Form
{
private DataSet ds = new DataSet();
private DataTable dt = new DataTable();
private NpgsqlDataAdapter da = new NpgsqlDataAdapter();
private NpgsqlCommandBuilder sBuilder = new NpgsqlCommandBuilder();
public conf_results()
{
InitializeComponent();
try
{
// PostgeSQL-style connection string
// Making connection with Npgsql provider
NpgsqlConnection conn;
conn = new NpgsqlConnection(Properties.Settings.Default.connString);
conn.Open();
string sql = "SELECT m.orig_code,m.sejtvonal,round_dbl(m.parm_b,2),round_dbl(m.parm_c,2),round_dbl(m.variance,2),round_dbl(100 /(2^(m.ic50 - 1)),2),m.toxic,m.meres_id, " +
"d.sejtvonal,round_dbl(d.parm_b,2),round_dbl(d.parm_c,2),round_dbl(d.variance,2),round_dbl(100 /(2^(d.ic50 - 1)),2),d.toxic,d.meres_id " +
"from vegyulet_curve m, vegyulet_curve d where m.assay_id=d.assay_id and m.orig_code=d.orig_code "+
"and m.sejtvonal='Mes-Sa' and d.sejtvonal='Dx5'";
da = new NpgsqlDataAdapter(sql, conn);
sBuilder = new NpgsqlCommandBuilder(da);
DataSet ds = new DataSet();
// filling DataSet with result from NpgsqlDataAdapter
//da.Fill(ds);
da.Fill(ds, "vegyulet_curve");
// since it C# DataSet can handle multiple tables, we will select first
dt = ds.Tables["vegyulet_curve"];
// connect grid to DataTable
dataGridView1.DataSource = ds.Tables["vegyulet_curve"];
conn.Close();
}
catch (Exception msg)
{
// something went wrong, and you wanna know why
MessageBox.Show(msg.ToString());
throw;
}
}
private void DataGridView1_SelectionChanged(object sender, EventArgs e)
{
int i = dataGridView1.SelectedRows[0].Index;;
// I am rewriting the code to use the chart on the form,
// I was debugging, but I could ot get the control
}
private void dataGridView1_CellContentClick_1(object sender, DataGridViewCellEventArgs e)
{
if (e.RowIndex > -1)
{
//detailForm f = new detailForm(dataGridView1.Rows[e.RowIndex].Cells[11].Value.ToString(),
//dataGridView1.Rows[e.RowIndex].Cells[12].Value.ToString(),
//dataGridView1.Rows[e.RowIndex].Cells[1].Value.ToString());
//this.AddOwnedForm(f);
//f.ShowDialog();
grafikon f = new grafikon(Convert.ToInt32(dataGridView1.Rows[e.RowIndex].Cells[7].Value.ToString()),
dataGridView1.Rows[e.RowIndex].Cells[0].Value.ToString(),
Convert.ToBoolean(dataGridView1.Rows[e.RowIndex].Cells[6].Value.ToString()),
Convert.ToInt32(dataGridView1.Rows[e.RowIndex].Cells[14].Value.ToString()),
Convert.ToBoolean(dataGridView1.Rows[e.RowIndex].Cells[13].Value.ToString()));
this.AddOwnedForm(f);
f.ShowDialog();
}
}
You can use the event CellMouseEnter to avoid having the user click the cell/row.
But take note, that the event will be invoked for each cell and might cause some performance issue if the user moves the cursor horizontally. What you can do is declare a global variable to hold the current row index, and first check when the event is invoked to see if the row changed or not.
I have a SQLDataAdapter, in my query i am fetching two fields, ID(PK), Name.
I registered a sql command builder to my data adapter so i don't have to write queries to update table in database.
when I call the da.update() method, sql throws error that cannot insert null into DimensionID, due to this error, i have to select this field too in my dataset, and then i filled this field in grid with appropriate value. then da.update() worked.
Now the problem is that I don't want this field to appear in my gird, when i set its visible property to false, command builder omits this column in query. To cater this issue I have to set the column width to 0, but there is still a tiny line in my grid.
Is there a better way to handle this situation ? except that I manually write queries.
Below is Code to Populate Grid;
private void frmAttributes_Load(object sender, EventArgs e)
{
ds.Tables.Add("Attributes");
SqlCommand cmd = new SqlCommand();
cmd.Connection = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString);
cmd.CommandText = "select ID,Attribute,Code,DimensionID from DimensionAttribute where dimensionid = " + SelectedAttribute;
da.SelectCommand = cmd;
cb.DataAdapter = da;
da.Fill(ds,"Attributes");
this.dgvAttributes.DataSource = ds.Tables["Attributes"];
this.dgvAttributes.Columns["ID"].Visible = false;
this.dgvAttributes.Columns["DimensionID"].Width = 0;
}
and here is the code behind Updated Button:
private void btnOk_Click(object sender, EventArgs e)
{
if (ds.HasChanges())
{
DialogResult d = new DialogResult();
d = MessageBox.Show("Are you sure you want to save changes to database?", this.Text, MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (d == DialogResult.Yes)
{
try
{
fillDimensionID();
da.UpdateCommand = cb.GetUpdateCommand();
da.InsertCommand = cb.GetInsertCommand();
da.DeleteCommand = cb.GetDeleteCommand();
da.Update(ds,"Attributes");
this.DialogResult = DialogResult.OK;
this.Close();
}
catch (Exception)
{
throw;
}
}
else
{
return;
}
}
}
This is a problem with AutoGeneratedCommands. They require every attribute assigned a proper value before update is triggered.
You can adopt either of the following:
Modify the column DimensionID to accept null values; or
Write your own update SP in the database and register it as UpdateCommand with your data adapter.
hope this will show you the path.