C# MS Access database - sending second UPDATE command display error - c#

Im trying to create UPDATE command to my program based on C# and Access database. And its working how i want it, but VS2013 display error when i try to update another or the same record second time.
InvalidComObjectException was unhandled.
COM object that has been separated from its underlying RCW can not be
used.
This is how my program looks like:
FormA - Main windows with DataGridView1 of table "Grafik" and Button to open FormB
FormB - Second form with DataGridView2 of table Employyes and Button to FormC
FormC - Form to add, delete and update records directly in to the database "Kategorie" using TextBox'es, ComboBox'es and Buttons (no directly on DataGridView)
UPDATE procedure uses ComboBox (comboBoxWybierzKategorie) to select "category" from database "Kategorie" to update, textBox (textBoxEdytujKategorie) to set new name of selected "Kategoria" and Button to accept procedure.
Broker.cs
public void Update_Kategorie(Kategorie oldKategoria, Kategorie newKategoria)
{
try
{
command.CommandText = "UPDATE Kategorie SET Kategoria = #kat WHERE IDKategorii= #old";
//command.CommandType = CommandType.Text;
command.Parameters.AddWithValue("#kat", newKategoria.Kategoria);
command.Parameters.AddWithValue("#old", oldKategoria.IDKategorii);
connection.Open();
//command.ExecuteNonQuery();
int cmd = command.ExecuteNonQuery();
//connection.Close();
if (cmd > 0)
{
MessageBox.Show("Kategoria zaktualizowana pomyślnie");
//connection.Close();
}
else
{
MessageBox.Show("Wystąpił błąd podczas aktualizacji kategorii.",
"Dodawanie kategorii",
MessageBoxButtons.OK,
MessageBoxIcon.Exclamation,
MessageBoxDefaultButton.Button1);
}
}
catch (OleDbException ex)
{
FormC.cs
private void buttonEdytujKategorie_Click(object sender, EventArgs e)
{
Kategorie oldKategoria = new Kategorie();
Kategorie newKategoria = new Kategorie();
oldKategoria = comboBoxWybierzKategorie.SelectedItem as Kategorie;
newKategoria.Kategoria = Convert.ToString(textBoxEdytujKategorie.Text);
b.Update_Kategorie(oldKategoria, newKategoria);
comboBoxWybierzKategorie.DataSource = b.FillComboBox_Kategorie(); //wypełnij comboBoxWybierzKategorie
textBoxEdytujKategorie.Text = String.Empty; //wyczyść textBoxEdytujKategorie
//this.Close();
//this.Controls.Clear();
//this.InitializeComponent();
I know that code is messed up, sorry for that. More interesting is when i close FormC and reopen it using Button, update function working fine, unless i want use it again.
VS2013 selecting this line as a reason of error:
int cmd = command.ExecuteNonQuery();
Connection to database:
OleDbConnection connection;
OleDbCommand command;
private void ConnectTo()
{
//inside//connection = new OleDbConnection(#"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=bc3e-ps.accdb");
/*outside*/
connection = new OleDbConnection(#"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=D:\bc3e-ps.accdb");
command = connection.CreateCommand();
What im doing wrong?

Since you are adding parameters to the command every time, you should not re-use the command object again.
Just call connection.CreateCommand() every time you want to execute another command.
You can re-use commands, but then you only want to set the existing parameters on each call instead of adding parameters on each call.

Related

MySQL connection doesnt close

I'm hitting a wall right now in my project.
I have a form, with some listboxes, and add button. When I click on the add button, I got a small dialog form with a textbox, an OK and a Cancel button. The app is connected to a MySQL database. So whenever the text change, the program checks if the name exists in the database, disable the OK button and the textbox turn red if the name exists, otherwise it turns them back to normal. Works without a problem when I'm writing and the name doesn't exist, and when it does, it turns red, like it should. And here is the problem. After turning red, it doesn't go back to normal, even when I enter a valid name.
Here is the code :
private void DialogTxb_TextChanged(object sender, EventArgs e)
{
//ConnexionData class where i do all the SQL manipulation
MySqlDataReader selection = ConexionData.CheckSectionName(DialogTxb.Text);
while (selection.Read())
{
if (selection.HasRows)
{
DialogOk.Enabled = false;
toolTip1.Show("La section existe", TooltibLb);
DialogTxb.BackColor = System.Drawing.ColorTranslator.FromHtml("#ffaaaa");
}
else
{
toolTip1.Hide(TooltibLb);
DialogTxb.BackColor = Color.White;
DialogOk.Enabled = true;
}
}
ConexionData.ConexionClose();//Method to close connection
}
I think I have an idea where the problem is but have don't know why it happens and how to solve it. If I simply exit the form and try to do anything else, like select another element from a listbox which will trigger some database interaction, the program close and visual studio provide me with info on the error:"The connexion is already open". I tried to close in other moments of the code, looked for some solutions on the internet, tried MysqlConection.ClearAllPools(), and still the same issue.
Connexion opens and closes properly in other parts of the application.
Thanks for your attention.
Edit
Here are the methods in ConexionData
class ConnexionData
{
private static MySqlConnection Connexion;
public static MySqlCommand Command;
//Many other methods
//.......
public static void ConnexionClose()
{
Connexion.Close();
}
public static MySqlDataReader CheckSectionName(string name)
{
Connexion.Open();
string checkSectionName = ("Select sectionName from section where sectionName = '" + name + "'");
Command.CommandText = checkSectionName;
Reader = Command.ExecuteReader();
return Reader;
}
}
I use the Connexion.Close() in many parts of the program. I have 2 Data Grid views, and some list box where i load data from the database. I open and close those DGV and change values in listbox and all work fine. Then i try my small form to insert a new values on those tables, test it and close it (actually i insert nothing, i simply close it and there is a ConexionData.Close() in the close event) and here where the problem of connection start.
Edit-Solved
I finally solved the problem. I turned Private MysqlConection to public, and directly closed the property after closing the dialog.
if selection.Read() returns true it means you have at least 1 record. It seems you are looking for
private void DialogTxb_TextChanged(object sender, EventArgs e) {
try {
//TODO: check ConexionData.CheckSectionName (re-)creates connection if required
using (MySqlDataReader selection = ConexionData.CheckSectionName(DialogTxb.Text)) {
if (selection.Read()) {
// Name exists (we've read it)
DialogOk.Enabled = false;
toolTip1.Show("La section existe", TooltibLb);
DialogTxb.BackColor = System.Drawing.ColorTranslator.FromHtml("#ffaaaa");
}
else {
// Name doesn't exist: the cursor is empty (so we've failed to read it)
toolTip1.Hide(TooltibLb);
DialogTxb.BackColor = Color.White;
DialogOk.Enabled = true;
}
}
}
finally {
// finally: rain or shine the connection should be closed
ConexionData.ConexionClose(); // Method to close connection
}
}
In case connection is not closing then you can try to call close() connection or sqldatareader before executing method "CheckSectionName()".
FYR Below is some example Let me know, if it helps.
Approch 1:
System.Data.SqlClient.SqlConnection sqlConn = new System.Data.SqlClient.SqlConnection();
if(sqlConn.State!= System.Data.ConnectionState.Closed)
{
sqlConn.Close();
}
System.Data.SqlClient.SqlDataReader SqlReader= new System.Data.SqlClient.SqlDataReader();
if(!SqlReader.IsClosed)
{
SqlReader.Close();
}
MySqlDataReader selection = ConexionData.CheckSectionName(DialogTxb.Text);
Approch 2: We can use "using" clause
using (MySqlDataReader selection = ConexionData.CheckSectionName(DialogTxb.Text))
Approch 3:
Add close() and dispose() into finally block.

Retrieving and assigning a combobox value to a stored procedure parameter

Problem from yesterday...still couldn't get sorted.
Have gone through a million post and videos but couldn't find the one that points out what I'm doing wrong. I basically want to retrieve and assign a combobox's value, and send it to an SQL stored procedure's parameter. I was blamed not to provide code enough yesterday so here's a bit more detailed.
SqlConnection _connection;
SqlCommand _command;
_connection = new SqlConnection(#"Data Source=someserver;Initial Catalog=sometable;Integrated Security=True");
_connection.Open();
_command = _connection.CreateCommand();
_command.CommandType = CommandType.StoredProcedure;
private void SelectedIndexChanged(object sender, EventArgs e)
{
try
{
_command.CommandText = "someprocedurename";
_command.Parameters.Add("#someparameter", SqlDbType.NVarChar).Value = combobox1.SelectedItem.ToString();
_command.ExecuteNonQuery();
}
catch (Exception)
{
MessageBox.Show("Error", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
_connection.Close();
}
}
What happens: when I select the item in the combobox, it first automatically pops up the error message, then it shows the item in the box, but apparently the value of it is not getting passed to #someparameter, or at least the stored procedure is not getting triggered. The stored procedure, written and tested in SQL, works - the problem is in the C# code. I'm aware this might be a lame question for lots of pros out there, but please considerate I've done my research. Please be as specific as you can - not here to get criticized but to improve my newbie skills. Thanks.
C#, Windows Forms
EDIT:
catch block was amended as Sir Henry recommended.
This is what I see now
and after moving _connection.Open(); into the try block,
this is what I see
Pretty much wtf...
EDIT 2:
it seems the problem is gone, thanks to Sir Henry. All I need to figure out now is how to populate the second combobox, based on the stored procedure that was called by the value of the first combobox. This is how the code looks like now:
private void SelectedIndexChanged(object sender, EventArgs e)
{
using (SqlConnection _connection =
new SqlConnection(#"Data Source=someserver;Initial Catalog=sometable;Integrated Security=True"))
try
{
_connection.Open();
SqlCommand _command = new SqlCommand("someprocedurename", _connection);
_command.CommandType = CommandType.StoredProcedure;
_command.Parameters.Add("#someparameter", SqlDbType.NVarChar).Value = combobox1.SelectedValue.ToString();
_command.ExecuteNonQuery();
/* i guess this is the part where i should "inform" combobox2 about the
result of the stored procedure, based on combobox1. have no idea though,
how it could be done. maybe i need a dataset? clueless. just to be clear:
if value "a" was selected in combobox1, i want to populate combobox2 with
the value "1". if value "b" was selected in combobox1, i want to populate
combobox2 with the value "2". etc. */
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
Have you tried combobox1.SelectedValue.ToString(); instead of combobox1.SelectedItem.ToString();
One of the reasons for this kind of issue is you're binding an entity to the ComboBox. Can you share the code of data binding to the ComboBox?
This should work
SqlConnection _connection;
SqlCommand _command;
_connection = new SqlConnection(#"Data Source=someserver;Initial Catalog=sometable;Integrated Security=True");
_connection.Open();
_command = _connection.CreateCommand();
_command.CommandType = CommandType.StoredProcedure;
private void SelectedIndexChanged(object sender, EventArgs e)
{
try
{
_command.CommandText = "someprocedurename";
_command.Parameters.Add("#someparameter", SqlDbType.NVarChar).Value = combobox1.SelectedValue.ToString();
_command.ExecuteNonQuery();
}
catch (Exception)
{
MessageBox.Show("Error", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
_connection.Close();
}
}

How to use a IsPostBack for Update command with timing issue

I have asp.net detailsview which inserts data into payment allocation table, however I want to run a update command that update data into s_transaction_enquiry table, which is done in C#. When I run the code, the update command runs first before the insert command so not data is put into the s_transaction_enquiry table.
I have created the update command to run when user click's on the insert button in the details view.
The insert command is linked to the detailview's Sql Data source.
I have been told I can use "IsPostBack" property in the page_load but sure how to do this, is there anyone who could help me??
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
string conn = "";
string sqlCOmmand = "UPDATE s_transaction_enquiry, payment_allocation SET s_transaction_enquiry.payment_amount = payment_allocation.payment_amount, s_transaction_enquiry.payment_ref = payment_allocation.payment_ref, s_transaction_enquiry.payment_received_date = payment_allocation.payment_received_date WHERE payment_allocation.s_invoice_numer = s_transaction_enquiry.s_invoice_number AND payment_allocation.account_number = s_transaction_enquiry.account_number";
conn = ConfigurationManager.ConnectionStrings["Conn"].ToString();
UpdateRow(conn, sqlCOmmand);
}
}
public void UpdateRow(string connectionString, string insertSQL)
{
using (OleDbConnection connection = new OleDbConnection(connectionString))
{
OleDbCommand command = new OleDbCommand(insertSQL);
command.Connection = connection;
try
{
connection.Open();
command.ExecuteNonQuery();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
Insert statement from datasource:
<asp:SqlDataSource ID="SqlDataSource2" runat="server" ConnectionString="<%$ ConnectionStrings:ConnectionString1 %>" DeleteCommand="DELETE FROM [payment_allocation] WHERE [payment_ref] = ?" ProviderName="<%$ ConnectionStrings:ConnectionString1.ProviderName %>" SelectCommand="SELECT * FROM [payment_allocation]"
InsertCommand="INSERT INTO [payment_allocation] ([payment_ref], [account_number], [account_ref], [allocate_date], [payment_amount], [payment_received_date], [s_invoice_numer]) VALUES (?, ?, ?, ?, ?, ?, ?)">
Button which invokes the insert statement:
<asp:Button ID="Button1" runat="server" CausesValidation="True" CommandName="Insert" Text="Create"/>
you should close your connection after every action. you have opened a connection on 'page_load' but didnt close it, and since page_load executes on every postback as well, which means every time you click your button, you attempt to open a connection again without closing it.
also notice that page_load executes before the button_click event handler therefore every time you click your button you open the connection without closing, and the trying to open it again in the click handler.
OleDbConnection is a disposable object, which means it implements the .dispose() function, which also means that it can be used in a using() statement .
the using() statement creates a new object and disposes of it after.
you have a pretty good explanation on openning/closing/disposing of OleDbConnection in c# here on microsoft website: OleDbConnection Class
basically in order to adapt it to your code, you would want to do something like this,
first put your database handling in a separate function for convenience and call it from page load::
protected void Page_Load(object sender, EventArgs e)
{
string conn = "";
string sqlCOmmand= "inset blablabla into blablabla";
conn = ConfigurationManager.ConnectionStrings["Conn"].ToString();
InsertRow(conn,sqlCOmmand);
}
//taken from microsoft website
public void InsertRow(string connectionString, string insertSQL)
{
using (OleDbConnection connection = new OleDbConnection(connectionString))
{
// The insertSQL string contains a SQL statement that
// inserts a new row in the source table.
OleDbCommand command = new OleDbCommand(insertSQL);
// Set the Connection to the new OleDbConnection.
command.Connection = connection;
// Open the connection and execute the insert command.
try
{
connection.Open();
command.ExecuteNonQuery();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
// The connection is automatically closed when the
// code exits the using block.
}
}
alright, after this part is clear, off to the next one:
i assume you want to insert to the database only when your page loads right? and not after every button click. so you need to direct asp.net to only execute your code if its a first load and not a postback. you would want to change your page_load function to look like that:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
string conn = "";
string sqlCOmmand = "inset blablabla into blablabla";
conn = ConfigurationManager.ConnectionStrings["Conn"].ToString();
InsertRow(conn, sqlCOmmand);
}
}
as for your click handler, you should create another function UpdateRow and do the same with a using() statement and put it in your click handler

Saving changes from a DataGridView back to an SQL database?

I'm trying to make a DataGridView that displays data from an SQL database (GSM.sdf) and saves changes made to the DataGridView back to the database when a save button is pressed. The data displays fine, but nothing happens when the save button is pressed. I've been following the top answer from this thread:
http://social.msdn.microsoft.com/Forums/en/csharpgeneral/thread/98bc0b4d-a2ea-4b74-81f0-473da624528a
But it isn't working out. Here is my code:
namespace WindowsFormsApplication5
{
public partial class Zeelot : Form
{
DataTable table = new DataTable();
SqlCeDataAdapter z;
DataSet gSMDataSet = new DataSet();
public Zeelot()
{
InitializeComponent();
}
private void Zeelot_Load(object sender, EventArgs e)
{
string b = #"Data Source =.\SQLEXPRESS;database=GSM;Integrated Security=FALSE;Connection Timeout=30;User Instance=FALSE";
SqlCeConnection conn = new SqlCeConnection(b);
conn.Open();
string cd = "SELECT * FROM PhoneNumbers";
z = new SqlCeDataAdapter(cd, conn);
z.Fill(gSMDataSet, "PhoneNumbers");
table = gSMDataSet.Tables[0];
conn.Close();
dataGridView1.DataSource = table;
}
private void SaveButton_Click(object sender, EventArgs e)
{
SqlCeCommandBuilder local_SqlCommandBuilder = new SqlCeCommandBuilder(z);
local_SqlCommandBuilder.ConflictOption = System.Data.ConflictOption.OverwriteChanges;
z.UpdateCommand = local_SqlCommandBuilder.GetUpdateCommand();
z.Update(((System.Data.DataTable)this.dataGridView1.DataSource));
((System.Data.DataTable)this.dataGridView1.DataSource).AcceptChanges();
}
}
}
I believe your problem lies where you are casting a DataSource to System.Data.DataTable
Try z.Update(gSMDataSet);
I also don't believe you need AcceptChanges()
you dont need to call .AcceptChanges() on the DataGridView at the end.
AcceptChanges() submits the changes from your DGV to the connected DataSource (your DataTable)
Try calling AcceptChanges() before you try to submit the changes to your Database.
I think your problem is with your connection. You manually create and open a connection and assign it to your DataAdapter. But within your _Load() Method, you close the connection. As the DataAdapter used inside the _Click EventHandler is the same as the one used in your _Load() Method (therefore uses the same connection). It can't submit any changes to your Database because you already closed the connection.
Don't you get any exceptions at runtime?
Try using breakpoints to examine the current state of your connection Object before you attempt to submit your changes to your Database.
Also, AFAIK MS advises against creating connections manually if you want to use them within a DataAdapter.
DataAdapter's Constructor can create a connection on its own

SQL Dependency not Firing

I've tried setting up an SQL Dependency to fire on a "Count Rows" query (written in C#, SQL Server 2008 Express), but after the original subscription SQLNotificationType goes, the event handler never seems to want to fire again (despite rows being added, and I've checked the SQL and it's returning the expected value...).
My code is below. Any thoughts are much appreciated!
EDIT: The project that this code is in is a WPF program. I have this particular code stored in a separate class, which my WPF program creates an instance of in an 'Initialized' event handler. I then have a method in this class that basically calls ConnectToDatabase() first, and then calls SetupSQLDependency().
EDIT 2: As a side note, this program is a WPF which I was hoping to distribute to a few users. The goal was to have the WPF update with certain information whenever new rows were added to a database. I thought that this would be the best way to go about it, as opposed to always querying the database.
private void ConnectToDatabase()
{
//This method is the first to be called, and is the entry
// point into my SQL database code.
databaseConnection = new SqlConnection(connectionString);
// Setup command used in SqlDependecy
SqlCommand tempCmd = new SqlCommand();
tempCmd.Connection = databaseConnection;
tempCmd.CommandText = "SELECT COUNT(ID) FROM [Example].[dbo].[ExampleTable]";
sqlCmd = tempCmd;
try
{ databaseConnection.Open(); }
catch (Exception e)
{ writeDebug(e.ToString()); }
}
private void SetupSQLDependency()
{
SqlDependency.Stop(connectionString);
SqlDependency.Start(connectionString);
sqlCmd.Notification = null;
// create new dependency for SqlCommand
SqlDependency sqlDep = new SqlDependency(sqlCmd);
sqlDep.OnChange += new OnChangeEventHandler(sqlDep_OnChange);
SqlDataReader reader = sqlCmd.ExecuteReader();
}
private void sqlDep_OnChange(object sender, SqlNotificationEventArgs e)
{
// FROM: http://msdn.microsoft.com/en-us/a52dhwx7.aspx
if (e.Type == SqlNotificationType.Change)
{
//++++++ THIS IS THE BLOCK THAT IS NEVER TRIGGERED ++++++//
// Have to remove this as it only work's once
SqlDependency sqlDep = sender as SqlDependency;
sqlDep.OnChange -= sqlDep_OnChange;
// Resetup Dependecy
SetupSQLDependency();
}
else if (e.Type == SqlNotificationType.Subscribe)
{
double te = 12; // Used this just to test a break... code is useless
}
}
I believe the problem here is the COUNT. Refer to the MSDN documentation for Supported SELECT Statements for more info:
The projected columns in the SELECT statement may not contain aggregate expressions unless the statement uses a GROUP BY expression. When a GROUP BY expression is provided, the select list may contain the aggregate functions COUNT_BIG() or SUM(). [...]

Categories

Resources