如何使用 SqlCommand 创建带有参数化数据库名称的数据库?

发布于 2024-09-15 02:45:35 字数 1240 浏览 6 评论 0原文

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

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

为什么?

To put it short. I've got two simple helpers:

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

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

But this doesn't:

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

Why?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

流年已逝 2024-09-22 02:45:35

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

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

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

Parameters are supported for DML operations not DDL operations, there are no execution plans for DDL operations. you will need to use dynamic SQL

DDL = Data Definition Language (create, drop, alter....)

DML = Data Manipulation Language (select, update, delete, insert)

你与昨日 2024-09-22 02:45:35

遗憾的是,您可以通过将 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();

Sadly you can accomplish this by wrapping your DDL operation in a DML operation.

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();
卸妝后依然美 2024-09-22 02:45:35

您只能在 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 注入攻击,因此必须小心;如果您不信任数据库名称的来源,请不要这样做!

如果文件名部分也无法参数化,则需要对这些部分进行类似的更改。

You can only use parameters in places where SQL Server supports them. Unfortunately SQL Server does not support parameterised CREATE DATABASE statements (although I have a feeling the filename parts may support parameters).

You'll need to construct the SQL yourself:

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

CAUTION: this is susceptable to SQL-injection attacks so caremust be taken; if you don't trust the source of the database name, don't do this!

You'll need to make similar changes to the filename parts if those can't be parameterised either.

秋日私语 2024-09-22 02:45:35

作为丹尼尔和里奇答案的结合。通过运行 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(); 

这应该使用传递的正确参数执行以下 DML sql 查询。

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

As a bit of a combination of both Daniel's and Rich's answer. By running a DML query to sp_executesql you can have a dynamically built query, also by using QUOTENAME it should escape any attempts at sql injection someone may pass in.

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

This should execute the following DML sql query with the proper parameters passed.

CREATE DATABASE [Demo]
       ON (FILENAME = @filename) 
       LOG ON (FILENAME = @filenamelog) 
       FOR ATTACH GO
你与清晨阳光 2024-09-22 02:45:35

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

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

I solved this problem by creating an extension method to wrap all entities appropriately.

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

Usage:

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);
掀纱窥君容 2024-09-22 02:45:35

我通过调用存储过程“sp_executesql”中的构建来解决此任务。
用于创建数据库的连接字符串指向“master”。
完整的SQL语句是参数值的一部分:

using (SqlConnection connection = new SqlConnection(ConnectionString))
{
    using (SqlCommand command = new SqlCommand("sp_executesql", connection))
    {
        command.CommandType = CommandType.StoredProcedure;
        var sql = $"CREATE DATABASE NewDatabaseName";
        command.Parameters.Add("MyParameterName", SqlDbType.NVarChar).Value = sql;
        connection.Open();
        command.ExecuteNonQuery();
    }
}

I solved this task by calling the build in stored precedure 'sp_executesql'.
The connectionstring used to create DB points to 'master'.
The complete SQL statement is part of parameter value:

using (SqlConnection connection = new SqlConnection(ConnectionString))
{
    using (SqlCommand command = new SqlCommand("sp_executesql", connection))
    {
        command.CommandType = CommandType.StoredProcedure;
        var sql = 
quot;CREATE DATABASE NewDatabaseName";
        command.Parameters.Add("MyParameterName", SqlDbType.NVarChar).Value = sql;
        connection.Open();
        command.ExecuteNonQuery();
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文