Vad händer där inne?
Du citerar parameterlistorna för flera överbelastningar av Add
. Dessa är bekvämlighetsmetoder som direkt motsvarar konstruktoröverbelastningar för SqlParameter
klass. De konstruerar i huvudsak parameterobjektet med hjälp av vilken konstruktor som helst som har samma signatur som den bekvämlighetsmetod du anropade, och anropar sedan SqlParameterCollection.Add(SqlParameter)
så här:
SqlParameter foo = new SqlParameter(parameterName, dbType, size);
this.Add(foo);
AddWithValue
är liknande men tar bekvämligheten ännu längre och anger också värdet. Men det infördes faktiskt för att lösa ett ramfel. För att citera MSDN,
Överbelastningen av
Add
som tar astring och ett objekt utfasades på grund av möjlig tvetydighet medSqlParameterCollection.Add
överbelastning som tar enString
och enSqlDbType
uppräkningsvärde där överföring av ett heltal med strängen kan tolkas som antingen parametervärdet eller motsvarandeSqlDbType
värde. AnvändAddWithValue
närhelst du vill lägga till en parameter genom att ange dess namn och värde.
Konstruktorn överlastar för SqlParameter
klass är bara bekvämligheter för att ställa in instansegenskaper. De förkortar koden, med marginell inverkan på prestanda:konstruktören kan kringgå sättermetoder och arbeta direkt på privata medlemmar. Om det är skillnad blir det inte mycket.
Vad ska jag göra?
Observera följande (från MSDN)
För dubbelriktade parametrar och utdataparametrar och returvärden måste du ställa in värdet
Size
. Detta krävs inte för inmatningsparametrar, och om det inte uttryckligen ställts in härleds värdet från den faktiska storleken på den angivna parametern när en parametriserad sats exekveras.
Standardtypen är inmatning. Men om du tillåter att storleken härleds så här och du återvinner parameterobjektet i en slinga (du sa att du var orolig för prestanda) kommer storleken att ställas in av det första värdet och alla efterföljande värden som är längre kommer att vara klippt. Uppenbarligen är detta signifikant endast för värden med variabel längd som strängar.
Om du skickar samma logiska parameter upprepade gånger i en loop rekommenderar jag att du skapar ett SqlParameter-objekt utanför loopen och storleksanpassar det på lämpligt sätt. Att överdimensionera en varchar är ofarligt, så om det är en PITA för att få det exakta maximala, ställ bara in den större än du någonsin förväntar dig att kolonnen ska vara. Eftersom du återvinner objektet istället för att skapa ett nytt för varje iteration, kommer minnesförbrukningen under loopens varaktighet sannolikt att falla även om man blir lite exalterad av överdimensioneringen.
Sanningen att säga, såvida du inte behandlar tusentals samtal, kommer inget av detta att göra någon större skillnad. AddWithValue
skapar ett nytt objekt och kringgår storleksproblemet. Det är kort och gott och lätt att förstå. Om du går igenom tusentals, använd min metod. Om du inte gör det, använd AddWithValue
för att hålla din kod enkel och lätt att underhålla.
2008 var länge sedan
Under åren sedan jag skrev detta har världen förändrats. Det finns nya typer av dejter, och det finns också ett problem som jag inte tänkte på förrän ett problem med dejter nyligen fick mig att tänka på konsekvenserna av en utvidgning.
Utvidgning och avsmalning, för dem som inte känner till termerna, är egenskaperna hos datatypkonverteringar. Om du tilldelar en int till en dubbel, förlorar du ingen precision eftersom dubbel är "bredare". Det är alltid säkert att göra detta, så konverteringen sker automatiskt. Det är därför du kan tilldela en int till en dubbel men om du går åt andra hållet måste du göra en explicit cast - dubbel till int är en minskande omvandling med potentiell förlust av precision.
Detta kan gälla strängar:NVARCHAR är bredare än VARCHAR, så du kan tilldela en VARCHAR till en NVARCHAR men att gå åt andra hållet kräver en cast. Jämförelse fungerar eftersom VARCHAR implicit vidgar till NVARCHAR, men detta kommer att störa användningen av index!
C#-strängar är Unicode, så AddWithValue kommer att producera en NVARCHAR-parameter. I den andra änden breddas VARCHAR-kolumnvärdena till NVARCHAR för jämförelse. Detta stoppar inte exekveringen av frågor men det förhindrar att index används. Det här är dåligt.
Vad kan du göra åt det? Du har två möjliga lösningar.
- Skriv in parametern explicit. Detta innebär inte mer AddWithValue
- Ändra alla strängkolumntyper till NVARCHAR.
Att dike VARCHAR är förmodligen den bästa idén. Det är en enkel förändring med förutsägbara konsekvenser och det förbättrar din lokaliseringsberättelse. Men du kanske inte har detta som ett alternativ.
Nuförtiden gör jag inte mycket direkt ADO.NET. Linq2Sql är nu mitt valvapen, och handlingen att skriva den här uppdateringen har fått mig att undra hur den hanterar detta problem. Jag har en plötslig, brinnande önskan att kontrollera min dataåtkomstkod för uppslagning via VARCHAR-kolumner.
2019 och världen har gått vidare igen
Linq2Sql är inte tillgängligt i dotnet Core, så jag använder mig av Dapper. [N]VARCHAR-problemet är fortfarande en grej men det är inte längre så långt begravt. Jag tror att man också kan använda ADO så det har gått runt i det avseendet.