从.NET安全地获取下一个SQL Server序列值

时间:2013-01-08 17:26:11

标签: c# sql sql-server sequence sql-server-2012

我正在尝试SQL Server中的SEQUENCE对象,并通过指定序列名称来获取C#的下一个值。范围很简单,因为它们有一个存储过程,您可以传递序列名称;

    public static T Reserve<T>(string name, int count, SqlConnection sqlConn)
    {
        using (var sqlCmd = new SqlCommand("sp_sequence_get_range", sqlConn))
        {
            sqlCmd.CommandType = CommandType.StoredProcedure;
            var firstValueParam = new SqlParameter("@range_first_value", SqlDbType.Variant) { Direction = ParameterDirection.Output };

            sqlCmd.Parameters.AddWithValue("@sequence_name", name);
            sqlCmd.Parameters.AddWithValue("@range_size", count);
            sqlCmd.Parameters.Add(firstValueParam);

            sqlCmd.ExecuteNonQuery();

            return (T)firstValueParam.Value;
        }
    }

但是单身价值呢?在我看来,我可以调用上面的计数'1',或者我可以动态地构造SQL。即。

var sqlCmdStr = string.Format("SELECT NEXT VALUE FOR {0}", name);

我知道这通常是不好的做法(即SQL注入)。

有人会建议什么?

4 个答案:

答案 0 :(得分:5)

  

我知道这通常是不好的做法(即SQL注入)。

并非每个动态SQL都是邪恶的。

是否对SQL注入开放取决于值(在SQL文本中插入的值)来自何处。如果它来自严密控制代码的地方(例如,从一组字符串常量中选择的switch语句),则SQL注入不是问题。

或者,您可以简单地为每个序列分别设置一个查询(假设您没有很多序列)。

答案 1 :(得分:2)

我的建议是@Gserg的回答和您当前的解决方案的组合。编写一个带有VARCHAR参数@Name的存储过程。根据@GSerg的建议,使用QUOTENAME在存储过程中构建sql字符串。使用EXECsp_executesql运行脚本。

像这样(徒手):

CREATE PROCEDURE [GetNext]
    @Name VARCHAR(50)
AS
BEGIN
    DECLARE @sql VARCHAR(200);
    SET @Name = QUOTENAME(@Name, '[');
    SET @sql = 'SELECT NEXT VALUE FOR ' + @Name;

    EXEC (@sql);
END

答案 2 :(得分:1)

Paul解决方案的另一个版本,它将从SQL序列返回格式化的字母数字键

CREATE PROCEDURE [sp_GetNextKey]
@Name NVARCHAR(50),
@FormatText NVARCHAR(50)
AS

--DECLARE @Name NVARCHAR(50)='CustomerKeySequence'
--DECLARE @FormatText NVARCHAR(50) = 'CUS0000#'

DECLARE @sql NVARCHAR(200) = 'SELECT FORMAT((NEXT VALUE FOR ' + QUOTENAME(@Name, '"') + '),'+QUOTENAME(@FormatText, '''')+')';
EXEC (@sql)

/*
RETURNS i.e CUS00184
*/

答案 3 :(得分:0)

当我需要做类似的事情时,我会这样做:

string sanitized_name;
using (var sqlCmd = new SqlCommand("select quotename(@unsafe_name, '[');", sqlConn))
{
    sqlCmd.Parameters.AddWithValue("@unsafe_name", name);

    sanitized_name = (string)sqlCmd.ExecuteScalar();
}

using (var sqlCmd = new SqlCommand(string.Format("select next value for {0};", sanitized_name), sqlConn))
{
    ...
}

或者创建一个执行相同操作的服务器端过程。