Jednoduchá databázová aplikace   otázka

VB.NET, Databáze

Už asi dva měsíce zlehka experimentuji s VB 2005 EE, .NET Framework 2.0 a MSSQL Server 2005 EE.

Dřív jsem trochu dělal ve VB 5, takže se seznamuji s něčím o tři generace novějším (k tomu všemu jsem ještě o jednu generaci starší, než většina přispěvovatelů na tomto fóru...:-)

Mým cílem je vytvořit jednoduchou desktopovou databázovou aplikaci, kde na jednom PC (označme je "C") poběží MSSQL server, na něm bude jednoduchá databáze s cca 10-15 tabulkami (stovky, max. vyjímečně tisíce řádků) a též aplikace ve VB, která bude jednak prostřednictvím tří DataGridView prezentovat data z db tabulek v tříhladinovém režimu Master -> Detail a musí také umožňovat samotné pořízení dat jak editací v DataGridView, tak občas přímým programovým přiřazením hodnot do buněk mřížky.

Vždy, když se pro danou celodenní akci data takto předpřipraví, přepne se aplikace na stanici C do režimu ReadOnly a spustí se stejná aplikace na druhém PC v lokální síti (ozačme je "S"), na které bude dále obsluha průběžně občas upravovat již zadané údaje.

Takto "občerstvovaná" data potom bude využívat aplikace na stanici C k dalšímu zpracování (výběr, třídění, další prezentace).

Po počátečním laborování s ConnectionStringy a nastavením SQL Serveru 2005 se mi podařilo rozchodit vzdálený přístup k databázi, ale brzdí mě jiné problémy.

Nejprve jsem se snažil základní model aplikace "naklikat" pomocí různých průvodců, ale již brzy mě začal děsit objem kódu v modulu designer.vb příslušného datasetu, počítaný na tisíce řádků, kterému jsem navíc místy ne zcela rozuměl.

Zkusil jsem tedy celý kód napsat od nuly ručně v následujících intencích:

Jeden dataset, jeden objekt Connection, který uzavírám až při skončení aplikace, pro každou tabulku z databáze objekt SqlCommand s ručně napsaným SELECTem, DataTable, SQLDataAdapter, objekty DataRelation, kterými řeším vazby typu Master->Detail přes objekty BindingSource.

Pro SQL příkazy dataadaptérů jsem zkoušel používat objekt SqlCommandBuilder, ale při bližším zkoumání automaticky vytvořených příkazů začínám i tady přecházet na "ruční práci"

Příklad - pro ilustraci

Public con As SqlConnection
  Public dsDost As DataSet
  Public cmdAkceText As String = "SELECT AKC_ID, AKC_DatumKonani, ZAV_ID, AKC_Nazev FROM Akce ORDER BY AKC_DatumKonani"
  Public cmdAkce As SqlCommand
  Public cmdPom As SqlCommand
  Public parPom As SqlParameter
  Public dtAkce As DataTable
  Public daAkce As SqlDataAdapter
  Public cbAkce As SqlCommandBuilder
  Public WithEvents bsAkce As BindingSource

    Try
      con = New SqlConnection(My.Settings.Default.ConnnectionString)
      con.Open()                                        'Otevreni spojeni na databazi
    Catch exc As SqlException
      MessageBox.Show(exc.Number & vbCrLf & exc.Message)
      Exit Sub
    Catch oexc As Exception
      MessageBox.Show(oexc.Message)
      Exit Sub
    End Try

    dsDost = New DataSet("Dost")                      'Inicializace Datasetu

    'ukázka pouze pro první tabulku
    cmdAkce = New SqlCommand(cmdAkceText, con)        'Definice Selectu pro data adapter Akce
    daAkce = New SqlDataAdapter(cmdAkce)              'Inicializace datoveho adapteru
    dtAkce = New DataTable("Akce")                    'Inicializace datove tabulky dtAkce
    dsDost.Tables.Add(dtAkce)                         'Pridani datove tabulky dtAkce do datasetu
    daAkce.MissingSchemaAction = MissingSchemaAction.AddWithKey  'Zaridi, ze do schematu v Datasetu se pridaji informace o klicich z podkladove DB
    daAkce.Fill(dtAkce)                               'Naplneni tabulky dtAkce z podkladove databaze
    cbAkce = New SqlCommandBuilder(daAkce)            'Automaticke vygenerovani SQL prikazu
    daAkce.InsertCommand = cbAkce.GetInsertCommand    'pro Insert
                                                      'Pro Delete už raději ručně
    cmdPom = New SqlCommand("DELETE FROM [Akce] WHERE [AKC_ID] = @p1", con)
    parPom = New SqlParameter("@p1", SqlDbType.Int, 0, ParameterDirection.Input, False, 0, 0, "AKC_ID", DataRowVersion.Current, Nothing)
    cmdPom.Parameters.Add(parPom)
    daAkce.DeleteCommand = cmdPom
                                                      'Pro Update už raději také ručně
    cmdPom = New SqlCommand("UPDATE [Akce] SET [AKC_DatumKonani] = @p1, [ZAV_ID] = @p2, [AKC_Nazev] = @p3 WHERE [AKC_ID] = @p4", con)
    parPom = New SqlParameter("@p1", SqlDbType.DateTime, 0, ParameterDirection.Input, False, 0, 0, "AKC_DatumKonani", DataRowVersion.Current, Nothing)
    cmdPom.Parameters.Add(parPom)
    parPom = New SqlParameter("@p2", SqlDbType.Int, 0, ParameterDirection.Input, False, 0, 0, "ZAV_ID", DataRowVersion.Current, Nothing)
    cmdPom.Parameters.Add(parPom)
    parPom = New SqlParameter("@p3", SqlDbType.NVarChar, 0, ParameterDirection.Input, False, 0, 0, "AKC_Nazev", DataRowVersion.Current, Nothing)
    cmdPom.Parameters.Add(parPom)
    parPom = New SqlParameter("@p4", SqlDbType.Int, 0, ParameterDirection.Input, False, 0, 0, "AKC_ID", DataRowVersion.Current, Nothing)
    cmdPom.Parameters.Add(parPom)
    daAkce.UpdateCommand = cmdPom

    bsAkce = New BindingSource(dsDostihy, "Akce")               'Inicializace vazebniho prvku pro tabulku Akce
    dgwAkce.DataSource = bsAkce                                 'Inicializace datove mrizky pro tabulku Akce

atd. naplneni datasetu pro další tabulky

dále ukázka vytváření sloupců v hlavní mřížce, kde s výhodou používám sloupec typu DataGridViewComboBoxColumn, pro zobrazení položek, které mají v dané tabulce uloženo pouze ID s vazbou do další tabulky, kde je text, popřípadě další údaje.

Dim AKC_DatumKonani As New DataGridViewTextBoxColumn
    AKC_DatumKonani.DataPropertyName = "AKC_DatumKonani"
    AKC_DatumKonani.HeaderText = "Datum"
    AKC_DatumKonani.Name = "AKC_DatumKonani"
    AKC_DatumKonani.Width = 80
    dgwAkce.Columns.Add(AKC_DatumKonani)

    Dim Zav_Nazev As New System.Windows.Forms.DataGridViewComboBoxColumn
    Zav_Nazev.DataPropertyName = "ZAV_ID"
    Zav_Nazev.DataSource = dtZavodiste   'odkaz do "číselníkové" tabulky
    Zav_Nazev.DisplayMember = "ZAV_Nazev"
    Zav_Nazev.ValueMember = "ZAV_ID"
    Zav_Nazev.HeaderText = "Závodiště"
    Zav_Nazev.Name = "Zav_Nazev"
    Zav_Nazev.Width = 120
    dgwAkce.Columns.Add(Zav_Nazev)

    Dim AKC_Nazev As New DataGridViewTextBoxColumn
    AKC_Nazev.DataPropertyName = "AKC_Nazev"
    AKC_Nazev.HeaderText = "Název"
    AKC_Nazev.Name = "AKC_Nazev"
    AKC_Nazev.Width = 200
    dgwAkce.Columns.Add(AKC_Nazev)

Model odpojené databáze, tak příjemný pro ASP.NET mi pro dané použití příliš nevyhovuje, protože potřebuji všechny editační operace v podstatě okamžitě potvrzovat do podkladové databáze. Zvolil jsem proto událost CurrentChanged příslušného objektu BindingSource pro každou komponentu DataGridView a tam volám proceduru UlozTabulku, která mi pomocí metody Update příslušného SqlDataAdapteru uloží změnu do databáze.

Private Sub UlozTabulku(ByRef dtPom As DataTable, ByRef daPom As SqlDataAdapter)
    BindingContext(dtPom).EndCurrentEdit()
    Dim dt As DataTable = dtPom.GetChanges
    If Not dt Is Nothing Then
      Try
        daPom.Update(dt)
        dtPom.AcceptChanges()
      Catch exc As SqlException
        MessageBox.Show(exc.Number & vbCrLf & exc.Message)
        Exit Sub
      Catch oexc As Exception
        MessageBox.Show(oexc.Message)
        Exit Sub
      End Try
    End If
  End Sub

Jelikož jsem v prostředí .NET úplný začátečník, moc by mi pomohlo, kdyby mi někdo zkouknul popsaný postup a ukázky kódu a popř. mě upozornil, zda nedělám něco špatně, či příliš kostrbatě apod.

Nevím si dále vůbec rady s následujícími problémy:

1. Při pořizování dat v DataGridView se mi nedaří čistě ošetřit chybové stavy.

Potřeboval bych jednoduše nechat obsluhu vždy zadat celý řádek v mřížce, resp. alespoň položky, které jsou Not Null a nenechat ji přejít na jiný řádek dokud se nevyřeší nevyplněné položky NotNull, formátové chyby, a narušení duplicity ve sloupcích, které jsou Unique Key. Zároveň nechci psát příliš mnoho "jednoúčelového" kódu, protože aplikaci budu obsluhovat pouze já, takže komfort nemusí být velký.

Prošel jsem na webu různá doporučení, experimentoval s událostmi Validating a Validated pro buňku, řádek i celou mřížku a nejlepší se mi zdálo odchycení a ošetření události DataError u mřížky, ale událost bohužel nenastává vždy, když bych potřeboval, navíc nemám asi úplně dobře kód událostní procedury, takže často spadnu až do neošetřené výjimky (např.Sloupec SEZ_Poradi nepovoluje hodnoty NULL apod.)

Private Sub Dgw_DataError(ByVal sender As Object, ByVal e As DataGridViewDataErrorEventArgs) Handles dgwAkce.DataError, dgwSeznam.DataError, dgwDetail.DataError
    Dim ErrStr As String  = "Chyba " & e.Context.ToString
    MessageBox.Show(ErrStr)
    e.ThrowException = False
    e.Cancel = True
  End Sub

2. Nemohu přijít na to, jak vynutit uložení změněných řádků v mřížce přes podkladovou DataTable do databáze na serveru v případě, že hodnota do nějaké buňky v mřížce nebyla vložena z klávesnice a posléze odchycena událost BindingSource.CurrentChanged, ale programovým přiřazením přímo do buňky.

např.

dgwDetail.CurrentRow.Cells("CAP_ID").Value = 1

tady se musí zavolat něco, co promítne změnu z mřížky přes BindingSource do tabulky v datasetu, ale zkoušel jsem různé metody EndEdit a Commit, ale nic nezabírá...

Budu vděčen za jakékoli připomínky a rady.

Jirka

nahlásit spamnahlásit spam 2 / 2 odpovědětodpovědět
                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.
  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:
Heslo:

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback