Edelleen tietokantoja - Luento 11

Luentotaltiointi

Ongelmia videon katselussa?

luentomalli (zip)

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:

Entity Data Model

esimerkki (zip)

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;
        }

    }
}

Saving Data in Datasets

Walkthrough: Saving Data to a Database (Multiple Tables)

Be charp with c#, database access

How to: Delete Rows From the Database (LINQ to SQL)

Windows Forms Data Controls and Databinding FAQ

Käyttäjien kommentit

Kommentoi tätä sivua Lisää uusi kommentti
Kurssimateriaalien käyttäminen kaupallisiin tarkoituksiin tai opetusmateriaalina ilman lupaa on ehdottomasti kielletty!
http://appro.mit.jyu.fi/gko/luennot/luento11/
© Antti Ekonoja (antti.j.ekonoja@jyu.fi) <http://users.jyu.fi/~anjoekon/>
Tommi Lahtonen (tommi.j.lahtonen@jyu.fi) <http://hazor.iki.fi/>
Jukka Mäntylä (jmantyla@iki.fi) <http://www.iki.fi/jmantyla/>
2010-10-14 12:30:39
Informaatioteknologia - Jyväskylän yliopiston informaatioteknologian tiedekunta