Как использовать SqlCommand для создания базы данных с параметризованным именем БД? - PullRequest
14 голосов
/ 19 августа 2010

Короче говоря. У меня есть два простых помощника:

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

Почему?

Ответы [ 5 ]

18 голосов
/ 19 августа 2010

Параметры поддерживаются для операций DML, а не для операций DDL, планов выполнения для операций DDL нет.вам нужно будет использовать динамический SQL

DDL = язык определения данных (создать, удалить, изменить ....)

DML = язык управления данными (выбрать, обновить, удалить, вставить)

9 голосов
/ 19 августа 2010

Вы можете использовать параметры только там, где их поддерживает 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-инъекций, поэтому необходимо соблюдать осторожность; если вы не доверяете источнику имени базы данных, не делайте этого!

Вам потребуется внести аналогичные изменения в части имени файла, если они не могут быть параметризованы.

7 голосов
/ 10 сентября 2014

К сожалению, вы можете сделать это, поместив свою операцию 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();
3 голосов
/ 10 сентября 2014

Как сочетание ответа Даниэля и Рича. Запустив 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
0 голосов
/ 20 октября 2017

Я решил эту проблему, создав метод расширения для надлежащей упаковки всех объектов.

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

Использование:

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);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...