简体   繁体   中英

C# Access database doesn't update data after inserting

I'm working on a school project and I ran into a problem. When I insert/update/delete data from the database, the data isn't shown before I restart the application. I have to close the app and open it again for it to show changes.

When I change the OleDbConnection string (when I change the source), data is updated but it's only shown until I close the app, when I close the app I lose all the new data. Data from the database is shown in dataGridView . When I choose the data source for dataGridView the code line is written automatically in form load event with that code I can update data when the source is changed to the file that doesn't save data.

I'm wondering if there are any other ways to automatically refresh(or with a button) dataGridView after new data has been added. I'm using VS C# 2008 express edition because that is the one we use in school.

Here's the code for the insert button:

 private void buttonInsert_Click(object sender, EventArgs e)
    {

        konekcija.Open();
        OleDbCommand komanda = konekcija.CreateCommand();
        komanda.CommandType = CommandType.Text;
        komanda.CommandText = ("Insert into Sobe(Broj_sobe,Tip_sobe,Telefon,Stanje)values('"
            + textBoxBrSobe.Text + "','" + textBox2.Text +"','" + textBoxTelefon.Text + "','" + textBox1.Text + "')");
        komanda.ExecuteNonQuery();
        
        konekcija.Close();
        MessageBox.Show("Uspjeli ste");
//this is written automatically in form load event
        this.sobeTableAdapter1.Update(this.hotelDataSet1.Sobe);
    }

Here is the connection string, this is the way I've been taught in school.

OleDbConnection konekcija = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:/Users/Pupo/Desktop/Hotel/Hotel/Hotel/Hotel.mdb;Persist Security Info=False");

When I change the connection string to go to the bin/debug folder it updates automatically but doesn't save data.

OleDbConnection konekcija = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=Hotel.mdb;Persist Security Info=False");

OK the reason you're hitting problems and different behaviors when you change the connection string is one of:

  • There are two databases on disk, and you're looking in the wrong one
  • You're doing your data access in the wrong way

You're hitting both of these. First, i'll explain the first one.

When you added the access database to your project (however you did it, there are a few ways) this dialog box (or similar - this is vs 2019) probably appeared

X

It is long and boring, and tells people a lot of stuff they probably don't understand so they just hit Yes and forget about it. Later they will be very confused when trying to save data

This box is basically saying "i'll copy the db into your project folder, but remember that every time you press play, it will be copied again to the BIN folder and your running program will modify the database in the bin folder , not the db in the project folder, and not the db from your desktop (or wherever)"

Later the dev runs the program, saves some data, looks in Access, doesn't find it. Or they run the program again and wonder "where is my data gone that I just saved".

"Every time you run the program the db from the project folder is copied to the bin folder"

It means the database your program saves its data in will be wiped and replaced every time you build your project in visual studio. It doesn't affect a live app (live apps don't build themselves every time you run them), just one you run in VS

And if you look in any db other than the one in BIN folder, you wont find the data, because you're looking in the wrong one

I suggest you find your DB in solution explorer, right click it, choose Properties and change "Copy to output" to "Copy If Newer". This way VS will only replace the db in the bin folder when you've made a change to the db in the project folder (ie added a new table) - this is more like what you want to do, usually


Now I commented that you're doing your data access incorrectly:

private void buttonInsert_Click(object sender, EventArgs e)
{

    konekcija.Open();
    OleDbCommand komanda = konekcija.CreateCommand();
    komanda.CommandType = CommandType.Text;
    komanda.CommandText = ("Insert into Sobe(Broj_sobe,Tip_sobe,Telefon,Stanje)values('"
        + textBoxBrSobe.Text + "','" + textBox2.Text +"','" + textBoxTelefon.Text + "','" + textBox1.Text + "')");
    komanda.ExecuteNonQuery();
    
    konekcija.Close();
    MessageBox.Show("Uspjeli ste");
    //this is written automatically in form load event
    this.sobeTableAdapter1.Update(this.hotelDataSet1.Sobe);
}

There is absolutely no need to make an OleDbCommand in your app, at all. VS writes all that code for you, in a DataSet (HotelDataSet). A TableAdapter contains an OleDbCommand inside it; several in fact. A TableAdapter is a device that downloads data from your db into your dataset, and is also responsible for saving it back to the db when there are changes (new records, changed records and deleted records).

All that magic happens in tableadapter.Update(..) - Update doesn't just run updates. It runs inserts and deletes too - when you download data into a dataset it ends up stored inside a datarow, inside a datatable. This is very similar to databases. DataRows track what your user does - if there is a new row made and added to a datatable it has a RowState of Added. If you change the data in an existing row it has a state of Modified. If you remove the row from the datatable it will be marked as deleted. When you call tableadapter.Update(theDatatable) different SQL INSERT/UPDATE/DELETE will be made to save whatever you did to your Added/Modified/Deleted rows respectively. After you save, a row changes to Unchanged state. If you edit it again, it becomes Modified again, so it will be saved again when you call Update

Therefore if you want to insert a row into the DB, you should Add it to the datatable, then Update to save it. If the datatable is being viewed by eg a DataGridView, then the grid will see the change automatically and show it when you add it

It probably goes like this:

//put this in a button click
hotelDataSet1.Sobe.AddSobeRow(textBoxBrSobe.Text, textBox2.Text, textBoxTelefon.Text, textBox1.Text);

Now that row will appear in a datagridview. It is not in the database yet it is only in the local datatable, which is like a cache/temporary data storage in the client only. You must save it to make it go into the DB. This saves the data:

this.sobeTableAdapter1.Update(this.hotelDataSet1.Sobe);

You claimed that is written in the Form Load; it isn't. The thing in the form load is a call to Fill , NOT Update . Fill is for loading. Update is for saving.

The default behavior of windows forms using datasets when you add bound grids to forms is to call Fill on the form load, to download all data out of the database so you have something to look at. It doesn't call update in the form load because the form is new; there is nothing to update. Any time you want to download data from the db you call Fill.

This is why you hit your second problem when you changed your connection string to point to some different DB not in the BIN folder, and then you used an OleDbCommand to insert data into the db - it only showed up when you reloaded the program because your Form Load calls Fill, which is how your new data gets from the db to the program. Because you used an OleDbCommand to do a direct insert you went completely outside all the data handling built into your program, and inserted to the DB directly. Your program never knew the data was written; you never stored the data in anything in your program that was wired up to the UI

Of course, if you only ever load data once upon loading the form then you will have to restart your program to load the data. The idea is that you can call Fill at other times too; not just at form load. I'll talk about this later

Don't use OleDbCommands to directly insert data in you're working with tableadapters; it will cause a world of confusion and is a bad solution. Use tableadapters how they were intended; load your new data into a datatable (inside a dataset) using a tableadapter and later save the table using the tableadapter. If the tableadapter doesn't load/save how you want you can change that:


Now, you actually probably want to get rid of this line in form load that loads the entire db table into the local dataet, because downloading all data rarely what we do. Loading all million stock prices into the app is a bad idea; just load the ones you want

To give an example of this, open the HotelDataSet file in your project, find some tableadapter, like SobeTableAdapter, right click it, choose Add>>Query. Choose "select that returns rows", put a query like SELECT * FROM Sobe WHERE Broj_Sobe LIKE? , call it FillByBrojSobe, finish the wizard.

Now in your code you can search like:

sobeTableAdapter1.FillByBrojSobe(this.HotelDataSet.Sobe, "ABC"); // fill only records with a Broj_Sobe = "ABC"

Or

sobeTableAdapter1.FillByBrojSobe(this.HotelDataSet.Sobe, "DEF%"); // fill only records with a Broj_Sobe starting with "DEF"

The records are filled into HotelDataSet.Sobe . Any datagridview binded to this (ie a line of code like this will bind the grid to the data: datagridViewSobe.DataSource = HotelDataSet.Sobe - this is usually done in the forms designer so you don't see a line of code like this in your project*) will refresh automatically to show the data you filled. Edit the data in the grid, and save it by wiring up some other button that calls tableadapter.Update(..) . You even normally get a cute one by default when you drop a grid onto the form (out of the Data Sources window), embedded in a toolbar called the BindingNavigator

Remember; stay away from OleDbCommand; you absolutely don't need it, ever. You can add all the queries you'll ever need to a tableadapter using the wizard like I showed above. It's faster and more secure; the query you wrote in the oledbcommand was horribly at risk of SQL injection hacking and writing SQLs like that could one day get you fired. Writing queries into the tableadapter wizard ensures that they will be built into your tableadapter in a secure way that is not at risk of hacking

One day you'll probably move on from datasets and tableadapters and shift to entity framework, but this is a good intro, just a bit of a steep learning curve.

Oh, and install VS2019 even if only on your personal computer if available; 2008 is a horrific experience in comparison (and I don't think it can even work with .NET 4..)

* actually what the form designer usually does is bind a bindingsource to the datatable and then binds the grid to the bindingsource, but i'll ignore that for now

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM