2010-08-19 8 views
13

Para decirlo brevemente. Tengo dos ayudantes simples:¿Cómo se usa SqlCommand para CREAR BASE DE DATOS con el nombre de db parametrizado?

private SqlCommand CreateCommand(string text) 
    { 
     SqlCommand cmd = new SqlCommand(); 
     cmd.Connection = connection; 
     cmd.CommandType = CommandType.Text; 
     cmd.CommandText = text; 
     return cmd; 
    } 

    void SetParameter(SqlCommand cmd, string p, string dbName) 
    { 
     cmd.Parameters.Add(p, SqlDbType.NVarChar); 
     cmd.Parameters[p].Value = dbName; 
    } 

Esto ejecuta OK:

var cmd = CreateCommand("CREATE DATABASE Demo "+ 
      @"ON (FILENAME = N'c:\demo_data.mdf') "+ 
      @"LOG ON (FILENAME = N'c:\demo_data.mdf.LDF') "+ 
      "FOR ATTACH " + 
      "GO"); 
cmd.ExecuteNonQuery(); 

pero esto no significa:

string dataBaseAttachText = "CREATE DATABASE @dbname " + 
           "ON (FILENAME = @filename) " + 
           "LOG ON (FILENAME = @filenamelog) " + 
           "FOR ATTACH GO"; 
var cmd = CreateCommand(dataBaseAttachText); 

SetParameter(cmd, "@dbname", "Demo"); 
SetParameter(cmd, "@filename", @"c:\demo_data.mdf"); 
SetParameter(cmd, "@filenamelog", @"c:\demo_data.mdf.LDF"); 

cmd.ExecuteNonQuery(); 

¿Por qué?

Respuesta

13

Los parámetros son compatibles con las operaciones DML, no con las operaciones DDL, no hay planes de ejecución para las operaciones DDL. tendrá que utilizar SQL dinámico

DDL = Data Definition Language (crear, gota, alterar ....)

LMD Language = manipulación de datos (seleccionar, actualizar, eliminar, insertar)

+0

realmente no es una respuesta a la pregunta, Rich Hildebrand tiene una mejor respuesta. –

9

Solo puede usar parámetros en lugares donde SQL Server los admita. Lamentablemente, SQL Server no admite sentencias de CREATE DATABASE parametrizadas (aunque tengo la sensación de que las partes de nombre de archivo pueden).

Tendrá que construir el SQL sí mismo:

string dataBaseAttachText = "CREATE DATABASE [" + dbName + "] " + 
           "ON (FILENAME = @filename) " + 
           "LOG ON (FILENAME = @filenamelog) " + 
           "FOR ATTACH GO"; 
var cmd = CreateCommand(dataBaseAttachText); 

SetParameter(cmd, "@filename", @"c:\demo_data.mdf"); 
SetParameter(cmd, "@filenamelog", @"c:\demo_data.mdf.LDF"); 

cmd.ExecuteNonQuery(); 

PRECAUCIÓN: este es susceptible a ataques de inyección SQL para caremust ser tomadas; si no confías en el origen del nombre de la base de datos, ¡no hagas esto!

Tendrá que hacer cambios similares en las partes del nombre de archivo si tampoco se pueden parametrizar.

+0

Gracias por su respuesta! –

7

Tristemente puede lograr esto al envolver su operación DDL en una operación DML.

var createDatabaseQuery = "exec ('CREATE DATABASE ' + @databaseName)"; 

var sqlCommand = new SqlCommand(createDatabaseQuery, sqlConnection); 
sqlCommand.Parameters.Add("@databaseName", SqlDbType.Text); 
sqlCommand.Parameters["@databaseName"].Value = "HelloWorld"; 

sqlCommand.ExecuteNonQuery(); 
+0

Si bien parece que este enfoque proporciona protección contra la inyección de SQL (lo que usa parámetros), ¡no es así! –

3

Como una combinación de las respuestas de Daniel y Rich. Mediante la ejecución de una consulta DML a sp_executesql se puede tener una consulta de forma dinámica, utilizando también los QUOTENAME debe escapar a cualquier intento de inyección de SQL alguien puede pasarlo en.

string dataBaseAttachText = @" 
DECLARE @SQLString nvarchar(500); 
DECLARE @ParmDefinition nvarchar(500); 
SET @SQLString = 
    N'CREATE DATABASE ' + QUOTENAME(@dbName) + N' 
     ON (FILENAME = @filename) 
     LOG ON (FILENAME = @filenamelog) 
     FOR ATTACH GO' 
SET ParmDefinition = N'@filename nvarchar(MAX), @filenamelog nvarchar(MAX)' 
EXECUTE sp_executesql @SQLString, @ParmDefinition, @filename = @filename, @filenamelog = @filenamelog"; 

var cmd = CreateCommand(dataBaseAttachText); 

SetParameter(cmd, "@dbname", "Demo"); 
SetParameter(cmd, "@filename", @"c:\demo_data.mdf"); 
SetParameter(cmd, "@filenamelog", @"c:\demo_data.ldf"); 

cmd.ExecuteNonQuery(); 

Esto debería ejecutar la siguiente consulta SQL DML con el buen parámetros pasados

CREATE DATABASE [Demo] 
     ON (FILENAME = @filename) 
     LOG ON (FILENAME = @filenamelog) 
     FOR ATTACH GO 
+0

¿de qué ensamblaje forma parte CreateCommand? Está disponible en .net 45 – user1591131

+0

@ user1591131 CreateComand es una función escrita en la parte superior de esta página en la pregunta. –

0

Resolví este problema al crear un método de extensión para ajustar todas las entidades de forma adecuada.

/// <summary> 
    /// Quotes the provided string in a sql friendly way using the standard [ and ] characters 
    /// </summary> 
    /// <param name="ObjectName">string to quote</param> 
    /// <example> 
    /// "mytable".QuoteSqlName() would return [mytable] 
    /// "my[complex]table".QuoteSqlName() would return [my[[complex]]table] 
    /// </example> 
    /// <returns>quoted string wrapped by quoting characters</returns> 
    /// <remarks>For dynamic sql this may need to be called multiple times, one for each level of encapsulation.</remarks> 
    public static string QuoteSqlName(this string ObjectName) 
    { 
     return ObjectName.QuoteSqlName(']'); 
    } 

    /// <summary> 
    /// Quotes the provided string in a sql friendly way using the provided character 
    /// </summary> 
    /// <param name="ObjectName">string to quote</param> 
    /// <param name="QuoteCharacter">Character to quote with, use [ or ] for standard sql quoting</param> 
    /// <example> 
    /// "mytable".QuoteSqlName() would return [mytable] 
    /// "my[complex]table".QuoteSqlName() would return [my[[complex]]table] 
    /// "justin's computer".QuoteSqlName('\'') would return 'justin''s computer' 
    /// </example> 
    /// <returns>quoted string wrapped by quoting characters</returns> 
    public static string QuoteSqlName(this string ObjectName, char QuoteCharacter) 
    { 
     return ObjectName.QuoteSqlName(QuoteCharacter, false); 
    } 

    /// <summary> 
    /// Quotes the provided string in a sql friendly way using the provided character 
    /// </summary> 
    /// <param name="ObjectName">string to quote</param> 
    /// <param name="QuoteCharacter">Character to quote with, use [ or ] for standard sql quoting</param> 
    /// <param name="IsNvarChar">if true and QuoteCharacter is ' will prefix the quote with N e.g. N'mytable' vs 'mytable'</param> 
    /// <example> 
    /// "mytable".QuoteSqlName() would return [mytable] 
    /// "my[complex]table".QuoteSqlName() would return [my[[complex]]table] 
    /// "justin's computer".QuoteSqlName('\'') would return 'justin''s computer' 
    /// "mytable".QuoteSqlName('\'',false) would reutrn 'mytable' 
    /// "mytable".QuoteSqlName('[',true) would return [mytable] 
    /// "mytable".QuoteSqlName('\'',true) would reutrn N'mytable' 
    /// </example> 
    /// <returns>quoted string wrapped by quoting characters</returns> 
    public static string QuoteSqlName(this string ObjectName, char QuoteCharacter, bool IsNvarChar) 
    { 
     if (string.IsNullOrEmpty(ObjectName)) 
      return ObjectName; 

     char OtherQuoteCharacter = (char)0; 
     bool UseOtherChar = false; 
     if (QuoteCharacter == ']' || QuoteCharacter == '[') 
     { 
      QuoteCharacter = '['; 
      OtherQuoteCharacter = ']'; 
      UseOtherChar = true; 
     } 

     var sb = new StringBuilder((int)(ObjectName.Length * 1.5) + 2); 
     if (QuoteCharacter == '\'' && IsNvarChar) 
      sb.Append('N'); 

     sb.Append(QuoteCharacter); // start with initial quote character 
     for (var i = 0; i < ObjectName.Length; i++) 
     { 
      sb.Append(ObjectName[i]); 
      // if its a quote character, add it again e.g. ] becomes ]] 
      if (ObjectName[i] == QuoteCharacter || UseOtherChar && ObjectName[i] == OtherQuoteCharacter) 
       sb.Append(ObjectName[i]); 
     } 
     sb.Append(UseOtherChar ? OtherQuoteCharacter : QuoteCharacter); // finish with other final quote character 

     return sb.ToString(); 
    } 

Uso:

var QuotedDBName = this.DBName.QuoteSqlName(); 
CreateDBQuery.AppendFormat("USE {0};", QuotedDBName); 
CreateDBQuery.AppendFormat("IF TYPE_ID({0}) IS NULL", DBType.Name.QuoteSqlName('\'', true)); 
CreateDBQuery.AppendFormat(" CREATE TYPE {0} as {1};", DBType.Name.QuoteSqlName(), DBType.Value); 
Cuestiones relacionadas