11. november 2008 - 23:36Der er
36 kommentarer og 1 løsning
Concurrency-fejl når jeg tilgår Access database
Hej eksperter...
Jeg er en nybegynder i programmering, og for at lære at programmere i C#, er jeg ved at udvikle et program til at lave vagtplaner og optælling af timer for medarbejdere.
I den første fase af udviklingen, benyttede jeg en SQL Server 2005 til at gemme og hente oplysninger om bl.a. medarbejdere og medarbejderes timer. Jeg opererede med 6 forskellige database-tables, og 8 forskellige datasets med udtræk heraf.
Jeg besluttede mig for at gå over til at bruge en Access fil som database i stedet (ikke af driftsmæssige årsager) og så opstod en række problemer. Det største problem lige nu er, at jeg bliver tæppebombet med concurrency-fejl; ved operationer hvor jeg tilgår databasen og vil opdatere en række, får jeg smidt en concurrency violation (updated 0 out of expected 1 rows) i hovedet, og under NOGLE operationer hvor jeg vil oprette en ny række, bliver jeg mødt med en OleDbException fordi at jeg prøver at oprette duplicate values af min primary id kolonne.
Jeg har prøvet en række forskellige ting for at få koden til at virke på baggrund af de løsninger jeg har kunne læse på engelsk-sprogede fora:
- sat datasettenes id-kolonnes properties "AutoIncrementSeed" og "AutoIncrementStep" til 0 hhv. 1, 1 hhv 1 samt sat begge til negative værdier
- skiftet Id-kolonner i alle tables i Access-databasen til number (altså IKKE autogenererede numre)
Sidstnævnte gjorde at jeg kunne oprette NOGLE rækker i NOGLE Datasets (og opdatere dem til databasen) men jeg bliver stadigvæk mødt af mange exceptions...
Mit spørgsmål er nu:
Er der ikke en måde at få Access til at opføre sig lidt mere modent og være lidt SQL Server 2005- agtig?
Programmet er ikke tiltænkt at skulle tilgåes af mere end en bruger, men jeg er ikke interesserede i at have en Id-kolonne med der tillader duplikerede værdier...
For at være helt ærlig, lyder det mere som om det er noget i din kode som giver problemer, fremfor Access databasen. Kan vi eventuelt se lidt af den kode som skriver til databasen?
Jo selvfølgelig... Men jeg har svært ved at se at det skulle være min kode der er årsag til problemet, da den fungerede fint med med SQL Server 2005...
Jeg har sat primærnøgler på alle tables i min database.
Fejlen jeg får ved updates af databasen er:
DBConcurrencyException: Concurrency violation: the UpdateCommand affected 0 of the expected 1 records.
Jeg har fundet en løsning som virker nogenlunde, og det er at kalde en .Clear() på datasettet efter hver update, og så fylde det igen, men jeg frygter at det vil blive meget ressourcekrævende når antallet af rækker i nogle af tables f.eks. runder de 10.000.
Et andet problem jeg er stødt på, er at Access har ændret Id-kolonnen i nogle af mine datasets´ AutoNumber indstilling, til at generere random-værdier. Jeg kan ikke ændre det tilbage igen, da "random-valued AutoNumber fields ikke kan ændres til incremental AutoNumber fields"
Jeg må indrømme at jeg ikke har hørt om sådan et problem før! Det virker lidt som en bug i TableAdapter klassen. Hvis du får performanceproblemer med mange rækker, kan du overveje og droppe brugen af TableAdapter, og arbejde direkte med ADO.NET klasserne. Her er et eksempel:
selectCommand.CommandText = "select * from sheet1"; OleDbDataAdapter adapter = new OleDbDataAdapter(selectCommand);
OleDbCommandBuilder cmdBuilder = new OleDbCommandBuilder(adapter); cmdBuilder.GetInsertCommand();
adapter.Update(theDS.Sheet1);
I eksemplet er theDS mit dataset. sheet1 er tabellen i databasen jeg inserter i. Jeg testede med at inserte 10000 rækker i en tabel, og det tog 7 sekunder. Alternativt kan det gøres uden at bruge dataadapteren. Nedenstående ekstempel insertede 10000 rækker på 3,5 sekunder:
Access er uanset en super langsom database. Jeg prøvede at køre samme eksempel imod en SQLite database, som er en filbaseret database ligesom Access. Denne tog 300 millisekunder. Samme eksempel, bare med en million rækker, tog 10 sekunder i SQLite. Jeg tør slet ikke køre dette eksempel imod Access, da det sikkert kommer til at tage mere end 20 minutter, hvis det overhovedet kan lade sig gøre.
Problemet med random-valued AutoNumber fields, har jeg desværre ingen anelse om hvordan man løser, men du bliver nok nød til at oprette tabellen forfra.
Som rookie har det været super-lækkert at arbejde med et visuelt interface som table-adapteren, men jeg kan godt se, at jeg nok bliver nødt til at bevæge mig ud på mere usikkert farvand, og skrive mine egne connection-strings osv....
Angående SQlite: Pointen med at overgå til Access-databasen, var at brugeren ville have filen liggende decentralt på sin egen computer (eller netværks-drev), uden at skulle installere en masse ekstra software. Ville jeg kunne opnå samme effekt med SQLite? For så tror jeg helt klart at jeg vil overveje at skifte...
Som sagt er jeg ikke synderligt rutineret udi den her kunst, så det vil nok tage mig (minimum) nogle dage at efterprøve dine råd. Det lyder dog til at jeg ved at arbejde på minimum én af de valgmuligheder du foreslår vil kunne nå frem til en løsning, så jeg ser ingen grund til at du skal vente på dine point. Hvis du sender mig et svar, tilskriver jeg dig pointene...
SQLite er endnu nemmere at bruge end Access. For at bruge Access fra .NET, skal Microsoft Jet være installeret. Microsoft Jet er som default installeret i Windows, men man hører indimellem om maskiner hvor den ikke er installeret. Med SQLite, skal du bare følge det første link jeg angav i min tidligere kommentar og downloade SQLite-1.0.60.0-setup.exe filen, og installere på din udviklingsmaskine. For at bruge SQLite i dit program, skal du tilføje en reference til System.Data.SQLite. Når dit projekt skal køre på andre maskiner, skal du bare sørge for at System.Data.SQLite.DLL filen bliver kopieret sammen med exe filen. Der skal ikke installeres noget på de maskiner som skal køre dit program.
Hvis du skal bruge en connectionstring til en eller anden database, kan jeg anbefale dig at se på denne side: www.connectionstrings.com Jeg kan se at siden er nede lige nu, men når den virker er det en god reference.
Hvis du vil, kan jeg godt lave et lille simpelt SQLite eksempel til dig?
Jeg tror ikke at det bliver nødvendigt med et SQLite eksempel, men tak for tilbuddet. Jeg vil følge dit råd, og gå med SQLite, og så ellers tage det i stiv arm derfra...
Hej Arne_v. Prøv at wrappe dine queries i en transaction, så vil SQLite koden garanteret køre hurtigst af alle databaser du har testet. Det er et kendt spidsfindighed ved SQLite, at den er langsom til at oprette transaktioner. I dit eksempel opretter og comitter den 10000 transaktioner.
Jeg har prøvet at køre din test, hvor den eneste ændring er en transaktion rundt om løkken som kalder ExecuteNonQuery(), som vist herunder. Derudover har jeg fjernet firebird fra teste, da jeg ikke har denne database på min maskine.
using (IDbTransaction transaction = con.BeginTransaction()) { ins.Transaction = transaction; for (int i = 0; i < NREC; i++) { ((IDbDataParameter)ins.Parameters["@f1"]).Value = "K" + i; ((IDbDataParameter)ins.Parameters["@f2"]).Value = "Dette er bare en lille test"; ins.ExecuteNonQuery(); } transaction.Commit(); }
Resultatet: MS Access : 3,72 seconds SQLite : 0,45 seconds SQLServer CE : 0,87 seconds MS Access : 3,67 seconds SQLite : 0,67 seconds SQLServer CE : 0,73 seconds MS Access : 3,14 seconds SQLite : 0,52 seconds SQLServer CE : 0,75 seconds
Jeg prøvede at køre testen med 100000 rækker. MS Access : 37,41 seconds SQLite : 3,14 seconds SQLServer CE : 7,29 seconds MS Access : 35,36 seconds SQLite : 3,19 seconds SQLServer CE : 7,22 seconds MS Access : 32,66 seconds SQLite : 3,53 seconds SQLServer CE : 7,33 seconds
Hvis du skal indsætte mange rækker i en SQLite database, bør indsætte alle rækkerne i en transaktion. SQLite er forholdsvis langsom til at oprette og committe transaktioner, så hvis du indsætter mange rækker uden at gøre det i en transaktion, vil SQLite oprette en transaktion for hver række.
Problemet opstår når jeg importere data fra en tekstfil til databasen, kan selvfølgelig godt være SQLite Administratoren der ikke viser tegnene rigtigt.
Det er vel heller ikke nogen særlig typisk brug, med 10000 transaktioner i minuttet!? Desuden har SQLite i de fleste tilfælde også bedre performance i selects og joins, end de andre databaser vi sammenligner med.
Det er klart at der er situationer, hvor SQLite ikke er en optimal database at bruge, men når man sammenligner med Access og SQL Server CE, synes jeg ikke der er tvivl om hvilken database der er den bedste. Hvis man har brug for en database med flere features end SQLite, og som håndhæver relationer og datatyper, synes jeg Firebird er et meget godt alternativ. Firebird er dog lidt mere besværlig end SQLite at sætte op til at køre embedded.
Efter at dette spørgsmål blev oprettet, har jeg testet lidt på VistaDB. VistaDB er en embedded database som er skrevet i C#. VistaDB har de fleste features man ønsker sig i en database, blandt andet har den CLR stored procedures. Da den er 100% managed, kræver den bare medium trust for at køre, hvilket betyder at den kan bruges som drag 'n drop database på de fleste webhoteller, hvilket du hverken kan med SQLite, SQL Server CE eller Firebird. Denne kan i mange tilfælde være et interessant alternativ. Der findes en gratis Express version. Den har dog langt fra så god performance som SQLite.
groyk -> Jeg bruger SQLite Administrator som databaseværktøj. Jeg har aldrig haft problemer med æ, ø og å i denne, så jeg tror ikke det er der problemet ligger. Hvis du stadig har problemer med dette, kan du så ikke vise lidt af din kode her?
Her er den kode jeg bruger til at smide data ind i SQLite med
Public Sub opdater_sanistaal()
Dim CountErrors As Integer = 0
Dim SQLconnect As New SQLite.SQLiteConnection() Dim SQLcommand As New SQLite.SQLiteCommand SQLconnect.ConnectionString = "Data Source=" & Main.DB2 & ";" SQLconnect.Open() SQLcommand = SQLconnect.CreateCommand 'Insert Record into Fo Using transaction As IDbTransaction = SQLconnect.BeginTransaction() SQLcommand.Transaction = transaction
' Looper ALLE inserts igennem Dim i As Integer i = FreeFile() Dim ii As Integer Dim Vl(19) As String Dim SQLstring As String = ""
If System.IO.File.Exists(Main.DB1) Then FileOpen(i, Main.DB1, OpenMode.Input) Do While Not EOF(i) Vl = Split(LineInput(i).Replace("""", ""), ";")
SQLstring = "" For ii = 0 To Vl.Length - 1 SQLstring = SQLstring + "'" + Vl(ii) + "'" If Not ii = Vl.Length - 1 Then SQLstring = SQLstring + "," Next
If Not SQLstring = "" Then Try If Vl(0) = "OPR" Then 'Insert = Insert + SQLstring + ";" 'MsgBox(SQLstring) 'SQLcommand.CommandText = "DELETE FROM sanistaal WHERE saninr = " + Vl(2) SQLcommand.CommandText = "INSERT INTO sanistaal (identifikation,varenr,saninr,varegruppe,varetekst,subvaretekst,listepris,enhedsbetegnelse,enhedsfaktor,minsalg,rabattype,rabatkode,meterprlgd,kgprmeter,direktesogeord,eankode,rabatiprocent,rabatikr,nettopris,kundensvarenr) VALUES (" + SQLstring + ")" SQLcommand.ExecuteNonQuery() End If If Vl(0) = "ÆND" Then SQLcommand.CommandText = "DELETE FROM sanistaal WHERE saninr = " + Vl(2) SQLcommand.CommandText = "INSERT INTO sanistaal (identifikation,varenr,saninr,varegruppe,varetekst,subvaretekst,listepris,enhedsbetegnelse,enhedsfaktor,minsalg,rabattype,rabatkode,meterprlgd,kgprmeter,direktesogeord,eankode,rabatiprocent,rabatikr,nettopris,kundensvarenr) VALUES (" + SQLstring + ")" SQLcommand.ExecuteNonQuery() End If If Vl(0) = "SLT" Then SQLcommand.CommandText = "DELETE FROM sanistaal WHERE saninr = " + Vl(2) SQLcommand.ExecuteNonQuery() End If Catch ex As Exception CountErrors += 1 End Try End If
Loop End If 'Catch ex As Exception
'End Try FileClose(i) ' Færdig
transaction.Commit() End Using SQLcommand.Dispose() SQLconnect.Close()
Som arne_v nævner tidligere, så skal du bruge parametre. Hvis du bruger parametre, slipper du i hvert fald for fejlen som opstår når der er en ' i teksten. Med hensyn til danske tegn, så er problemet enten at du ikke bruger parametre, eller også er det at du bruger en forkert encoding når du læser fra filen.
Parametre bruger du ved først at angive parametre i sql querien med @ forand parameternavnet.
"INSERT INTO sanistaal (identifikation,varenr,saninr, osv...) values (@identifikation, @varenr, @saninr...)"
og så tilføjer du parametre til SQLiteCommand objektet. SQLcommand.Parameters.Add("@identifikation", Vl(0)) SQLcommand.Parameters.Add("@varenr", Vl(1)) SQLcommand.Parameters.Add("@saninr", Vl(2)) osv...
Hvis dette ikke hjælper på danske tegn, så er det næsten sikkert i indlæsningen af filen der er noget galt. Jeg kan se at du bruger en gammel VB måde at læse filer på, det skader nok ikke at lære "the .NET way" uanset hvad. Men i hvert fald, så kan der opstå problemer, hvis filen er gemt med et karaktersæt, og indlæsningen foregår med et andet karakterset. Med FileOpen funktionen, har du ikke mulighed for at specificere karaktersæt.
Prøv at læse filen på følgende måde: dim reader as new StreamReader(Main.DB1, Encoding.GetEncoding(1252))
Do While reader.Peek() > -1 Vl = reader.ReadLine().Replace("""","").Split(';')
Læg mærke til at jeg tilføjer en Encoding (karaktersæt) i StreamReaderens konstruktør. Karaktersættet med codepage 1252, er det vest-europæiske karaktersæt, som er standard for blandt andet de skandinaviske lande.
Bare spørg hvis du ikke får det til at fungere, eller der er noget jeg skal forklare lidt bedre.
For at teste, hvad pokker det er der sker for alle æ, ø og å'erne, lavede jeg en lille testbase, og puttede nogle danske tegn ind i den. Og ganske rigtigt... i SQLite Manager ser æ,ø og å helt åndsvage ud. Jeg prøvede at åbne samme sqlite fil fra Sqliteman ( http://sqliteman.com/ ), og her ser de danske tegn helt fine ud. Så, det er åbenbart SQLite Manager som har et problem med danske tegn.
Tak for alt den hjælp du har givet. bruger du sqliteman som db admin?
Har også lie et spørgsmål mere hvis du har tid.
Kan denne sægning speedes op, synes ikke jeg får den hastighed jeg gerne vil have. (søgetid 2-4 sek.) Kunne gøres lige så hurtigt i en tekstfil!!
Kan man evt. lave en "prefered tabel" hvor den smider de sidste 1000 søgninger over i, og så kigger der før den kigger i MainDB?
Skal kun bruge et resultat pr. søgning (unik søgning)
Dim SQLconnect As New SQLite.SQLiteConnection() Dim SQLcommand As SQLite.SQLiteCommand SQLconnect.ConnectionString = "Data Source=" & Main.DB2 & ";" SQLconnect.Open() SQLcommand = SQLconnect.CreateCommand
If row = 1 Then SQLcommand.CommandText = "SELECT * FROM sanistaal WHERE saninr='" + UCase(search) + "'" If row = 2 Then SQLcommand.CommandText = "SELECT * FROM sanistaal WHERE direktesogeord='" + UCase(search) + "'"
Dim Vl As SQLite.SQLiteDataReader = SQLcommand.ExecuteReader() While Vl.Read() ' Hent nødvendige data her
Jeg har længe brugt Sqliteman, men er for lidt tid siden gået over til SQLite Administrator, da jeg synes den er lidt mere intuitiv. Jeg har dog aldrig lagt mærke til æ,ø og å problematikken, mærkeligt nok.
prøv at sætte et index på "direktesogeord" kolonnen. Fra SQLite Administrator højreklikker du på kolonnen og trykker "Create Index". Se om det ikke forbedre performance betydeligt.
Ingen problem. Jeg er bare glad for at kunne hjælpe.
Synes godt om
Ny brugerNybegynder
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.