Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/262.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何在sp_executesql中使用临时表和参数?_C#_Sql Server - Fatal编程技术网

C# 如何在sp_executesql中使用临时表和参数?

C# 如何在sp_executesql中使用临时表和参数?,c#,sql-server,C#,Sql Server,我使用一个存储过程添加了快速而肮脏的整个数据库搜索,该存储过程的灵感来自于。它工作正常,但我担心SQL注入。当然,我使用SQL参数: string query = GetUserInput(); using (SqlConnection con = new SqlConnection(conString)) { using (SqlCommand cmd = new SqlCommand("SearchAllTables", con)) {

我使用一个存储过程添加了快速而肮脏的整个数据库搜索,该存储过程的灵感来自于。它工作正常,但我担心SQL注入。当然,我使用SQL参数:

 string query = GetUserInput();
 using (SqlConnection con = new SqlConnection(conString))
 {
        using (SqlCommand cmd = new SqlCommand("SearchAllTables", con))
        {
                cmd.CommandType = CommandType.StoredProcedure;

                cmd.Parameters.Add("@SearchStr", SqlDbType.VarChar).Value = query;

                con.Open();
                SqlDataReader reader = cmd.ExecuteReader();
但是,由于SP构建并执行查询,SQL注入仍然是可能的。相关部分:

CREATE PROC [dbo].[SearchAllTables] 
(
@SearchStr nvarchar(100)
)
AS
BEGIN

CREATE TABLE #Results (TableName nvarchar(370), ColumnName nvarchar(370), ColumnValue nvarchar(3630), TableId int)
--snip boring part

SET @SearchStr2 = QUOTENAME('%' + @SearchStr + '%','''')
INSERT INTO #Results
EXEC
(
    'SELECT ''' + @TableName +''', ''' + @TableName + '.' + @ColumnName + ''', LEFT(CONVERT(varchar(max), ' + @ColumnName + '), 3630), [id]
     FROM ' + @TableName + ' (NOLOCK) ' +
     ' WHERE CONVERT(varchar(max), ' + @ColumnName + ') LIKE ' + @SearchStr2 --This is vulnerable
)

--snip all the END

SELECT TableName, ColumnName, ColumnValue, TableId FROM #Results
我想将其更改为使用带有参数的sp_executesql来防止SQL注入。我把它改成:

declare @query nvarchar(1000)
set @query= 'SELECT ''' + @TableName +''', ''' + @TableName + '.' + @ColumnName + ''', LEFT(CONVERT(varchar(max), ' 
            + @ColumnName + '), 3630), [id] FROM ' + @TableName + ' (NOLOCK) ' +
            ' WHERE CONVERT(varchar(max), ' + @ColumnName + ') LIKE @term'
INSERT INTO #Results            
EXEC sp_executesql @query, N'@term nvarchar(100)', @term=@SearchStr2;
但是我现在没有得到任何结果。我尝试在查询中包含“insert-into”并使用全局临时表,但没有骰子。如何防止SQL注入并返回结果?或者我的方法错了吗?

在动态查询中,诸如和列名之类的内容不能作为参数发送,因此必须附加它们。当然,这相当混乱(而且容易出错)

由于您使用的是C#,我建议您仔细研究一下。它提供了一些扩展,允许在LINQ查询中使用字符串查询。显示了生成动态查询的其他方法

好,回到你最初的问题

1) 动态Linq应允许您轻松编写查询,例如:

// this requires C# 6.0 to use interpolated strings. String.Format can be used instead
someRepository.GetAll.Where($"{col1} = {value1} And {col2} = {value2}");
因此,您有动态列和值,但需要动态表。一种方法是基于提供的类型动态获取存储库:

// this contains repositories for all types mapped to used tables
public class UnitOfWork : IUnitOfWork
{
    public IRepository<Table1> Table1Repository { get; private set; }
    public IRepository<Table2> Table2Repository { get; private set; }
    // other come here

    // all these are injected
    public UnitOfWork(IDbContext context, IRepository<Table1> table1Repository, IRepository<Table2> table2Repository
    {
        Table1Repository = table1Repository;
        Table2Repository = table2Repository;
        // other initializations
    }

    // typed version
    public IRepository<T> GetRepository<T>()
        where T: class
    {
        Type thisType = this.GetType();
        foreach (var prop in thisType.GetProperties())
        {
            var propType = prop.PropertyType;

            if (!typeof(IRepository).IsAssignableFrom(propType))
                continue;

            var repoType = propType.GenericTypeArguments[0];
            if (repoType == typeof(T))
                return (IRepository<T>) prop.GetValue(this);
        }

        throw new ArgumentException(String.Format("No repository of type {0} found", typeof(T).FullName));
    }

    // dynamic type version (not tested, just written here)
    public IRepository GetRepository(Type type)
        where T: class
    {
        Type thisType = this.GetType();
        foreach (var prop in thisType.GetProperties())
        {
            var propType = prop.PropertyType;

            if (!typeof(IRepository).IsAssignableFrom(propType))
                continue;

            var repoType = propType.GenericTypeArguments[0];
            if (repoType == type)
                return (IRepository) prop.GetValue(this);
        }

        throw new ArgumentException(String.Format("No repository of type {0} found", typeof(T).FullName));
    }
}
但是,如果要同时应用多个条件,这是相当棘手的

2) 一个有趣的方法是使用:

但是,我认为LINQ2SQL不能为Where生成SQL,因此源代码必须是对象列表。这是一个严重的问题,如果数据没有事先过滤以减少长度

3) 正如所指出的,表达式树似乎是最好的选择。比如:

var filterTableMap = new Dictionary<String, Type>()
    {
        { "Table1", typeof(Table1Repository) },
        { "Table2", typeof(Table2Repository) },
        // and so on
    };
var param = Expression.Parameter(typeof(String));
var condition =
    Expression.Lambda<Func<String, bool>>(
        Expression.Equal(
            Expression.Property(param, "Col1"),
            Expression.Constant("Value1", typeof(String))
        ),
        param
    ).Compile(); 
// for LINQ to SQL/Entities skip Compile() call

var query = source.Where(condition);
var param=Expression.Parameter(typeof(String));
var条件=
Lambda(
表达式。相等(
Expression.Property(param,“Col1”),
常量表达式(“值1”,类型(字符串))
),
param
).Compile();
//对于LINQ到SQL/实体,跳过Compile()调用
var query=source.Where(条件);
对于包含,解决方案更复杂,如图所示

我在C#中看到的对过滤建模的优势是:

  • 没有乱码
  • 没有SQL注入(但可能导致)
  • 如果使用了存储库和依赖项注入,那么单元测试就变得容易了
  • 易于维护
  • 缺点:

  • 更复杂
  • 可能的性能问题
  • 在动态查询中,诸如和列名之类的内容不能作为参数发送,因此必须追加它们。当然,这相当混乱(而且容易出错)

    由于您使用的是C#,我建议您仔细研究一下。它提供了一些扩展,允许在LINQ查询中使用字符串查询。显示了生成动态查询的其他方法

    好,回到你最初的问题

    1) 动态Linq应允许您轻松编写查询,例如:

    // this requires C# 6.0 to use interpolated strings. String.Format can be used instead
    someRepository.GetAll.Where($"{col1} = {value1} And {col2} = {value2}");
    
    因此,您有动态列和值,但需要动态表。一种方法是基于提供的类型动态获取存储库:

    // this contains repositories for all types mapped to used tables
    public class UnitOfWork : IUnitOfWork
    {
        public IRepository<Table1> Table1Repository { get; private set; }
        public IRepository<Table2> Table2Repository { get; private set; }
        // other come here
    
        // all these are injected
        public UnitOfWork(IDbContext context, IRepository<Table1> table1Repository, IRepository<Table2> table2Repository
        {
            Table1Repository = table1Repository;
            Table2Repository = table2Repository;
            // other initializations
        }
    
        // typed version
        public IRepository<T> GetRepository<T>()
            where T: class
        {
            Type thisType = this.GetType();
            foreach (var prop in thisType.GetProperties())
            {
                var propType = prop.PropertyType;
    
                if (!typeof(IRepository).IsAssignableFrom(propType))
                    continue;
    
                var repoType = propType.GenericTypeArguments[0];
                if (repoType == typeof(T))
                    return (IRepository<T>) prop.GetValue(this);
            }
    
            throw new ArgumentException(String.Format("No repository of type {0} found", typeof(T).FullName));
        }
    
        // dynamic type version (not tested, just written here)
        public IRepository GetRepository(Type type)
            where T: class
        {
            Type thisType = this.GetType();
            foreach (var prop in thisType.GetProperties())
            {
                var propType = prop.PropertyType;
    
                if (!typeof(IRepository).IsAssignableFrom(propType))
                    continue;
    
                var repoType = propType.GenericTypeArguments[0];
                if (repoType == type)
                    return (IRepository) prop.GetValue(this);
            }
    
            throw new ArgumentException(String.Format("No repository of type {0} found", typeof(T).FullName));
        }
    }
    
    但是,如果要同时应用多个条件,这是相当棘手的

    2) 一个有趣的方法是使用:

    但是,我认为LINQ2SQL不能为Where生成SQL,因此源代码必须是对象列表。这是一个严重的问题,如果数据没有事先过滤以减少长度

    3) 正如所指出的,表达式树似乎是最好的选择。比如:

    var filterTableMap = new Dictionary<String, Type>()
        {
            { "Table1", typeof(Table1Repository) },
            { "Table2", typeof(Table2Repository) },
            // and so on
        };
    
    var param = Expression.Parameter(typeof(String));
    var condition =
        Expression.Lambda<Func<String, bool>>(
            Expression.Equal(
                Expression.Property(param, "Col1"),
                Expression.Constant("Value1", typeof(String))
            ),
            param
        ).Compile(); 
    // for LINQ to SQL/Entities skip Compile() call
    
    var query = source.Where(condition);
    
    var param=Expression.Parameter(typeof(String));
    var条件=
    Lambda(
    表达式。相等(
    Expression.Property(param,“Col1”),
    常量表达式(“值1”,类型(字符串))
    ),
    param
    ).Compile();
    //对于LINQ到SQL/实体,跳过Compile()调用
    var query=source.Where(条件);
    
    对于包含,解决方案更复杂,如图所示

    我在C#中看到的对过滤建模的优势是:

  • 没有乱码
  • 没有SQL注入(但可能导致)
  • 如果使用了存储库和依赖项注入,那么单元测试就变得容易了
  • 易于维护
  • 缺点:

  • 更复杂
  • 可能的性能问题

  • 为什么允许用户搜索系统中的任何表?为什么用户甚至知道表名?您可以将表名包装在QUOTENAME中以帮助防止sql注入,但老实说,整个概念对我来说似乎有点不合适。如果要使用NOLOCK,则需要包含WITH关键字。不赞成省略它。当然,如果您希望在搜索中得到准确的结果,您应该使用该提示,因为它有时可能会丢失数据。用户不提供表/列名,这些变量在过程的前面已经填充。用户的搜索词已由QUOTENAME包装。在我看来,生成查询的方式似乎不起作用,因为您正在用QUOTENAME包装搜索值。用于对象名称而不是值的。在底部编码的方式看起来是正确的,只需去掉在提供的搜索词周围使用quotename的部分。调试这种类型的动态sql的唯一方法是使用print/select语句。如果您查看@query,您将看到您在quotename中引入的错误。删除quotename是有效的,但是我希望用户能够理解错误。当我保留QUOTENAME并打印查询时,如果我搜索“test”,它会显示为类似“%test%”的
    ,这在我看来很好。不要再仔细看了。由于使用了quotename,所以它现在在您的值周围有单引号。您正在动态sql中使用一个参数,因此它正在查找“%test%”之类的值,但找不到任何值,因为该单引号现在是搜索条件的一部分。为什么您允许用户搜索系统中的任何表?为什么用户