Как использовать SqlCommand для CREATE DATABASE с параметризованным именем db?
Короче говоря. У меня есть два простых помощника:
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;
}
Выполняется ОК:
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();
Почему?
Ответы
Ответ 1
Поддерживаются параметры для операций DML, а не DDL-операций, для выполнения DDL-операций нет планов выполнения. вам нужно будет использовать динамический SQL
DDL = язык определения данных (создание, падение, изменение....)
DML = Язык манипулирования данными (выберите, обновите, удалите, вставьте)
Ответ 2
Вы можете использовать только параметры в тех местах, где их поддерживает 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-инъекций, поэтому следует принять меры предосторожности; если вы не доверяете источнику имени базы данных, не делайте этого!
Вам нужно будет внести аналогичные изменения в части имени файла, если они не могут быть параметризованы.
Ответ 3
К сожалению, вы можете выполнить это, завернув операцию DDL в операции 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();
Ответ 4
Как немного сочетания ответа Даниэля и Бога. Запустив запрос DML в sp_executesql
, вы можете создать динамически построенный запрос, также используя 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();
Это должно выполнить следующий SQL-запрос DML с соответствующими переданными параметрами.
CREATE DATABASE [Demo]
ON (FILENAME = @filename)
LOG ON (FILENAME = @filenamelog)
FOR ATTACH GO
Ответ 5
Я решил эту проблему, создав метод расширения для надлежащего переноса всех объектов.
/// <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 computer".QuoteSqlName('\'') would return 'justin' 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 computer".QuoteSqlName('\'') would return 'justin' 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();
}
Использование:
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);