Avatar billede madssch Nybegynder
07. marts 2006 - 01:22 Der er 33 kommentarer og
1 løsning

Gemme formular i DB

Hej Eksperter!

Jeg er stort set ikke mere end én dag gammel i VB.NET, så jeg er nu rendt ind i et sikkert ligetil problem - jeg skal oprette en række i min SQL database og indsætte data fra min formular der i.

Jeg forsøger mig med nedenstående kode, som er en blanding af noget hjemmelavet og lidt stjålet her og der. Det siger sig selv, at det ikke kan virke. Jeg har sikkert brugt de helt forkerte elementer m.v., men se om det giver mening:

    Private Sub CreateButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CreateButton.Click

        Dim CurrentTable As DataTable = UsersDataSet.tblUsers
        Dim NewRow As DataRow = CurrentTable.NewRow()

        NewRow("UserFirstName") = Me.Create_UserFirstNameTextBox.Text
        NewRow("UserLastName") = Me.Create_UserLastNameTextBox.Text
        NewRow("UserAddress") = Me.Create_UserAddressTextBox.Text
        NewRow("UserPostalCode") = Me.Create_UserPostalCodeTextBox.Text
        NewRow("UserCity") = Me.Create_UserCityTextBox.Text
        NewRow("UserPhone") = Me.Create_UserPhoneTextBox.Text
        NewRow("UserEmail") = Me.Create_UserEmailTextBox.Text

        CurrentTable.Rows.Add(NewRow)

        Me.Close()

    End Sub

En løsning + en grundig forklaring kunne sikkert udløse flere points.  :)

På forhånd tak!
Avatar billede bernhof Nybegynder
07. marts 2006 - 09:34 #1
Når du henter data fra databasen, får du en lokal kopi af disse data, som ligger i dit dataset og underliggende datatabeller. Derfor, når du i din kode blot tilføjer en row til CurrentTable, tilføjer du den kun lokalt til din DataTable, og ikke til SQL Server databasen.

Til at gemme i databasen, har du en dataadapter, helt præcist, en System.Data.SqlClient.SqlDataAdapter, når der er tale om SQL Server.

Jeg kan anbefale, at når du arbejder med SQL Server, at du placerer følgende linie øverst i koden:

Imports System.Data.SqlClient

Så behøver du ikke skrive det foran SqlDataAdapter, hver gang du skal bruge den.

Nå, hvor kom vi fra... I koden du har skrevet her, tilføjer du som sagt en row til din DataTable. Enhver row (aka. DataRow) har en egenskab der hedder RowState. Den angiver bl.a. om din row er blevet ændret, tilføjet eller slettet siden du hentede den fra databasen.

SqlDataAdapteren bruger du til, at foretage samme ændringer i din SQL Server database, som dem, der er foretaget i dit DataSet/DataTable. Den fungerer altså på den måde, at den undersøger hver DataRow's RowState egenskab i et DataSet eller en DataTable, og finder på den måde ud af, om den enkelte DataRow skal slettes fra, ændres i, eller tilføjes til SQL Server databasen (eller ingen af delene). Dette gør den ved at eksekvere et tilsvarende SQL statement (INSERT, UPDATE eller DELETE).

Disse tre SQL statements skal du som udgangspunkt selv skrive, men du kan godt få hjælp fra den såkaldte SqlCommandBuilder. Denne kan lave de passende INSERT, UPDATE eller DELETE statements, som skal til for at gemme ændringerne i databasen.

Se følgende eksempelkode:

  Dim Con As New SqlConnection("-- din connection string her --")
  Dim Adapter As New SqlDataAdapter("SELECT * FROM TabelNavn", Con)
  Dim CmdBuilder As New SqlCommandBuilder(Adapter)

  'Nu har vi oprettet en connection, dataadapter og en commandbuilder
  'til vores dataadapter. CommandBuilderen sørger selv for at generere
  'INSERT, UPDATE og DELETE statements ud fra det SELECT statement, som
  'vi angiver når vi opretter SqlDataAdapteren.

  'For at gemme ændringerne til databasen, angiver vi den DataTable,
  'hvis ændringer vi gerne vil gemme, og kalder Update på vores SqlDataAdapter.

  Adapter.Update(UsersDataSet.tblUsers)


Håber det kan bruges!
Avatar billede madssch Nybegynder
07. marts 2006 - 09:55 #3
Hej Bernhof,

Tak for dit meget fine indlæg. Du har givet mig en fin forståelse af, hvordan selve processen foregår - desværre kan jeg ikke rigtig omdanne det til praksis. Roder nok stadig for meget rundt i alle de nye begreber, metoder osv.

Nå, men ændrede koden til følgende:

    Private Sub CreateButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CreateButton.Click

        Dim CurrentTable As DataTable = UsersDataSet.tblUsers
        Dim NewRow As DataRow = CurrentTable.NewRow()

        NewRow("UserFirstName") = Me.Create_UserFirstNameTextBox.Text
        NewRow("UserLastName") = Me.Create_UserLastNameTextBox.Text
        NewRow("UserAddress") = Me.Create_UserAddressTextBox.Text
        NewRow("UserPostalCode") = Me.Create_UserPostalCodeTextBox.Text
        NewRow("UserCity") = Me.Create_UserCityTextBox.Text
        NewRow("UserPhone") = Me.Create_UserPhoneTextBox.Text
        NewRow("UserEmail") = Me.Create_UserEmailTextBox.Text

        CurrentTable.Rows.Add(NewRow)

        Dim NewDbConnection As New SqlConnection("UsersConnectionString")
        Dim Adapter As New SqlDataAdapter("SELECT * FROM tblUsers", NewDbConnection)
        Dim CmdBuilder As New SqlCommandBuilder(Adapter)

        Adapter.Update(UsersDataSet.tblUsers)

        Me.Close()

    End Sub

...og indsatte yderligere "Imports System.Data.SqlClient" øverst i formen (udenfor formen).

Tillægsspørgsmål: Skal alle Imports af ovenstående type importeres i alle aktive forms, eller kan det gøres et centralt sted?

Nå, men desværre virker min Update ikke. Får denne fejl:

"Initialiseringsstrengens format svarer ikke til den specifikation, der starter ved indekset 0."

Desværre er jeg jo ikke lige rutineret nok til at se, om jeg skal gøre mere ved min SQl-sætning eller om problemet ligger et helt andet sted?
Avatar billede bernhof Nybegynder
07. marts 2006 - 14:12 #4
Hehe, ja- jeg er klar over at der kan være meget nyt i det, så forstår godt, at det kan være svært at overskue.

Du har sat det rigtig nok ind i koden. Fejlmeddelelsen kunne jeg dog forestille mig har noget at gøre med din connection string, dvs. den som du har angivet til at være "UsersConnectionString". Er dette en variabel i dit projekt? Hvis det er, prøv da at fjerne gåseøjnene omkring den, og se om det virker. Hvis ikke, kan jeg fortælle, at en connection string typisk ser ud noget a la følgende:

Server=DinServer; Database=DinDatabase; User Id=DitBrugerNavn; Password=DitPassword

Dvs.

  Dim NewDbConnection As New SqlConnection("Server=DinServer ... osv")


Mht. Imports, så skal de enten, som du siger, placeres i hver klasse, eller på projekt-niveau. Hvis du placerer dem på projekt-niveau behøver du således IKKE placere dem i hver enkelt klasse.

Nu sidder jeg godt nok ikke med Visual Studio .NET 2005, men med 2003, så hvis du har 2005, kan det godt være at det skal gøres på en lidt anden måde. Men i 2003 højre-klikker jeg på projektet og vælger egenskaber. Herefter vælger jeg fanebladet Imports, og her kan jeg importere Namespaces (som fx System.Data.SqlClient) på projekt-niveau. Prøv dig frem :-)
Avatar billede madssch Nybegynder
07. marts 2006 - 14:24 #5
Jeg mindes, at da jeg fra starten af oprettede forbindelsen til databasen, blev jeg spurgt, om jeg ville gemme min ConnectionString. det sagde jeg ja til.

Den må jeg vel kunne bruge?

Ellers kan jeg vel være udsat for pludselig at skulle ændre den mange steder i applikationen?

Kan jeg nogen stedet tjekke om denne ConnectionString er gemt nogen steder?

Fjerner jeg forresten " omkring UserConnectionsString, siger VS, at variablen ikke er deklareret.

:(
Avatar billede bernhof Nybegynder
07. marts 2006 - 14:35 #6
Hmm. Det er vist en feature jeg ikke kender til. Måske er det noget 2005-specifikt? Desværre kan jeg ikke hjælpe dig med at finde ud af, hvordan du bruger den gemte connection string, men du kan jo starte med, bare for at afprøve koden, at sætte den ind i stedet for "UsersConnectionString". Se om Adapter.Update kører som den skal.

Ellers har du selvfølgelig ret i, at det smarteste er at have sin connection string stående kun ét sted, så man ikke skal rette flere steder i applikationen.
Avatar billede madssch Nybegynder
07. marts 2006 - 14:41 #7
Det har du naturligvis ret i. Problemet kan måske bare være, at jeg ikke ved, med hvilken brugerkonto databasen er oprettet?

Min kode ser efterhånden sådan ud:

    Private Sub CreateButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CreateButton.Click

        Try

            Dim CurrentTable As DataTable = UsersDataSet.tblUsers
            Dim NewRow As DataRow = CurrentTable.NewRow()

            NewRow("UserFirstName") = Me.Create_UserFirstNameTextBox.Text
            NewRow("UserLastName") = Me.Create_UserLastNameTextBox.Text
            NewRow("UserAddress") = Me.Create_UserAddressTextBox.Text
            NewRow("UserPostalCode") = Me.Create_UserPostalCodeTextBox.Text
            NewRow("UserCity") = Me.Create_UserCityTextBox.Text
            NewRow("UserPhone") = Me.Create_UserPhoneTextBox.Text
            NewRow("UserEmail") = Me.Create_UserEmailTextBox.Text

            CurrentTable.Rows.Add(NewRow)

            Dim NewDbConnection As New SqlConnection("Server=MS\SQLEXPRESS; Database=Users;")
            Dim Adapter As New SqlDataAdapter("SELECT * FROM tblUsers", NewDbConnection)
            Dim CmdBuilder As New SqlCommandBuilder(Adapter)

        Catch ex As Exception

            MessageBox.Show(ex.Message.ToString(), "Alert")

        End Try


        Me.Close()

    End Sub

Afvikler jeg den nu, får jeg ingen fejlmeddelelser, men der bliver desværre heller ikke oprettet noget i min DB.

Hmmm...  :(
Avatar billede bernhof Nybegynder
07. marts 2006 - 15:53 #8
Du mangler lige Adapter.Update(UsersDataSet.tblUsers) lige inden Catch-linien.
Avatar billede bernhof Nybegynder
07. marts 2006 - 16:10 #9
Mht. hvilken bruger...

Du kan evt. benytte integrated security til at logge ind på SQL serveren, hvis den ligger på din lokale maskine. Det gør du ved at skrive følgende istedet (og undlader at angive brugernavn/password):

Server=MS\SQLEXPRESS; Database=Users; Integrated Security=True;

I dette tilfælde benyttes din windows account til at logge ind (den som du er logget ind i windows med).
Avatar billede bernhof Nybegynder
07. marts 2006 - 16:12 #10
Hvis den ikke ligger lokalt, bliver du nødt til at angive brugernavn/password.

Du nævnte at du allerede havde indtastet din connectionstring én gang, da du valgte at gemme den. Der angav du vel brugernavn/password?

Henter du på noget tidspunkt data fra databasen?

Du kan jo, til at starte med, benytte sa-kontoen (system administrator). Det er måske lige i overkanten at give så mange rettigheder til din applikation, men hvis du ikke har helt styr på SQL server, må det være godt nok indtil videre. Da du installerede SQL serveren skulle du sikkert angive et password. Det du angav, er passwordet til sa-kontoen.

Dvs.

Server=MS\SQLEXPRESS; Database=Users; User Id=sa; Password=DitPassword;
Avatar billede madssch Nybegynder
07. marts 2006 - 16:19 #11
Jeg har sådan set fint styr på SQL Serveren. Nok bare ikke den der er integreret i VS.

Jeg har ganske rigtigt problemer med mit login nu. Har prøvet både med Trusted og som SA, men lige meget hjælper det. Med Trusted får jeg denne fejl:

Cannot open database "Users" reguested by the login. The login failed.

Benytter jeg SA, får jeg fejlen "Login failed for user "sa".

Men det kan jo ikke en gang nytte at sætte et login op i min SQL Server, for databasen er jo ikke engang registreret deri..?
Avatar billede madssch Nybegynder
07. marts 2006 - 16:20 #12
Og ja, jeg henter flere steder data UD fra databasen.

Det må jo foregå med mit Windows login, eller..?
Avatar billede bernhof Nybegynder
08. marts 2006 - 00:29 #13
Ja, man må gå ud fra, at det foregår med dit windows login, hvis du ikke på noget tidspunkt har angivet brugernavn/password i VS.

Men du siger, at databasen ikke er tilknyttet din SQL server? Jeg må indrømme, at jeg endnu ikke har særlig meget styr på SQL Server 2005, men jeg er klar over, at man nu kan connecte til databaser, som ikke er tilknyttet serveren. Dog skal man bruge en speciel connection string, hvis det er tilfældet. Jeg har ikke selv prøvet det, men forsøg med følgende:

Server=MS\SQLEXPRESS;AttachDbFilename=C:\StiTilDatabaseFil;Database=NavnPåDatabase;Trusted_Connection=Yes;
Avatar billede madssch Nybegynder
08. marts 2006 - 09:44 #14
Så kom den endelig!

Jeg havde længe forsøge mig med en reference til stien |DataDirectory|, men nu fik jeg pludselig fejlen udskrevet på skærmen - så var det jo ingen problem at se. Gik ind og skrev den absolutte sti - så var den der.

Tak for kampen bernhof!  ;-)

Et lynhurtigt spørgsmål til sidst:

Er princippet det samme for at rette en post i sin DB - eller gøres det anderledes?
Avatar billede bernhof Nybegynder
08. marts 2006 - 10:41 #15
Det er det samme. Som jeg nævnte, så sørger SqlDataAdapteren for at eksekvere UPDATE, DELETE og INSERT statements, alt efter nødvendighed.

Dvs., hvis du retter indholdet af en DataRow, fx:

  UsersDataSet.tblUsers.Rows(0)(0) = "Ny værdi"

sættes denne DataRow's RowState til DataRowState.Modified. I dette tilfælde, hvis du kører din SqlDataAdapter.Update metode, vil den selv sørge for at eksekvere et UPDATE statement for netop denne DataRow.

Idéen med dette er, at du kan arbejde disconnected fra din SQL server, så du én gang opretter forbindelse til databasen, henter alle nødvendige data, og lukker forbindelsen igen, altsammen inden for et splitsekund. Så kan du herefter lade brugeren rette løs i disse data uden at optage serverressourcer, og TIL SIDST, når du eller brugeren føler, at det er tid til at gemme ændringerne i den databasen, kaldes SqlDataAdapater.Update metoden så.

Held og lykke med det.

Selv tak, og tak for point :-)
Avatar billede madssch Nybegynder
08. marts 2006 - 10:49 #16
Alt det her kræver nok lidt tilvending!  :)

Er vant til at udvikle webløsinger - her er man jo stort set konstant i kontakt med databasen.

Mht. det at rette en post synes jeg at have set, at dette kan gøres med ca. 2 linjer kode. Hver eneste TextBox har jo i princippet fået fortalt, hvilket databasefelt det hører til. Ikke?

Jeg opretter gerne et nyt spørgsmål med points, hvis det er...?
Avatar billede bernhof Nybegynder
08. marts 2006 - 10:49 #17
En hurtig tilføjelse:

Hvis du blot ændrer en DataRow's indhold, får den RowState = Modified
Hvis du tilføjer en ny DataRow, får den RowState = Added
Hvis du sletter en allerede eksisterende DataRow, får den RowState = Deleted
Hvis du sletter en DataRow som har RowState = Added, bliver den fjernet fra tabellen igen.

Når jeg siger tilføje en DataRow, mener jeg, at man gør, som i din kode:

  Dim Row As DataRow = MinTabel.NewRow()
  Row(0) = 1
  Row(0) = "Value"
  MinTabel.Rows.Add(Row)

Når jeg siger slette en DataRow, mener jeg, at man kalder Delete metoden på en DataRow (dvs. vi benytter IKKE DataTable.Rows.Remove):

  MinTabel.Rows(0).Delete()

Når jeg siger ændre et DataRow, mener jeg bare, at en eller flere værdier ændres, fx på følgende måde:

  MinTabel.Rows(0)(1) = "Ny værdi"
Avatar billede bernhof Nybegynder
08. marts 2006 - 10:50 #18
Heh, det skulle have været:

  Dim Row As DataRow = MinTabel.NewRow()
  Row(0) = 1
  Row(1) = "Value"
  MinTabel.Rows.Add(Row)
Avatar billede madssch Nybegynder
08. marts 2006 - 10:57 #19
Er princippet ikke, at man arbejder med sine data i hukommelsen - og så sørger systemet for at synkronisere med databasen? Eller at man i givet fald beder den om at synkronisere?

Det jeg tænker på er, at den kode vi har arbejdet med, jo indsætter data direkte i databasen?

Er det i princippet forkert?
Avatar billede bernhof Nybegynder
08. marts 2006 - 11:57 #20
Jo, det er netop princippet, at man skal arbejde med det hele i hukommelsen, indtil man føler det er tid til at gemme data i DB, og det er jo netop det vi gør, når vi arbejder med DataSet osv.

I vores tilfælde, gemmer den kun til DB lige efter DataRow'en er blevet tilføjet, fordi vi kalder Update. Ellers gør den ikke. Du kan jo vælge at lade være med at kalde Update på dette tidspunkt, men vente til senere.

Forstod jeg dit spørgsmål ret?
Avatar billede bernhof Nybegynder
08. marts 2006 - 12:00 #21
Jeg havde lige overset dit spørgsmål vedr. at rette en post.

Jo, du kan godt lave databindings på dine kontroller, så du ikke behøver skrive kode, der aktivt ændrer en given post. Her vil en post/DataRow dog ligeledes have RowState = Modified, når den ændres via fx et tekstfelt.
Avatar billede madssch Nybegynder
08. marts 2006 - 12:02 #22
Ja, det tror jeg.  :)

Og hvis vi i dette tilfælde opretter flere brugere og først senere gemmer i databasen, så opretter den selv samtlige brugere ved at kalde Update?
Avatar billede madssch Nybegynder
08. marts 2006 - 12:03 #23
Vedrørende ret:

Kan du smide de par linjer i et nyt spørgsmål mod lidt points?
Avatar billede bernhof Nybegynder
08. marts 2006 - 12:12 #24
Ja, hvis du i hukommelsen har slettet 10 brugere og oprettet 20 nye, vil et kald til Update forårsage, at de samme 10 brugere slettes og de 20 nye oprettes i den fysiske database, alt på én gang.

Jeg har faktisk arbejdet meget lidt med DataBindings. Det er ikke specielt kompliceret, men jeg kan heller ikke garantere et svar, hvis du opretter et nyt spørgsmål. Men opret det bare alligevel, det kan jo være der er andre der kan hjælpe dig :o)
Avatar billede madssch Nybegynder
08. marts 2006 - 12:15 #25
Ok. Endnu engang en kæmpe tak for hjælpen!

Skal sgu nok få det lært..!  ;-)
Avatar billede bernhof Nybegynder
08. marts 2006 - 12:17 #26
Jeg kan give dig følgende at prøve dig frem med:

  Dim b As New Binding("Text", UsersDataSet.tblUsers, "UserName")
  MyTextBox.DataBindings.Add(b)

Se om det virker.
Avatar billede bernhof Nybegynder
08. marts 2006 - 12:18 #27
Hvor "Text" er navnet på den egenskab på din kontrol, som skal være bundet til et givent felt ("UserName" parameteren) på en given datakilde (UsersDataSet.tblUsers)

Koden kan fx placeres i Form_Load proceduren.
Avatar billede madssch Nybegynder
08. marts 2006 - 12:23 #28
Forstår vist ikke...

I VS markerer jeg min textbox, vælger "Databindings" i mit "Properties"-vindue, vælger "Text" og navigerer herefter frem til det aktuelle felt i databasen gennem min BindingSource.

Den registrerer måske ikke ændringer?
Avatar billede bernhof Nybegynder
08. marts 2006 - 12:26 #29
Jo, det er sådan man gør det fra designeren, og det er mindst lige så godt :-)

Den opretter faktisk kode, som den jeg netop skrev, den er bare skjult fra dig, når det gøres i designeren.
Avatar billede madssch Nybegynder
08. marts 2006 - 12:34 #30
Men stadig skal et eller andet jo kaldes, ikke?

Jeg mener, jeg åbner en form med nogle brugeroplysninger, ændrer navnet og lukker formen - så bliver intet jo gemt.

Der skal jo laves en sub med en click_event af en art?

(Når vi er færdige med dette emne, opretter jeg et nyt spørgsmål med points til dig).
Avatar billede bernhof Nybegynder
08. marts 2006 - 13:20 #31
Hvad mener du med gemt? Gemt i hukommelsen, eller gemt i databasen?

Hvis du laver databinding på dit tekstfelt, vil værdien blive ændret i DataRow'en så snart du ændrer teksten i dit tekstfelt, og altså er det ikke nødvendigt at lave kode, som gemmer ændringerne. Hvis du vil gemme i databasen, skal du dog, som før, benytte en dataadapter.
Avatar billede madssch Nybegynder
08. marts 2006 - 13:29 #32
Hvad så i dette scenarie:

Du har en liste med brugeren. Listen viser f.eks. fornavn og efternavn.

Ved klik på en bruger, åbner en ny form med brugerens data udfyldt i nogle textboxes. Man retter nu f.eks. fornavnet, men fortryder. Man klikker på X'et øverst på formen.

Så regner man ikke med, at der er foretaget nogen rettelse - men det er der faktisk?
Avatar billede bernhof Nybegynder
08. marts 2006 - 13:32 #33
Ja, der er faktisk foretaget ændringer. I det tilfælde ville man nok bruge RejectChanges metoden på den DataRow, som der blev ændret i, hvis brugeren trykkede Annuller, eller lukkede med krydset. RejectChanges annullerer ændringer, som er foretaget på en given DataRow (metoden kan også kaldes for en hel tabel, eller et helt dataset).
Avatar billede madssch Nybegynder
08. marts 2006 - 19:46 #34
Hehe... Okay, det må jeg sætte mig ind i senere!  :D

Har sat lidt points af til dig:

http://www.eksperten.dk/spm/693654
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester