如何使用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);