2010-03-15 9 views
32

cuando estaba leyendo libros para aprender C# (podría ser unos viejos libros Visual Studio 2005) que he encontrado consejos para usar siempre SqlCommand.Prepare cada vez que ejecuto llamada SQL (si su un SELECT/o UPDATEINSERT en SQL SERVER 2005/2008) y Le paso parámetros. ¿Pero es realmente tan?Pros y contras de usar SqlCommand Prepare in C#?

  1. ¿Se debe hacer siempre? ¿O solo algunas veces?

  2. ¿Importa si se pasa un parámetro o cinco o veinte?

  3. ¿Qué impulso debería dar si hay alguno? ¿Sería notable en absoluto (he estado usando SqlCommand.Prepare aquí y me salté allí y nunca tuve ningún problema o diferencias notables).

Por el bien de la pregunta, este es mi código habitual que uso, pero esto es más una pregunta general.

public static decimal pobierzBenchmarkKolejny(string varPortfelID, DateTime data, decimal varBenchmarkPoprzedni, decimal varStopaOdniesienia) { 
    const string preparedCommand = @"SELECT [dbo].[ufn_BenchmarkKolejny](@varPortfelID, @data, @varBenchmarkPoprzedni, @varStopaOdniesienia) AS 'Benchmark'"; 
    using (var varConnection = Locale.sqlConnectOneTime(Locale.sqlDataConnectionDetailsDZP)) //if (varConnection != null) { 
    using (var sqlQuery = new SqlCommand(preparedCommand, varConnection)) { 
     sqlQuery.Prepare(); 
     sqlQuery.Parameters.AddWithValue("@varPortfelID", varPortfelID); 
     sqlQuery.Parameters.AddWithValue("@varStopaOdniesienia", varStopaOdniesienia); 
     sqlQuery.Parameters.AddWithValue("@data", data); 
     sqlQuery.Parameters.AddWithValue("@varBenchmarkPoprzedni", varBenchmarkPoprzedni); 
     using (var sqlQueryResult = sqlQuery.ExecuteReader()) 
      if (sqlQueryResult != null) { 
       while (sqlQueryResult.Read()) { 

       } 
      } 
    } 
} 

aclaración adicional:

si muevo sqlQuery.Prepare() como en código de abajo excepción se produce que el tamaño tiene que ser declarado explícitamente, que básicamente me lleva a pensar que el tener sqlQuery.Prepare() como primera hace que sea inútil? ¿Alguien puede mostrar el uso correcto usando mi ejemplo?

public static decimal pobierzBenchmarkKolejny(string varPortfelID, DateTime data, decimal varBenchmarkPoprzedni, decimal varStopaOdniesienia) { 
    const string preparedCommand = @"SELECT [dbo].[ufn_BenchmarkKolejny](@varPortfelID, @data, @varBenchmarkPoprzedni, @varStopaOdniesienia) AS 'Benchmark'"; 
    using (var varConnection = Locale.sqlConnectOneTime(Locale.sqlDataConnectionDetailsDZP)) //if (varConnection != null) { 
    using (var sqlQuery = new SqlCommand(preparedCommand, varConnection)) { 

     sqlQuery.Parameters.AddWithValue("@varPortfelID", varPortfelID); 
     sqlQuery.Parameters.AddWithValue("@varStopaOdniesienia", varStopaOdniesienia); 
     sqlQuery.Parameters.AddWithValue("@data", data); 
     sqlQuery.Parameters.AddWithValue("@varBenchmarkPoprzedni", varBenchmarkPoprzedni); 
     sqlQuery.Prepare(); 
     using (var sqlQueryResult = sqlQuery.ExecuteReader()) 
      if (sqlQueryResult != null) { 
       while (sqlQueryResult.Read()) { 

       } 
      } 
    } 
} 

¿Cómo haré eso? Al agregar .size al lado de los parámetros y hacer varPortfel.Lenght si es una cadena, etc.

Respuesta

10

de la documentación de MSDN:.

"Antes de llamar Preparar, especificar el tipo de datos de cada parámetro de la sentencia que estar preparado para cada parámetro que tiene una longitud variable tipo de datos, debe establecer la propiedad tamaño al tamaño máximo necesario. Prepare devuelve un error si no se cumplen estas condiciones .

Si se llama a un método Ejecutar después llamando Preparar, cualquier valor del parámetro que es mayor que el valor especificado por la propiedad tamaño es automáticamente truncado con el tamaño original especificado del parámetro , y no hay errores de truncamiento se devuelven.

Los parámetros de salida (ya estén preparados o no) deben tener un tipo de datos especificado por el usuario. Si especifica una variable longitud tipo de datos, también debe especificar el tamaño máximo . "

Por otra parte," Si el propiedad CommandType se establece en TableDirect, Preparar no hace nada. Si CommandType está establecido en StoredProcedure, la llamada a Prepare debería tener éxito, ..."

Esto en general se utiliza para asegurarse de que el usuario final no está utilizando una técnica de inyección SQL para añadir o eliminar información que no quieren demasiado de la base de datos.

miré en él y mira este artículo http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.prepare.aspx. Tu problema es que necesitas definir tus parámetros antes de ejecutar .Prepare() y luego establecer tus parámetros después de ejecutar .Prepare(). Ahora mismo estás haciendo las dos cosas anteriormente. Intentaría algo como esto (Tenga en cuenta que no lo probé, por lo que mi sintaxis podría estar un poco fuera).

public static decimal pobierzBenchmarkKolejny(string varPortfelID, DateTime data, decimal varBenchmarkPoprzedni, decimal varStopaOdniesienia) { 
    const string preparedCommand = @"SELECT [dbo].[ufn_BenchmarkKolejny](@varPortfelID, @data, @varBenchmarkPoprzedni, @varStopaOdniesienia) AS 'Benchmark'"; 
    using (var varConnection = Locale.sqlConnectOneTime(Locale.sqlDataConnectionDetailsDZP)) //if (varConnection != null) { 
    using (var sqlQuery = new SqlCommand(preparedCommand, varConnection)) { 

     sqlQuery.Parameters.Add("@varPortfelID"); 
     sqlQuery.Parameters.Add("@varStopaOdniesienia"); 
     sqlQuery.Parameters.Add("@data"); 
     sqlQuery.Parameters.Add("@varBenchmarkPoprzedni"); 

     sqlQuery.Prepare(); 
     sqlQuery.ExecuteNonQuery();//This might need to be ExecuteReader() 

     sqlQuery.Parameters[0].Value = varPortfelID; 
     sqlQuery.Parameters[1].Value = varStopaOdniesienia; 
     sqlQuery.Parameters[2].Value = data; 
     sqlQuery.Parameters[3].Value = varBenchmarkPoprzedni; 

     using (var sqlQueryResult = sqlQuery.ExecuteReader()) 
      if (sqlQueryResult != null) { 
       while (sqlQueryResult.Read()) { 

       } 
      } 
    } 
} 
+0

¿Qué pasa con el rendimiento y otras cosas? ¿Solo para evitar la inyección de SQL? He leído la documentación de MSDN antes de hacer esta pregunta y la parte citada no es realmente útil con respecto a mis preguntas para usarla o no y si tiene algún beneficio de rendimiento que parece sugerirse en algunos sitios web. – MadBoy

+0

Según tengo entendido, al definir todo, puede almacenar en caché y reutilizar el plan de consulta. Supongo que no te da una cantidad extrema de beneficio. –

+0

¿El uso que tengo en el código es correcto? Intenté mover Preparar a después de configurar todos los Parámetros y lanzar el error. Entonces, ¿sqlQuery.Prepare siempre debe usarse antes de los parámetros o? – MadBoy

6

La otra ventaja es que al hacer esto, el plan de consulta SQL se compila, almacena en caché y reutilizó. Esto no es un gran problema si se trata de un bajo volumen de llamadas a su consulta, pero si tiene mucho, existen algunas ventajas significativas de rendimiento al hacerlo.

+3

¿Puede agregar algún enlace de soporte? –

+0

El plan se reutiliza de todos modos debido a los SqlParameters utilizados con el objeto SqlCommand, porque .net ajustará la instrucción sql en un procedimiento sp_executesql que generará un plan reutilizable. – Magier

4

Según mi propia experiencia: el aumento del rendimiento es MUY significativo. Hace algún tiempo trabajé en un proyecto en el que utilizamos nuestro propio mapeo relacional de objetos. Utilizamos una gran base de datos como un almacén persistente de modelos de objetos complejos, con carga de objetos bajo demanda y vida útil de objetos de referencia débil.

El uso de comandos preparados fue crucial para el éxito de esa aplicación, ya que solo hizo que el sistema fuera realmente utilizable.

En otras palabras: si ejecuta muchos comandos SQL, que son exactamente los mismos o solo difieren en los valores de los parámetros, verá un gran impulso en el rendimiento.

No tengo números exactos ni enlaces, pero puedo dar fe de mi propia experiencia.

+1

Creo sinceramente lo que dices, pero @OP: considera también lo contrario. Dependiendo de que el DB tenga las declaraciones preparadas, puede dar un mal momento al analizador de consultas y elegir los índices incorrectos. Vimos eso especialmente en DB2 con columnas con distribución de valores bastante desigual. Así como con cada consejo de rendimiento: debe medirlo para su escenario –

0

Muchas respuestas de Stackoverflow afirman que usar el método prepare mejoraría el rendimiento cuando se reutilizara una consulta varias veces, por lo que implementé un punto de referencia para evaluar la ganancia de rendimiento. El resultado: ¡no hubo ninguna mejora en absoluto!

decidí investigar para asegurarse de que no faltaba nada en mi punto de referencia y me encontré con este comentario en el IDbCommand.Prepare Method documentation:

El servidor almacena automáticamente los planes para su reutilización cuando sea necesario; por lo tanto, no hay necesidad de llamar a este método directamente en su aplicación cliente .

También encontré este Databases Administrators answer que le dan una gran cantidad de detalles sobre el método prepare y por qué no he tenido ninguna mejora significativa.

Acerca de la inyección de SQL, estará protegido contra esto porque ha utilizado un comando sqlCommand parametrizado ... no porque haya llamado al método prepare.