Fetstil Fetstil Kursiv Understrykning linje färgläggning tabellverk Punktlista Nummerlista Vänster Centrerat högerställt Utfyllt Länk Bild htmlmode
  • Forum & Blog
    • Forum - översikt
      • .Net
        • asp.net generellt
        • c#
        • vb.net
        • f#
        • silverlight
        • microsoft surface
        • visual studio .net
      • databaser
        • sql-server
        • databaser
        • access
        • mysql
      • mjukvara klient
        • datorer och komponenter
        • nätverk, lan/wan
        • operativsystem
        • programvaror
        • säkerhet, inställningar
        • windows server
        • allmänt
        • crystal reports
        • exchange/outlook
        • microsoft office
      • mjukvara server
        • active directory
        • biztalk
        • exchange
        • linux
        • sharepoint
        • webbservers
        • sql server
      • appar (win/mobil)
      • programspråk
        • c++
        • delphi
        • java
        • quick basic
        • visual basic
      • scripting
        • asp 3.0
        • flash actionscript
        • html css
        • javascript
        • php
        • regular expresssion
        • xml
      • spel och grafik
        • DirectX
        • Spel och grafik
      • ledning
        • Arkitektur
        • Systemutveckling
        • krav och test
        • projektledning
        • ledningsfrågor
      • vb-sektioner
        • activeX
        • windows api
        • elektronik
        • internet
        • komponenter
        • nätverk
        • operativsystem
      • övriga forum
        • arbete karriär
        • erbjuda uppdrag och tjänster
        • juridiska frågor
        • köp och sälj
        • matematik och fysik
        • intern information
        • skrivklåda
        • webb-operatörer
    • Posta inlägg i forumet
    • Chatta med andra
  • Konto
    • Medlemssida
    • Byta lösenord
    • Bli bonsumedlem
    • iMail
  • Material
    • Tips & tricks
    • Artiklar
    • Programarkiv
  • JOBB
  • Student
    • Studentlicenser
  • KONTAKT
    • Om pellesoft
    • Grundare
    • Kontakta oss
    • Annonsering
    • Partners
    • Felanmälan
  • Logga in

Hem / Artiklar / Titel på artikeln

Dataparametrar i ADO.NET

Postad 2004-05-28 av Herbjörn Wilhelmsen i sektionen ASP.NET, C#, Okategoriserat med 1 Kommentarer | Läst av: 9054, Betyg: 81%

Förord

Några programmerare använder Dataparametrar (DataParameter) när de programmerar i .NET, andra gör det inte. Enligt min erfarenhet beror detta i ganska hög grad på att några tycker om dataparametrarna och andra inte tycker om dessa. Det verkar dock som om tyckandet inte baserar sig på så mycket fakta, och därför vill jag försöka bidra med just fakta. Det är fakta som borde ligga till grund för att besvara frågan: Borde man använda dataparametrar i ADO.NET? Förutom att besvara frågan kommer jag att visa exempel på hur man använder dataparametrar.
Innehåll
  » Att använda databaser i .NET
  » Sökvärden
  » Konkatenering av text
  » Datatyp
  » Skydd mot SQL-injection
  » Anrop till lagrade procedurer
  » Prestanda
  » Kan man alltid använda parametrar?
  » Fördelar
  » Nackdelar
  » Rekommendation
  » Resurser


Att använda databaser i .NET

Vi börjar med att titta på hur ett databasanrop med ADO.NET kan se ut. Databasen som används som exempel i artikeln är Northwind, en databas som följer med i SqlServer. Jag har använt SqlServer som exempel i artikeln, men om inget annat anges är resonemangen giltiga också för andra databaser. De tabeller i Nortwind som används är :

  • Customer, med kolumnerna
    CustomerID (datatyp nchar längd 5, primärnyckel)
    CompanyName (datatyp nvarchar längd 40)
    City (datatyp nvarchar längd 15)
  • Products, med kolumnerna
    ProductID (datatyp int, primärnyckel)
    UnitsInStock (datatyp smallint)

Kodraderna nedan visar hur man hämtar data från tabellen Customers:

C#

string connectionString = ConfigurationSettings.AppSettings["ConnectionString"];
string cmdText = "SELECT * FROM Customers";
SqlConnection conn = new SqlConnection(connectionString);
SqlCommand cmd = new SqlCommand(cmdText,conn);
try{
conn.Open();
SqlDataReader reader = cmd.ExecuteReader();
while (reader.Read()) {
Response.Write(reader["CustomerID"] + " " + reader["CompanyName"] + " " + reader["City"]);
}
}
catch (Exception ex) {
//hantera felet
Response.Write (ex.Message);
}
finally {
if (conn.State == ConnectionState.Open) {
conn.Close();
}
}


VB.NET

Dim connectionString As String = ConfigurationSettings.AppSettings("ConnectionString")
Dim cmdText As String = "SELECT * FROM Customers"
Dim conn As SqlConnection = New SqlConnection(connectionString)
Dim cmd As SqlCommand = New SqlCommand(cmdText, conn)
Try
conn.Open()
Dim reader As SqlDataReader = cmd.ExecuteReader()
While reader.Read()
Response.Write(reader("CustomerID") & " " & reader("CompanyName") & " " & reader("City"))
End While
Catch ex As Exception
'hantera felet
Response.Write(ex.Message)
Finally
If conn.State = ConnectionState.Open Then
conn.Close()
End If
End Try


I mitt fall fick jag ut 91 rader med raden

ID=ALFKI, Name=Alfreds Futterkiste, City=Berlin

som första rad.

Några kommentarer till koden ovan: Det är en mycket bra rekommendation att alltid köra sina databasanrop inom try och catch eftersom databasanrop kan gå fel. Ytterligare en rekommendation är att se till att databaskopplingen alltid stängs ner när jobbet är gjort, dvs i finally-blocket, eftersom öppna databaskopplingar belastar servern i onödan.
I fallet ovan används ExecuteReader för att få en DataReader (SqlReader), men artikeln gäller alla former av databasanrop.



Sökvärden

I den verkliga världen så vill vi oftast välja ut vilken/vilka rader vi skall hämta ut från våra databastabeller. I vårt exempel kanske vi är intresserade av att välja ut endast de rader i Customers som finns i en viss stad eller har ett visst företagsnamn. Om vi skall kunna välja olika rader från tabellen beroende på användarens val måste vi tala om detta i vårt databasanrop. Om man har textboxar som tillåter inmatning av ort och företagsnamn skulle koden som konstruerar vår command text kunna se ut så här:

C#

string cmdText = "SELECT * FROM Customers WHERE city='" +
txtCity.Text + "' OR CompanyName='" +
txtCompanyName.Text + "'";


VB.NET

Dim cmdText As String = "SELECT * FROM Customers WHERE city='" & _
txtCity.Text & "' OR CompanyName='" & _
txtCompanyName.Text & "'"


Lägg märke till att vi måste ha en ' innan texten och efter texten som hämtas från textboxarna. Detta behövs eftersom en text i SQL skrivs på detta viset: 'en text'. Om jag skriver in ”Alfreds Futterkiste” och ”Berlin” i textboxarna får jag 1 rad tillbaka, samma rad som den första raden i förra frågan.

Nu kan det emellertid uppstå problem. Vad om jag istället skrev in ”Alfred's Futterkiste” i den första textboxen? Då genereras ett exception när frågan körs i databasen. Felmeddelandet blir:

Line 1: Incorrect syntax near 's'.

Detta fel får jag eftersom tecknet ' i en text (t.ex. nvarchar) i SQL betyder att texten avslutas. För att command-texten skall bli korrekt måste jag se till att tecknet ' tolkas som ett tecken och inte avslutar texten. Sättet att göra detta på i SQL är att ange två ' istället för en '. Detta kan man åstadkomma genom att göra en replace enligt följande:

C#

txtCity.Text.Replace("'","''"); //byt ut varje ' mot två stycken '


VB.NET

txtCity.Text.Replace("'","''") 'byt ut varje ' mot två stycken '


Detta måste göras på alla sökvärden som innehåller text, och därför även potentiellt tecknet '. Då blir alltså koden som bygger vår command text så här:

C#

string cmdText = "SELECT * FROM Customers WHERE city='" +
txtCity.Text.Replace("'","''") + "' OR CompanyName='" +
txtCompanyName.Text.Replace("'","''") + "'";


VB.NET

Dim cmdText As String = "SELECT * FROM Customers WHERE city='" & _
txtCity.Text.Replace("'", "''") & "' OR CompanyName='" & _
txtCompanyName.Text.Replace("'", "''") & "'"


I dataparametrar sköts båda dessa sakerna (' runt texter och ersättning av ' till två ' inuti texter) per automatik. Syntaxen blir då lite annorlunda för command-texten och vi behöver dessutom lägga till parametrar till vårt command-objekt enligt följande:

C#

string cmdText = "SELECT * FROM Customers WHERE city=@City OR CompanyName=@CompanyName";
SqlConnection conn = new SqlConnection(connectionString);
SqlCommand cmd = new SqlCommand(cmdText,conn);
cmd.Parameters.Add("@City",txtCity.Text);
cmd.Parameters.Add("@CompanyName",txtCompanyName.Text);


VB.NET

Dim cmdText As String = "SELECT * FROM Customers WHERE city=@City OR CompanyName=@CompanyName"
Dim conn As SqlConnection = New SqlConnection(connectionString)
Dim cmd As SqlCommand = New SqlCommand(cmdText, conn)
cmd.Parameters.Add("@City", txtCity.Text)
cmd.Parameters.Add("@CompanyName", txtCompanyName.Text)




Konkatenering av text

Att konkatenera text (lägga ihop strängar till en större sträng) är en tidsödande operation i .NET. Detta beror på att varje sträng är ett eget objekt och att strängarna i sig inte kan ändras. Med andra ord, varje gång du vill lägga till något i en sträng skapas en ny stäng.
I exemplet ovan måste alltså hela 5 string-objekt skapas, en för varje del som läggs ihop och en för resultatet av konkateneringen .



Datatyp

En viktig del i sökparametrar är att ta hand om datatyp. Vissa sökvärden skall ha datatypen nvarchar/varchar, andra datatypen Date eller int eller float eller något annat. Detta kan man kontrollera enkelt innan databasanropet om man använder sig av dataparametrar.

C#

string cmdText = "SELECT UnitsInStock FROM Products WHERE ProductID=@ProductID";
SqlConnection conn = new SqlConnection(connectionString);
SqlCommand cmd = new SqlCommand(cmdText,conn);
cmd.Parameters.Add("@ProductID",SqlDbType.Int).Value = txtProductID.Text;


VB.NET

Dim cmdText As String = "SELECT UnitsInStock FROM Products WHERE ProductID=@ProductID"
Dim conn As SqlConnection = New SqlConnection(connectionString)
Dim cmd As SqlCommand = New SqlCommand(cmdText, conn)
cmd.Parameters.Add("@ProductID", SqlDbType.Int).Value = txtProductID.Text


Detta gör att man kollar innan databasanropet att datatypen är korrekt. Om datatypen inte är korrekt så behövs inget anrop till databasen som ändå (förmodligen) kommer resultera i ett exception från databasen. Om datatypen är fel när vi använder dataparametrar genereras ett exception innan databasen kontaktas. Vi har alltså sparat tid och belastat databasen mindre.

Om man inte anger datatyp när parametern skapas väljs datatyp beroende på det värdet som skickas med till parametern. Om stringen ”Alfreds Futterkiste” skickas med får alltså parametern datatypen string som i databasen motsvaras av varchar/nvarchar.


Skydd mot SQL-injection

SQL-injection handlar om att en hacker försöker få din kod att göra oväntade saker. Genom att skriva in SQL i textboxen där företagsnamnet borde skrivas in kan en hacker potentiellt åstadkomma stor skada. Här vill jag inte ge några detaljrika exempel utan bara konstatera att en hacker skulle kunna påverka data och/eller strukturen i din databas med hjälp av skicklighet och lite tur. Om du inte skyddat din applikation mot SQL-injection vill säga! När man försöker skriva in SQL i en textbox gör man oftast ett tecken som avbryter sqlkommandot. Detta är oftast en ' eftersom detta kommenterar bort resten av raden. För att skydda sig mot detta kan man byta ut varje ' mot två ', som vi gjorde tidigare. I SqlServer får man ytterligare skydd mot detta om man använder dataparametrar. Detta beror på att en command text (dvs sql som inte är ett anrop till en lagrad procedur) med parametrar körs genom att lämna dels en command text samt parametrarna separat till en system procedure i SqlServer som heter sp_executesql. I lagrade procedurer i SqlServer behandlas parametrar endast som parametrar, om parametern innehåller Sql så körs detta inte som sql. Undantag till denna regel finns, men inte när det gäller sp_executesql. Även andra databaser än SqlServer kan ha liknande skydd.


Anrop till lagrade procedurer

Anrop till lagrade procedurer måste i .NET göras med parametrar, om man har några sökvärden vill säga. Om man anropar en lagrad procedur så sätter man sin command text och dessutom command type. Detta gör att man inte kan lämna med värden i sin command text. Om man t.ex. skriver in ”GetData 10”, dvs vill anropa en lagrad procedur som heter ”GetData” med 10 som värde till den 1:a parametern, så kommer detta inte att fungera. Då letar istället databasen efter en procedur som heter ”GetData 10” som den säkerligen inte kommer hitta. Det enda sättet att skicka med värden till en lagrad procedur är alltså via parametrar. Om man utvecklar utan lagrade procedurer, men kan komma att i ett senare skede använda lagrade procedurer är det alltså en fördel att direkt använda parametrar. Detta gör att övergången till lagrade procedurer medför mindre ändringar i koden.

För att anropa en lagrad procedur måste du också ändra på command-objektets comand type enligt följande:

c#

cmd.CommandType = CommandType.StoredProcedure;


VB.NET

cmd.CommandType = CommandType.StoredProcedure



Prestanda

Prestanda är alltid något man vill förbättra. Jag gjorde ett enkelt test för att se vad som gick snabbast av att konkatenera ihop en select och att använda parametrar. Jag kollade tiderna från det att selectsatsen började byggas till dess att frågan hade körts i databasen. Med den enkla frågan som användes ovan när vi använde två sökvärden (för City och CompanyName) så gick det mer än dubbelt så fort att använda parametrar!


Kan man alltid använda parametrar?

Det finns fall där det är lite jobbigare att använda paramtetrar:

”SELECT * FROM Products WHERE ProductID IN (1,2,3,4,5)”

Om man vill åstadkomma detta med parametrar kan man givetvis skriva

”SELECT * FROM Products WHERE ProductID IN (@P1,@P2,@P3,@P4,@P5)”

Men att ta hand om fallet när vi vill tillåta många värden i en och samma parameter fungerar inte utan vidare. Nedan en kod som man skulle kunna tänka sig att skriva om man låter en användare skriva in en kommaseparerad lista på produktnummer i en textbox.

C#

string cmdText = "SELECT UnitsInStock FROM Products WHERE ProductID IN (@ProductIDs)";
SqlConnection conn = new SqlConnection(connectionString);
SqlCommand cmd = new SqlCommand(cmdText,conn);
cmd.Parameters.Add("@ProductIDs",txtProductIDs.Text); //fungerar inte för mer än ett värde


VB.NET

Dim cmdText As String = "SELECT UnitsInStock FROM Products WHERE ProductID IN (@ProductIDs)"
Dim conn As SqlConnection = New SqlConnection(connectionString)
Dim cmd As SqlCommand = New SqlCommand(cmdText, conn)
cmd.Parameters.Add("@ProductIDs", txtProductIDs.Text) 'fungerar inte för mer än ett värde


Det finns minst två lösningar på detta problem. Det ena är att man i databasen kan parsa textsträngen med flera värden, plocka ut dessa och lägga dem i en tabell (hellst en tabell-variabel alternativt temptabell) och sedan använda en selectsats mot tabellen. Ett annat sätt att lösa detta är att i SqlServer skicka en parameter med datatypen XML till databasen och sedan använda XQuery till att hämta ut värden från XMLen. Så här kan det se ut om man väljer den senare lösningen:

C#

string cmdText = "SELECT UnitsInStock FROM Products WHERE ProductID IN (SELECT products.value('id[1]','int') FROM @ProductIDs.nodes('//product') as R(products))";
SqlConnection conn = new SqlConnection(connectionString);
SqlCommand cmd = new SqlCommand(cmdText,conn);
SqlParameter param = new SqlParameter("@ProductIDs",SqlDbType.Xml);
param.Value = "225";
cmd.Parameters.Add(param);


VB.NET

Dim cmdText As String = "SELECT UnitsInStock FROM Products WHERE ProductID IN (SELECT products.value('id[1]','int') FROM @ProductIDs.nodes('//product') as R(products))"
Dim conn As SqlConnection = New SqlConnection(connectionString)
Dim cmd As SqlCommand = New SqlCommand(cmdText, conn)
Dim param as SqlParameter = new SqlParameter("@ProductIDs",SqlDbType.Xml)
param.Value = "225"
cmd.Parameters.Add(param)




Fördelar

Dataparametrar har alltså följande fördelar:

  • Tar hand om ' i och runt varchar,datum och andra datatyper som behöver detta
  • Verifierar att datatypen är korrekt
  • Eliminerar resurskrävande konkatenering
  • Skyddar mot Sql injection
  • Måste användas när värden skall skickas till lagrade procedurer
  • Ökar prestandan

Det viktiga här är inte bara att vi inte behöver komma ihåg att skriva kod för att ta hand om detta. Koden som körs för att ta hand om dessa sakerna är inte bara skriven den är testad och optimerad.


Nackdelar

Ja, jag ser inte direkt några stora nackdelar med dataparametrar. Några skulle kanske uppleva det som en nackdel att man måste lära sig att använda parametrarna eller att man genom att konkatenera alla värden direkt kan skriva koden på färre antal rader. Dessa nackdelarna får man leva med. Personligen tycker jag att fördelarna väger mycket tyngre än nackdelarna (åtminstone de nackdelar jag har kommit på).


Rekommendation

Min rekommendation är väl inte så svårt att gissa sig till vid det här laget? Använd i så stor utsträckning som möjligt dataparametrar när värden skall skickas med vid databasanrop.


Resurser

Det finns lite olika syntaxer för att använda databasparametrar beroende på vilken .NET DataProvider man använder sig av. I vissa fall får man använda ? istället för parameternamn och sedan lägga in parametrarna i rätt ordning. Nedan finns länkar som visar exempel på syntax för de dataproviders som ingår i .NET Framework 1.1.


  • Databasparametrar för Odbc

  • Databasparametrar för OleDb

  • Databasparametrar för Oracle

  • Databasparametrar för SqlServer

  • Databasparametrar för SqlServer CE



Lycka till!
Herbjörn
Upp

1 Kommentarer


  1. Stefan Norinder
    8 jan 2006

    Det är den andra artikeln av Herbjörn jag läser. Båda har varit väldigt bra. Intressant innehåll beskrivet så att en nybörjare kan hänga med.

Skriv en kommentar på artikeln

Ditt betyg på artikeln



Kommentar:





Nyligen

  • 09:09 Vill du köpa medicinska tester?
  • 12:47 Vem beviljar assistansen – kommune
  • 14:17 Någon med erfarenhet av hemstädnin
  • 14:14 Bör man använda sig av en båtförme
  • 14:12 Finns det någon intressant hundblo
  • 14:25 Tips på verktyg för att skapa QR-k
  • 14:23 Tips på verktyg för att skapa QR-k
  • 20:52 Fungerer innskuddsbonuser egentlig

Sidor

  • Hem
  • Bli bonusmedlem
  • Läs artiklar
  • Chatta med andra
  • Sök och erbjud jobb
  • Kontakta oss
  • Studentlicenser
  • Skriv en artikel

Statistik

Antal besökare:
Antal medlemmar:
Antal inlägg:
Online:
På chatten:
4 569 153
27 952
271 704
891
0

Kontakta oss

Frågor runt konsultation, rådgivning, uppdrag, rekrytering, annonsering och övriga ärenden. Ring: 0730-88 22 24 | pelle@pellesoft.se

© 1986-2013 PelleSoft AB. Last Build 4.1.7169.18070 (2019-08-18 10:02:21) 4.0.30319.42000
  • Om
  • Kontakta
  • Regler
  • Cookies