I made a simple application that displays data from a database in a DataGridView, users can add rows, delete rows, update values and save the changes.
Now, let's say User A modifies a value in row 8 and saves. User B adds 50 rows and wants to modify a cell in row 8 also. When user B saves, DBConcurrencyException occurs and all his work is lost.
Considering the way people will use this app, this scenario should not happen but there is still a small chance.
Is it possible to keep the added rows when the DBConcurrencyException is raised ? Or should I just tell the users to save as often as possible ?
Here is the relevant code :
private BindingSource bindingSource = null;
private SqlCommandBuilder commandBuilder = null;
string conStringLocal = "xxxxxxxxxxx";
SqlCommand command;
SqlDataAdapter dataAdapter;
DataTable dataTable = new DataTable();
public Form1()
{
InitializeComponent();
DataBind();
}
private void DataBind()
{
dataGridViewCND.DataSource = null;
dataTable.Clear();
string query = "SELECT * FROM myTable";
SqlConnection con = new SqlConnection(conStringLocal);
try
{
con.Open();
command = con.CreateCommand();
command.CommandText = query;
dataAdapter = new SqlDataAdapter(query, con);
commandBuilder = new SqlCommandBuilder(dataAdapter);
dataAdapter.Fill(dataTable);
bindingSource = new BindingSource { DataSource = dataTable };
dataGridViewCND.DataSource = bindingSource;
this.dataGridViewCND.Columns["id"].Visible = false;
this.dataGridViewCND.Sort(this.dataGridViewCND.Columns["Date"], ListSortDirection.Ascending);
}
catch (Exception ex)
{
// GENERIC ERROR MESSAGE
}
}
private void buttonSave_Click(object sender, EventArgs e)
{
try
{
dataGridViewCND.EndEdit();
dataAdapter.Update(dataTable);
DataBind();
// UPDATE SUCCESS MESSAGE
}
catch(DBConcurrencyException ex)
{
// CONCURRENCY ERROR MESSAGE
DataBind();
}
catch (Exception ex)
{
// GENERIC ERROR MESSAGE
DataBind();
}
}
Related
I have a combobox that is filled from a database conditionally by checking off one of 10 checkboxes. Each of the 10 checkboxes contains the code below, which selects a portion of column based on a value in column2.
private void Check1_CheckedChanged(object sender, EventArgs e)
{
if (Check1.CheckState == CheckState.Checked)
{
// SQL Server connection
SqlConnection conn = new SqlConnection(#"Server = Server; Database = DB; Integrated Security = True");
DataSet ds = new DataSet();
try
{
conn.Open();
SqlCommand cmd = new SqlCommand("SELECT [Column1] FROM [DB].[dbo].[Table1] WHERE [Column2] = 50", conn);
SqlDataAdapter da = new SqlDataAdapter();
da.SelectCommand = cmd;
da.Fill(ds);
combo1.DisplayMember = "Column1";
combo1.ValueMember = "ID";
combo1.DataSource = ds.Tables[0];
}
catch (Exception ex)
{
//Exception Message
}
finally
{
conn.Close();
conn.Dispose();
}
}
if (Check1.CheckState == CheckState.Unchecked)
{
combo1.DataSource = null;
}
Therefore, it is rather trivial to fill the combobox with each separate condition. What I want to do that I'm not sure of the approach, however, is that when more than one checkbox is checked, the combobox will display the data from every checked checkbox at once (all this data will be from the same column). Moreover, when a single checkbox is then unchecked, I only want it to remove its own dataset from the combobox and not everything.
Is this possible?
I think it's possible if you have 1 dataset and then you dynamically build up your SQL query. Set up a variable for the columns you want to return based on all the selected comboBoxes.
Use 1 method for all your after update event on the comboBoxes to make it easier and more maintainable.
In terms of mapping the dynamics columns to the dropdowns, I don't work with Winform so I'm not sure but hope this can help a bit.
You can use the for loop to iterate the values you have retrieved and append the combo box value. Example:
comboBox.Items.Clear(); // <-- Declare this at initialization of the page or whatever scenario you have
private void Check1_CheckedChanged(object sender, EventArgs e)
{
if (Check1.CheckState == CheckState.Checked)
{
// SQL Server connection
SqlConnection conn = new SqlConnection(#"Server = Server; Database = DB; Integrated Security = True");
DataSet ds = new DataSet();
try
{
conn.Open();
SqlCommand cmd = new SqlCommand("SELECT [Column1] FROM [DB].[dbo].[Table1] WHERE [Column2] = 50", conn);
SqlDataAdapter da = new SqlDataAdapter();
da.SelectCommand = cmd;
da.Fill(ds);
// Using loop to iterate the values and append the combo box
for(int i=0;i<da.Rows.Count;i++)
{
combo1.Items.Add(da[i]["Column1"].ToString());
combo1.Items[combo1.Itemx.Count-1].Text=da[i]["Column1"].ToString();
combo1.Items[combo1.Itemx.Count-1].Value=da[i]["Column1"].ToString();
}
}
catch (Exception ex)
{
//Exception Message
}
finally
{
conn.Close();
conn.Dispose();
}
}
if (Check1.CheckState == CheckState.Unchecked)
{
combo1.DataSource = null;
}
This is just an example, hope you can get the idea
i have a datagridview which loads mysql database table t_pi_clients on form load event,and i have another tab which contains textboxes of the respective columns of t_pi_client, which am able to get data from fullrowselect mode into those textboxes. now i want to update the database upon changes in the those textbox values. so far i've tried some process and gets my "entry saved" messageBox.show but nothing happens to database, so am hoping someone could help me out maybe am missing something thanks
public partial class frmMain : Form
{
MySqlConnection connection;
MySqlDataAdapter mySqlDataAdapter;
DataSet dt = new DataSet();
DataSet DS = new DataSet();
DataSet dg = new DataSet();
public frmMain()
{
InitializeComponent();
}
#region Main load
private void frmMain_Load(object sender, EventArgs e)
{
var connectionString = ConfigurationManager.ConnectionStrings["Pigen"].ConnectionString;
connection = new MySqlConnection(connectionString);
if (this.OpenConnection() == true)
{
mySqlDataAdapter = new MySqlDataAdapter("select * from t_pi_Clients", connection);
DataSet DS = new DataSet();
mySqlDataAdapter.Fill(DS);
kryptonDataGridView1.DataSource = DS.Tables[0];
kryptonDataGridView1.Columns[0].Visible = false;
mySqlDataAdapter = new MySqlDataAdapter("select * from t_pi_msg_charge_Rate", connection);
DataSet dt = new DataSet();
mySqlDataAdapter.Fill(dt);
kryptonDataGridView2.DataSource = dt.Tables[0];
mySqlDataAdapter = new MySqlDataAdapter("select * from t_pi_client_deposits", connection);
DataSet dg = new DataSet();
mySqlDataAdapter.Fill(dg);
kryptonDataGridView3.DataSource = dg.Tables[0];
}
}
//loads selected row data into textboxes
private void kryptonDataGridView1_DoubleClick(object sender, EventArgs e)
{
textboxClientCode.Text = kryptonDataGridView1.SelectedRows[0].Cells["ClientCode"].Value.ToString();
txtboxClientName.Text = kryptonDataGridView1.SelectedRows[0].Cells["ClientName"].Value.ToString();
txtboxPostalAddress.Text = kryptonDataGridView1.SelectedRows[0].Cells["PostalAdd"].Value.ToString();
txtboxTelephone.Text = kryptonDataGridView1.SelectedRows[0].Cells["Telephone"].Value.ToString();
txtboxFax.Text = kryptonDataGridView1.SelectedRows[0].Cells["Fax"].Value.ToString();
txtboxEmailAddress1.Text = kryptonDataGridView1.SelectedRows[0].Cells["EmailAdd1"].Value.ToString();
txtboxEmailAddress2.Text = kryptonDataGridView1.SelectedRows[0].Cells["EmailAdd2"].Value.ToString();
txtboxEmailAddress3.Text = kryptonDataGridView1.SelectedRows[0].Cells["EmailAdd3"].Value.ToString();
txtboxWebsite.Text = kryptonDataGridView1.SelectedRows[0].Cells["Website"].Value.ToString();
txtboxChargeRate.Text = kryptonDataGridView1.SelectedRows[0].Cells["ChargeRate"].Value.ToString();
txtboxTotalDepo.Text = kryptonDataGridView1.SelectedRows[0].Cells["TotalDeposit"].Value.ToString();
txtboxAccountBal.Text = kryptonDataGridView1.SelectedRows[0].Cells["AccountBal"].Value.ToString();
txtboxEntrydate.Text = kryptonDataGridView1.SelectedRows[0].Cells["EntryDate"].Value.ToString();
}
now i tried this method to update but doesn't update database
private void kryptonbtnUpdate_Click(object sender, EventArgs e)
{
var connectionString = ConfigurationManager.ConnectionStrings["Pigen"].ConnectionString;
using (MySqlConnection Conn = new MySqlConnection(connectionString))
if (Conn.State.ToString() != "Open")
{
}
else
{
connection.Open();
}
try
{
DataTable changes = ((DataTable)kryptonDataGridView1.DataSource).GetChanges();
if (changes != null)
{
MySqlCommandBuilder mcb = new MySqlCommandBuilder(mySqlDataAdapter);
mySqlDataAdapter.UpdateCommand = mcb.GetUpdateCommand();
mySqlDataAdapter.Update(changes);
((DataTable)kryptonDataGridView1.DataSource).AcceptChanges();
mySqlDataAdapter.Update(DS);
}
// adapter.Update(rowsToUpdate);
// mySqlDataAdapter.Update(DS);
MessageBox.Show("Entry Saved");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
This is just a pseudocode of what you need to do
string cmdText = #"UPDATE t_pi_Clients
SET ClientName = #ClientName,
PostalAdd = #PostalAdd,
Telephone = #Telephone,
Fax = #Fax,
.... etc ....
WHERE ClientCode = #ClientCode";
using(MySqlConnection cn = new MySqlConnection(.....))
using(MySqlCommand cmd = new MySqlCommand(cmdText, cn))
{
cn.Open();
cmd.Parameters.AddWithValue("#ClientName", txtboxClientName.Text);
cmd.Parameters.AddWithValue("#PostalAdd", txtboxPostalAddress.Text);
....etc etc...
cmd.Parameters.AddWithValue("#ClientCode", textboxClientCode.Text);
int rowsUpdated = cmd.ExecuteNonQuery();
if(rowsUpdated > 0)
{
// extract the code that loads DataGridView1 from the Form_Load
// and create a reusable method that you could call from here
}
}
First you build an sql command text with the UPDATE clause. I assume that your primary key (the field that uniquely identifies your records) is the ClientCode field.
Then create the connection and the command. Fill the command parameters collection with the parameters required by your text taking the values from the TextBoxes.
Call the ExecuteNonQuery to store the values.
If you succeed then you need to update or reload your datagridview. The best approach would be setting one by one the gridview cells of the current row with the new values from the textboxes, or you could simply extract the code used in form_load to fill the grid and make a new method that you could call from the button click event. (But this could be slower if you have many records)
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 two combobox in my form, using a mysql connection to a remote server. The first combobox populated nicely. However, I need the indexid since that is a foreign key to populate a second combobox. Based on the selection, it will change the data in the second combo (for the xample, i the first combo is for car makes, then the models of each make gets filled, so if I choose Nissan, the models will then have Altima, Maxima, Sentra, ... but if I chose Toyota, the combo will then show Corolla, Camry, Prius, ...)
My foreign key is always -1 for some reason. I am using the selectindex change method, but it keeps crashing/ bc the value is always -1.
I am very new to MySQL in C# and eager to learn. Any help is appreciated. The code is below.
private void cboMake_SelectedIndexChanged(object sender, EventArgs e)
{
if (cboMake.SelectedIndex >= 0)
cboModel.Enabled = true;
else
cboModel.Enabled = false;
// get the foreign key value of the model id to get the make for each brand to populate here
// MessageBox.Show(cboModel.ValueMember);
// right now selectindex always shows -1. why?
// if it changes, you need to then enable the right cbo, else, disable.
// but also, you need the mid, fk, so you can then do a new sql statement with the where clause to populate it.
int fk = cboModel.SelectedIndex;
string connStr = "server=123.456.7.8;user=root;database=car;port=3306; password=nowayjose";
MySqlConnection conn = new MySqlConnection(connStr);
try
{
string sql = "SELECT * FROM cs_model WHERE mid='fk'";
MySqlCommand cmd = new MySqlCommand(sql, conn);
DataTable dt = new DataTable();
MySqlDataAdapter da = new MySqlDataAdapter(cmd);
da.Fill(dt);
cboMake.DataSource = dt;
cboMake.DisplayMember = "model";
cboMake.ValueMember = "mmid";
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString(), "MySQL Connection Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
conn.Close();
}
private void frmMain_Load(object sender, EventArgs e)
{
string connStr = "server=123.456.7.8;user=root;database=car;port=3306; password=nowayjose";
MySqlConnection conn = new MySqlConnection(connStr);
try
{
string sql = "SELECT * FROM cs_make";
MySqlCommand cmd = new MySqlCommand(sql, conn);
DataTable dt = new DataTable();
MySqlDataAdapter da = new MySqlDataAdapter(cmd);
da.Fill(dt);
cboMake.DataSource = dt;
cboMake.DisplayMember = "make";
cboMake.ValueMember = "mid";
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString(), "MySQL Connection Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
conn.Close();
}
Correct me if I'm wrong, but considering code provided, just a guess :
private void cboMake_SelectedIndexChanged(object sender, EventArgs e)
{
int fk = cboModel.SelectedIndex; // ?????
.....
}
You access here cboModel, which is not that one whom item was actually selected. At least lookin on event name cboMake_SelectedIndexChanged, seems to me that you should look on cboMake selected index.
Hope this helps.
private void cboMake_SelectedIndexChanged(object sender, EventArgs e)
{
int foreignKey = (int)cboMake.SelectedIndex +1; // i forgot to cast the int from string since the database was tinyint, it still returns string in c#
string fk = foreignKey.ToString(); // since i need the string for the query, back it goes, but i needed to do arithmetic
string connStr = "server=10.18.30.1;user=notroot;database=car;port=3306;password=password";
MySqlConnection conn = new MySqlConnection(connStr);
try
{
string sql = "SELECT * FROM cs_model WHERE mid=" + fk; // forgot to escape out of the string (used to php $var style within double quotes)
MySqlCommand cmd = new MySqlCommand(sql, conn);
DataTable dt = new DataTable();
MySqlDataAdapter da = new MySqlDataAdapter(cmd);
da.Fill(dt);
cboModel.DataSource = dt; // i had cboModel by a silly mistake
cboModel.DisplayMember = "model";
cboModel.ValueMember = "mmid";
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString(), "MySQL Connection Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
conn.Close();
}
This code reads and updates the form fine when I run the program, but it does not update when I edit the values in the datagridview and click update.
I am following a video tutorial series and this code does work in the video to update the table.
Question: Why does it not update when I edit the values in the datagridview?
using System.Data.SqlServerCe;
namespace WindowsFormsApplication2
{
public partial class Form1 : Form
{
private SqlCeConnection conn; // Our Connection
private SqlCeDataAdapter da; // Data Adapter
private DataSet ds; // Dataset
private string sTable = "authors"; // Table Name
public Form1()
{
InitializeComponent();
InitData(); // Get the Data
dataGridView1.DataSource = ds;
dataGridView1.DataMember = sTable;
}
public void InitData()
{
try
{
// Instantiate the Connection
conn = new SqlCeConnection("Data Source=|DataDirectory|\\Database1.sdf");
// Instantiate a new DataSet
ds = new DataSet();
// init SQLDataAdapter with select command and connection
da = new SqlCeDataAdapter("SELECT id, name, email FROM " + sTable, conn);
// Automatically generates insert, update and delete commands
SqlCeCommandBuilder cmdBldr = new SqlCeCommandBuilder(da);
// Fill in the dataset view
da.Fill(ds, sTable);
}
catch (Exception excep)
{
MessageBox.Show("Exception: " + excep.Message);
}
}
private void button1_Click(object sender, EventArgs e)
{
try
{
da.Update(ds, sTable);
}
catch (Exception excep)
{
MessageBox.Show(excep.Message);
}
}
}
}
The program compiles, but the update command is not working to update the database on click of button1.
This may seem silly but have you checked that the user name you are using to connect has write permissions and not just read-only on the database?