Edelleen tietokantoja - Luento 11
- Luentotaltiointi
- Transaktiot ja poikkeukset
- Vyöryvät poistot
- Päivitykset moneen tauluun
- Lisäykset moneen tauluun
- accept_changes vs adapter.update vs updateall
- Tietokannan muutosten päivittäminen graafisiin komponentteihin
- Liitokset
- Entity Data Model
Luentotaltiointi
Ongelmia videon katselussa?Transaktiot ja poikkeukset
System.Transactions pitää lisätä projektin references-listaukseen.
using System.Transactions; // lisättävä referencesiin using System.Data.SqlClient; try { using (TransactionScope scope = new TransactionScope()) { ... ... scope.Complete(); } } catch (SqlException ex) { } catch (DBConcurrencyException ex) { } catch (TransactionAbortedException ex) { } catch (SystemException ex) { } catch (ApplicationException ex) { }
Vyöryvät poistot
Jos halutaan poistaa tietue, jonka poistaminen estyy viite-eheyden takia, on ensin poistettava kaikki viittaavat tietueet muista tauluista. Tämä on ehdottomasti tehtävä transaktiolla!
reseptiBindingSource.DataMember = "Resepti"; reseptiBindingSource.DataSource = this.ohjaus5DataSet; nimiTextBox.DataBindings.Add(new System.Windows.Forms.Binding("Text", this.reseptiBindingSource, "Nimi", true)); ohjeBindingSource.DataMember = "Ohje_ReseptiID"; ohjeBindingSource.DataSource = this.reseptiBindingSource; this.ohjeDataGridView.DataSource = this.ohjeBindingSource; this.liittyyBindingSource.DataMember = "Liittyy_ReseptiID"; this.liittyyBindingSource.DataSource = this.reseptiBindingSource; this.liittyyDataGridView.DataSource = this.liittyyBindingSource; // poistetaan resepti, joka edellyttää reseptiin viittaavien tietueiden poistoa // ohje- ja liittyy-tauluista // muista myös poikkeusten käsittely! using (TransactionScope scope = new TransactionScope()) { // tyhjennetään liittyy taulusta kaikki reseptiin viittaavat for (int i = liittyyBindingSource.Count - 1; i>= 0; i--) { liittyyBindingSource.RemoveAt(i); } for (int i = ohjeBindingSource.Count - 1; i>= 0; i--) { ohjeBindingSource.RemoveAt(i); } // päivitetään muokatut (poistetut) tietueet tietokantaan ohjeBindingSource.EndEdit(); ohjaus5DataSet.OhjeDataTable deletedOhjeRecords = (ohjaus5DataSet.OhjeDataTable)ohjaus5DataSet.Ohje.GetChanges(DataRowState.Deleted); if ( deletedOhjeRecords != null) ohjeTableAdapter.Update(deletedOhjeRecords); liittyyBindingSource.EndEdit(); ohjaus5DataSet.LiittyyDataTable deletedLiittyyRecords = (ohjaus5DataSet.LiittyyDataTable)ohjaus5DataSet.Liittyy.GetChanges(DataRowState.Deleted); if (deletedLiittyyRecords != null) liittyyTableAdapter.Update(deletedLiittyyRecords); reseptiBindingSource.EndEdit(); reseptiBindingSource.RemoveCurrent(); ohjaus5DataSet.ReseptiDataTable deletedReseptiRecords = (ohjaus5DataSet.ReseptiDataTable)ohjaus5DataSet.Resepti.GetChanges(DataRowState.Deleted); if (deletedReseptiRecords != null) reseptiTableAdapter.Update(deletedReseptiRecords); scope.Complete(); }
Päivitykset moneen tauluun
Päivitykset tehdään samaan tapaan kuin poistotkin. Jos tehdään useampaan tauluun niin on tehtävä transaktiona.
DataRowView v = (DataRowView)liittyyBindingSource.Current; DataRow l = v.Row; l.SetField("Maara", 10); liittyyBindingSource.EndEdit(); // lienee tarpeeton? ohjaus5DataSet.LiittyyDataTable updatedLiittyyRecords = (ohjaus5DataSet.LiittyyDataTable)ohjaus5DataSet.Liittyy.GetChanges(DataRowState.Modified); if (updatedLiittyyRecords != null) liittyyTableAdapter.Update(updatedLiittyyRecords);
Lisäykset moneen tauluun
Lisäykset on tehtävä viite-eheyden edellyttämässä järjestyksessä!
try { using (TransactionScope scope = new TransactionScope()) { // lisää tietoja ohjaus5DataSet.RuokalajiRow r = this.ohjaus5DataSet.Ruokalaji.NewRuokalajiRow(); r.SetField("Nimi", "Hernesoppa"); r.SetField("Kuvaus", "kansallisherkku"); this.ohjaus5DataSet.Ruokalaji.Rows.Add(r); ohjaus5DataSet.RuokalajiDataTable insertedReseptiRecords = (ohjaus5DataSet.RuokalajiDataTable)ohjaus5DataSet.Ruokalaji.GetChanges(DataRowState.Added); if (insertedReseptiRecords != null) ruokalajiTableAdapter.Update(insertedReseptiRecords); this.tableAdapterManager.UpdateAll(this.ohjaus5DataSet); int ruokalajiID = (int)r["RuokalajiID"]; DataRow resepti = this.ohjaus5DataSet.Resepti.NewReseptiRow(); resepti.SetField("Nimi", "Hernesoppa"); resepti.SetField("RuokaLajiID", 1); resepti.SetField("Kuvaus", "kansallisherkku"); resepti.SetField("Henkilomaara", 1); this.ohjaus5DataSet.Resepti.Rows.Add(resepti); ohjaus5DataSet.ReseptiDataTable insertedRRecords = (ohjaus5DataSet.ReseptiDataTable)ohjaus5DataSet.Resepti.GetChanges(DataRowState.Added); if (insertedRRecords != null) reseptiTableAdapter.Update(insertedRRecords); // tätä ei pitäisi aina kutsua. esim. jos lisättäisiin resepti ja siihen liittyvä // ohje niin updateallin kutsu aiheuttaisi ohjeen lisäämisen kahteen kertaan -> poikkeus // mistä tämä johtuu? this.tableAdapterManager.UpdateAll(this.ohjaus5DataSet); scope.Complete(); } } catch (SqlException ex) { this.Text = ex.Message; } catch (DBConcurrencyException ex) { this.Text = ex.Message; } catch (TransactionAbortedException ex) { this.Text = ex.Message; } catch (SystemException ex) { this.Text = ex.Message; } catch (ApplicationException ex) { }
accept_changes vs adapter.update vs updateall
update/updateall päivittää datasetin muuttuneet tiedot tietokantaan. Kutsuu lopuksi accept_changes nollatakseen muutoslaskurit.
accept_changes-metodi nollaa muutoslaskurit eikä tee muutoksia tietokantaan
Update: Calls the respective INSERT, UPDATE, or DELETE statements for each inserted, updated, or deleted row
AcceptChanges: Commits all the changes made to this DataSet since it was loaded or since the last time AcceptChanges was called.
Tietokannan muutosten päivittäminen graafisiin komponentteihin
Periaatteessa tietokantaan sidottujen komponenttien pitäisi päivittää sisältönsä automaattisesti jos niihin liittyvät datasetit muuttuvat. Jos näin ei kuitenkaan tapahdu niin päivityksen voi komentaa fill-metodilla:
this.liittyyTableAdapter.Fill(this.ohjaus5DataSet.Liittyy);
Liitokset
Listaukset joissa pitää yhdistää useamman taulun tietoja pitää tehdä joko linq-kyselyn avulla tai sitten luoda dataset designerissa uusi Tableadapter ja määritellä siihen SQL-kysely, joka hoitaa koostamisen. Koostettua ei voi suoraan sellaisenaan päivittää tietokantaan. esim. listaus jossa näkyy reseptin nimi, aineen nimi, aineen määrä ja yksikkö saadaan seuraavasti:
- Luo uusi TableAdapter datasourcen designerissa valitsemalla kontekstivalikosta Add|Tableadapter
- Kirjoita adapterin SQL-kyselyksi:
SELECT Resepti.Nimi, Liittyy.Maara, Liittyy.Yksikko_Lyhenne, Aine.Nimi AS Expr1 FROM Resepti INNER JOIN Liittyy ON Resepti.ReseptiID = Liittyy.Resepti_ReseptiID INNER JOIN Aine ON Liittyy.Aine_AineID = Aine.AineID
tai sama toisessa muodossa
SELECT Resepti.Nimi, Liittyy.Maara, Liittyy.Yksikko_Lyhenne, Aine.Nimi AS Expr1 FROM resepti, aine, liittyy WHERE resepti.reseptiID = liittyy.resepti_reseptiID AND aine.aineID = liittyy.aine_aineID
- Raahaa uusi tableadapter lomakkeellesi
Entity Data Model
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Data.Objects.DataClasses; namespace edm_resepti { public partial class Form1 : Form { ohjaus5Entities Ctx; public Form1() { InitializeComponent(); Ctx = new ohjaus5Entities(); } void update_grids() { Resepti r = (Resepti)bindingSourceResepti.Current; bindingSourceLiittyy.DataSource = r.Liittyy; dataGridViewLiittyy.DataSource = bindingSourceLiittyy; // piilotellaan turhat kentät dataGridViewLiittyy.Columns["resepti_reseptiID"].Visible = false; dataGridViewLiittyy.Columns["aine_aineID"].Visible = false; dataGridViewLiittyy.Columns["Resepti"].Visible = false; bindingSourceOhje.DataSource = r.Ohje; dataGridViewOhje.DataSource = bindingSourceOhje; dataGridViewOhje.Columns["ReseptiID"].Visible = false; dataGridViewOhje.Columns["Resepti"].Visible = false; } private void Form1_Load(object sender, EventArgs e) { bindingSourceResepti.DataSource = from rr in Ctx.Resepti select rr; // sidotaan labeliin labelResepti.DataBindings.Add(new Binding("Text", this.bindingSourceResepti, "Nimi", true)); update_grids(); } private void buttonPrev_Click(object sender, EventArgs e) { bindingSourceResepti.MovePrevious(); update_grids(); } private void buttonNext_Click(object sender, EventArgs e) { bindingSourceResepti.MoveNext(); update_grids(); } private void buttonLisaa_Click(object sender, EventArgs e) { // seuraava pitäisi olla transaktiona ja poikkeukset pitää napata kiinni Resepti r = new Resepti(); r.Nimi = "Foobar"; r.Henkilomaara = 1; r.RuokalajiID = 1; Ctx.Resepti.AddObject(r); Ohje ohje = new Ohje(); ohje.Ohjeteksti = "ohje1"; ohje.Vaihenro = 1; ohje.Resepti = r; Ctx.Ohje.AddObject(ohje); ohje = new Ohje(); ohje.Ohjeteksti = "ohje2"; ohje.Vaihenro = 2; ohje.Resepti = r; Ctx.Ohje.AddObject(ohje); // vasta SaveChanges tallentaa tietokantaan Ctx.SaveChanges(); // ei päivity näkyville ilman seuraavaa bindingSourceResepti.DataSource = from rr in Ctx.Resepti select rr; } private void buttonPaivita_Click(object sender, EventArgs e) { // seuraava pitäisi olla transaktiona ja poikkeukset pitää napata kiinni Resepti r = (Resepti)bindingSourceResepti.Current; r.Nimi = "Bar Foo päivitetty nimi"; Ohje o = r.Ohje.First(); // ensimmäinen ohje. Pitää muistaa, että ohjeita voi olla monta (one-to-many) o.Ohjeteksti = "Päivitetty ohjeteksti"; Ohje[] kaikki = r.Ohje.ToArray(); // kaikki ohjeet foreach (Ohje item in kaikki) { if ( item.Vaihenro> 1 ) item.Ohjeteksti = "Uusi ohjeteksti"; } Ctx.SaveChanges(); // ei päivity näkyville ilman seuraavaa bindingSourceResepti.DataSource = from rr in Ctx.Resepti select rr; } private void buttonPoista_Click(object sender, EventArgs e) { // poistetaan malliksi resepti // huom.tämä poistaa vaikka viite-eheys kieltäisi eli poistaa väkisten liittyvät tiedot // ohje- ja liittyy-tauluista Resepti r = (Resepti)bindingSourceResepti.Current; Ctx.DeleteObject(r); Ctx.SaveChanges(); update_grids(); } } // lisätään tostring-metodit niin saadaan helposti nätimmät datagridit public partial class Resepti : EntityObject { public override string ToString() { return this.Nimi; } } public partial class Aine : EntityObject { public override string ToString() { return this.Nimi; } } public partial class Liittyy : EntityObject { public override string ToString() { return this.Maara.ToString() + " " + this.Yksikko_Lyhenne + " " + this.Aine.Nimi; } } public partial class Yksikko : EntityObject { public override string ToString() { return this.Nimi; } } }
Walkthrough: Saving Data to a Database (Multiple Tables)
Be charp with c#, database access
Käyttäjien kommentit