O que está acontecendo lá dentro?
Você cita as listas de parâmetros para várias sobrecargas de
Add
. Esses são métodos de conveniência que correspondem diretamente a sobrecargas de construtor para o SqlParameter
aula. Eles essencialmente constroem o objeto de parâmetro usando qualquer construtor que tenha a mesma assinatura que o método de conveniência que você chamou e, em seguida, chamam SqlParameterCollection.Add(SqlParameter)
assim:SqlParameter foo = new SqlParameter(parameterName, dbType, size);
this.Add(foo);
AddWithValue
é semelhante, mas leva a conveniência ainda mais longe, também definindo o valor. No entanto, ele foi realmente introduzido para resolver uma falha da estrutura. Para citar o MSDN,
A sobrecarga deAdd
que recebe uma string e um objeto foi descontinuado devido a uma possível ambiguidade com oSqlParameterCollection.Add
sobrecarga que recebe umaString
e umSqlDbType
valor de enumeração em que passar um inteiro com a string pode ser interpretado como sendo o valor do parâmetro ou oSqlDbType
correspondente valor. UseAddWithValue
sempre que desejar adicionar um parâmetro especificando seu nome e valor.
O construtor sobrecarrega para o
SqlParameter
class são meras conveniências para definir propriedades de instância. Eles encurtam o código, com impacto marginal no desempenho:o construtor pode ignorar métodos setter e operar diretamente em membros privados. Se houver diferença, não será muita. O que devo fazer?
Observe o seguinte (do MSDN)
Para parâmetros bidirecionais e de saída e valores de retorno, você deve definir o valor deSize
. Isso não é necessário para parâmetros de entrada e, se não for definido explicitamente, o valor é inferido do tamanho real do parâmetro especificado quando uma instrução parametrizada é executada.
O tipo padrão é entrada. No entanto, se você permitir que o tamanho seja inferido assim e reciclar o objeto de parâmetro em um loop (você disse que estava preocupado com o desempenho), o tamanho será definido pelo primeiro valor e quaisquer valores subsequentes que forem mais longos serão cortado. Obviamente, isso é significativo apenas para valores de comprimento variável, como strings.
Se você estiver passando o mesmo parâmetro lógico repetidamente em um loop, recomendo criar um objeto SqlParameter fora do loop e dimensioná-lo adequadamente. O superdimensionamento de um varchar é inofensivo, portanto, se for um PITA para obter o máximo exato, basta configurá-lo maior do que você espera que a coluna seja. Como você está reciclando o objeto em vez de criar um novo para cada iteração, o consumo de memória durante a duração do loop provavelmente cairá mesmo se você ficar um pouco animado com o superdimensionamento.
Verdade seja dita, a menos que você processe milhares de chamadas, nada disso fará muita diferença.
AddWithValue
cria um novo objeto, evitando o problema de dimensionamento. É curto e doce e fácil de entender. Se você percorrer milhares, use minha abordagem. Caso contrário, use AddWithValue
para manter seu código simples e fácil de manter. 2008 foi há muito tempo
Nos anos desde que escrevi isso, o mundo mudou. Existem novos tipos de datas, e também há um problema que não me passou pela cabeça até que um problema recente com datas me fez pensar nas implicações da ampliação.
Ampliação e estreitamento, para aqueles que não estão familiarizados com os termos, são qualidades das conversões de tipo de dados. Se você atribuir um int a um double, não haverá perda de precisão porque double é "mais amplo". É sempre seguro fazer isso, então a conversão é automática. É por isso que você pode atribuir um int a um double, mas indo para o outro lado, você precisa fazer uma conversão explícita - double to int é uma conversão estreita com potencial perda de precisão.
Isso pode se aplicar a strings:NVARCHAR é mais largo que VARCHAR, então você pode atribuir um VARCHAR a um NVARCHAR, mas ir para o outro lado requer uma conversão. A comparação funciona porque o VARCHAR se amplia implicitamente para NVARCHAR, mas isso interferirá no uso de índices!
As cadeias de caracteres C# são Unicode, portanto, AddWithValue produzirá um parâmetro NVARCHAR. Na outra extremidade, os valores da coluna VARCHAR são ampliados para NVARCHAR para comparação. Isso não interrompe a execução da consulta, mas impede que os índices sejam usados. Isto é mau.
O que você pode fazer sobre isso? Você tem duas soluções possíveis.
- Digite explicitamente o parâmetro. Isso significa que não há mais AddWithValue
- Altere todos os tipos de coluna de string para NVARCHAR.
Abandonar o VARCHAR é provavelmente a melhor ideia. É uma mudança simples com consequências previsíveis e melhora sua história de localização. No entanto, você pode não ter isso como uma opção.
Hoje em dia eu não faço muito ADO.NET direto. O Linq2Sql agora é minha arma de escolha, e o ato de escrever esta atualização me deixou imaginando como ele lida com esse problema. Eu tenho um desejo repentino e ardente de verificar meu código de acesso a dados para pesquisa por meio de colunas VARCHAR.
2019 e o mundo mudou de novo
Linq2Sql não está disponível no dotnet Core, então estou usando o Dapper. O problema do [N]VARCHAR ainda existe, mas não está mais tão enterrado. Acredito que também se pode usar o ADO para que as coisas tenham um círculo completo a esse respeito.