将表名作为参数传递给Dapper

22

是否可以将表名作为参数传递给Dapper查询命令?我不想要一个SQL表定义函数或SQL表变量。我想在C#中定义表名并将其传递给Dapper。这是我的代码,当执行时,返回一个错误必须声明表变量"@TableName"

var foo = conn.Query("SELECT * FROM @TableName WHERE Id = @Id", new { TableName = "MyTable", Id = 123 });
4个回答

29

SQL不支持参数化表名,而Dapper只是SQL的一个非常轻量级的封装 - 因此:不行。

但您可以使用string.format:

string sql = string.Format("... from [{0}] ...", table name);

请注意,即使使用[/]符号,仍存在内在的SQL注入风险。


3
谢谢Marc。我感谢你的建议,目前我正在按照你建议的方法使用string.format进行操作。我也意识到了注入风险,但这个脚本是由我们控制的内部脚本,不存在未知输入的可能性。 - bigmac
1
从C#6开始,您可以使用字符串插值:$“select * from {tablename} where id = @Id”,然后对其他所有内容使用参数。但是,这并没有解决SQL注入风险问题。 - sarin
如果您替换空格,``string sql = string.Format("... from [{0}] ...", tableName.replace(" ",""));```就不会受到 SQL 注入攻击,是吗? - love2code
1
@love2code 这并没有针对 SQL 注入做任何处理,对象/模式等名称允许包含空格。 - Marc Gravell
@MarcGravell 假设有一个恶意用户试图将这个 SQL 注入到输入中,以删除员工表而不是真实的数据表,例如:post; drop table employees; 没有空格的话就会变成 post;droptableemployees; 这样是行不通的,因为会导致 SQL 引擎运行时出错。在我的情况下,我的数据表上没有空格,所以我提到了它,因为这对我可能有效。 - love2code

5

为了防止 SQL 注入,您可以先检查表是否存在:

string tableNameExistsCheck = "SELECT count(1) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = @tableName";

if (c.QuerySingle<int>(tableNameExistsCheck, new { tableName }) == 1)
{
    string sql = string.Format("... from [{0}] ...", tableName);
    var result = c.Query(sql);                    
}

1
我真的很喜欢这种方法。虽然运行时会增加一些开销,但如果需要动态插入表名,这可以帮助减轻 SQL 注入的风险。 - Newteq Developer

2

对于内部通用脚本,我们使用TableNameMapper来构建SQL:

public async Task<bool> DeleteAsync(int id)
{
    string tableName = Dapper.Contrib.Extensions.SqlMapperExtensions.TableNameMapper(typeof(T));
    using (var conn = new SqlConnection(ConnectionString))
    {
        int affectedRows = await conn.ExecuteAsync($"delete from {tableName} where id=@id", new { id });
        return affectedRows > 0;
    }
}

0

如果您只是通过id查询表,则可以使用Dapper.Contrib包。

在类型上添加Table属性和Id列上的Key属性,然后在连接上使用Get(dynamic id)扩展方法。

这样,您就不需要编写动态查询。

[Table("cardtype-orders")]
public class CardTypeOrder 
{
   [Key]
   public Guid Id { get; set; }
 
   public string Description { get; set; }
}

public class CardTypeOrderRepository: GenericRepository<CardTypeOrder>
{
}

public abstract class GenericRepository<T>
{
   public T Get(object id)
   {
       using (var connection = GetConnection())
       {
           return connection.Get<T>(id);
       }
   }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接