如何使用SqlCommand使用参数化的db名称创建DATABASE?

简而言之。 我有两个简单的助手:

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; } 

这执行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(); 

但这不是:

 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(); 

为什么?

DML操作支持参数而不支持DDL操作,DDL操作没有执行计划。 您将需要使用动态SQL

DDL =数据定义语言(创建,删除,更改….)

DML =数据操作语言(选择,更新,删除,插入)

您只能在SQL Server支持它们的位置使用参数。 不幸的是,SQL Server不支持参数化的CREATE DATABASE语句(尽管我感觉文件名部分可能支持参数)。

您需要自己构建SQL:

 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(); 

注意:这对SQL注入攻击是可以接受的,因此必须小心谨慎; 如果您不信任数据库名称的来源,请不要这样做!

如果不能对参数化进行参数化,则需要对文件名部分进行类似的更改。

遗憾的是,您可以通过在DML操作中包装DDL操作来实现此目的。

 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(); 

作为丹尼尔和里奇的回答的结合。 通过对sp_executesql运行DML查询,您可以拥有一个动态构建的查询,同样通过使用QUOTENAME它应该转义任何有人可能传入的sql注入尝试。

 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(); 

这应该执行以下DML sql查询并传递适当的参数。

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

我通过创建一个扩展方法来适当地包装所有实体来解决这个问题。

  ///  /// Quotes the provided string in a sql friendly way using the standard [ and ] characters ///  /// string to quote ///  /// "mytable".QuoteSqlName() would return [mytable] /// "my[complex]table".QuoteSqlName() would return [my[[complex]]table] ///  /// quoted string wrapped by quoting characters /// For dynamic sql this may need to be called multiple times, one for each level of encapsulation. public static string QuoteSqlName(this string ObjectName) { return ObjectName.QuoteSqlName(']'); } ///  /// Quotes the provided string in a sql friendly way using the provided character ///  /// string to quote /// Character to quote with, use [ or ] for standard sql quoting ///  /// "mytable".QuoteSqlName() would return [mytable] /// "my[complex]table".QuoteSqlName() would return [my[[complex]]table] /// "justin's computer".QuoteSqlName('\'') would return 'justin''s computer' ///  /// quoted string wrapped by quoting characters public static string QuoteSqlName(this string ObjectName, char QuoteCharacter) { return ObjectName.QuoteSqlName(QuoteCharacter, false); } ///  /// Quotes the provided string in a sql friendly way using the provided character ///  /// string to quote /// Character to quote with, use [ or ] for standard sql quoting /// if true and QuoteCharacter is ' will prefix the quote with N eg N'mytable' vs 'mytable' ///  /// "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' ///  /// quoted string wrapped by quoting characters 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 eg ] 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(); } 

用法:

 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);