【问题标题】:passing string as a table type from c# code in to stored procedure将字符串作为表类型从 c# 代码传递到存储过程
【发布时间】:2018-05-14 07:50:50
【问题描述】:

我想将一个表作为字符串传递给存储过程作为参数,并希望从该特定表中检索数据,例如我制作了一些适合年份的表格,例如 purchase20162017、transfer20162017。 所以问题是我创建了一个下拉框,我在其中显示了年份列表,例如 2016-2017,2017-2018,2018-2019 用户从下拉框中选择年份,然后单击一个名为生成分类帐按钮的按钮。所以我从这个下拉框中取出一个字符串,就像这样

string str = DropDownList1.SelectedItem.ToString();
str = str.Replace(@"-", "");
string purdtb = "purchase" + str;

现在我在 purdtb 中有准确的表,但它是字符串,我想将此字符串值作为参数传递给存储过程。请告诉我如何执行此操作,以便我可以将此字符串转换为表名。

【问题讨论】:

  • 您不能在 SQL 中参数化标识符。您可以在存储过程中使用动态 SQL,但是您必须将表名列入白名单,或者您可以以正确的方式进行操作,并通过以下方式修复您的损坏数据库模型将所有这些基于年份的表合并到一个单一表中(这样purchase20162017purchase20142015 将变为purchase,并添加一列来指示年份。
  • 你能举个例子吗?
  • 举个例子?不知道你的数据库长什么样,不知道你要执行什么sql语句...

标签: c# stored-procedures


【解决方案1】:

有两种方法。

A) 最好的是:

string connectionString =
        ConsoleApplication1.Properties.Settings.Default.ConnectionString;
    //
    // In a using statement, acquire the SqlConnection as a resource.
    //
    using (SqlConnection con = new SqlConnection(connectionString))
    {
        //
        // Open the SqlConnection.
        //
        con.Open();
        //
        // The following code uses an SqlCommand based on the SqlConnection.
        //
        using (SqlCommand command = new SqlCommand("SELECT TOP 2 * FROM Dogs1", con))
        using (SqlDataReader reader = command.ExecuteReader())
        {
            while (reader.Read())
            {
                Console.WriteLine("{0} {1} {2}",
                    reader.GetInt32(0), reader.GetString(1), reader.GetString(2));
            }
        }
    }

在 Dogs1 中放置您的表,使用变量通过代码修改查询。当您可以使用变量从代码中管理所有内容时,为什么要在存储过程中放置​​非常尴尬的动态表名?

B) 第二种方式(不好但有效):

在您的存储过程中使用sp_executesql,将表名作为参数传递并在存储过程中构建查询,如文档中所示并捕获结果。

这样,您可以逐个构建查询,并在其中放入您想要的所有内容(SQL 保留关键字除外,无论如何您都应该小心)。

declare @SqlString nvarchar(2000)
declare @ParamDef nvarchar(2000)

set @SqlString = N'exec proc1 @param1, @param2, @param3'

set @ParamDef = N'@param1 bit, @param2 bit, @param3 bit'

EXECUTE sp_executesql @SqlString ,@ParamDef, @param1 = 0, @param2 = 1, @param3 = 1

解释一下,sp_executesql 的工作原理是这样的

EXECUTE sp_executesql
   N'proc1',                                 -- SQL
   N'@param1 bit, @param2 bit, @param3 bit', -- DECLARE
   @param1 = 0, @param2 = 1, @param3 = 1     -- VALUES

翻译成什么

EXECUTE sp_executesql
   N'proc1',                                 -- SQL
   N'@param1 bit, @param2 bit, @param3 bit', -- DECLARE
   @param1 = 0, @param2 = 1, @param3 = 1     -- VALUES

-- DECLARE
  declare @param1 bit, @param2 bit, @param3 bit
-- VALUES
  select @param1 = 0, @param2 = 1, @param3 = 1
-- SQL
  proc1

3) 最坏的情况:

通过 Sqlcommand 从 C# 代码调用 sp_executesql

【讨论】:

  • 当您可以使用变量管理代码中的所有内容时,为什么要在存储过程中放置​​非常尴尬的动态表名? - 因为它为sql注入。猜猜如果有人使用purchase20162017; drop table purchase20162017;-- 作为表名会发生什么? 使用存储过程并将表名列入白名单是安全的。一旦表名不在列表中,您只需返回错误而不运行查询。
  • @ZoharPeled 没有参数化查询。你说的是什么注射?他管理他的代码,他构建他的查询。我们在这里讨论的不是带有用户输入的开放场景
  • 正如我在对该问题的第一条评论中所写的那样 - 您不能在 SQL 中对标识符进行参数化。这使您只有 3 个选项 - 1. 做正确的事情并更改数据库结构,2. 做次优并使用动态 sql,同时白名单标识符,3. 做最坏的事情并在 c# 中连接字符串以创建一个不安全的 SQL 语句。
  • @ZoharPeled 你怎么不能? sp_executesql 让你这样做,我记得我过去这样做过。我还记得实体框架允许你这样做
  • 尝试运行DECLARE @t sysname = N'Dog1';EXEC sp_ExecuteSql N'SELECT * FROM @t', N'@t sysname', @t。您将收到一个错误 - 必须声明表变量“@t”。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多