C#中SQL LIKE查询与参数化实践指南

C#中SQL LIKE查询与参数化实践指南

在C#开发中,实现模糊查询是数据库交互的常见需求,而LIKE语句与参数化查询的结合使用直接关系到系统安全性与性能表现。本文将从基础语法、安全风险、实现方案到性能优化,系统阐述LIKE查询的最佳实践。

一、LIKE语句的基础语法与风险

LIKE是SQL标准中的模式匹配操作符,通过通配符实现模糊查询:

  • %:匹配任意长度字符串(含空字符串)
  • _:匹配单个任意字符
  • []:匹配括号内任意单个字符(部分数据库支持)

1.1 基础语法示例

  1. -- 查询包含"test"的记录
  2. SELECT * FROM Table WHERE Column LIKE '%test%'
  3. -- 查询以"pre"开头的记录
  4. SELECT * FROM Table WHERE Column LIKE 'pre%'
  5. -- 查询第二个字符为"b"的记录
  6. SELECT * FROM Table WHERE Column LIKE '_b%'

1.2 字符串拼接的致命风险

传统拼接方式存在SQL注入漏洞:

  1. // 危险示例:直接拼接用户输入
  2. string searchTerm = Request.QueryString["term"];
  3. string sql = $"SELECT * FROM Users WHERE Name LIKE '%{searchTerm}%'";
  4. // 若searchTerm为"admin' --",将导致查询逻辑被篡改

攻击者可通过构造特殊输入破坏SQL语句结构,导致数据泄露或系统崩溃。

二、参数化查询的正确实现

参数化查询通过预编译机制分离SQL逻辑与数据,有效防范注入攻击。

2.1 ADO.NET基础实现

  1. using (SqlConnection conn = new SqlConnection(connectionString))
  2. {
  3. string searchTerm = "test"; // 实际应用中应验证输入
  4. string sql = "SELECT * FROM Products WHERE Name LIKE @Pattern";
  5. SqlCommand cmd = new SqlCommand(sql, conn);
  6. // 参数化通配符处理(关键点)
  7. cmd.Parameters.AddWithValue("@Pattern", "%" + searchTerm + "%");
  8. conn.Open();
  9. SqlDataReader reader = cmd.ExecuteReader();
  10. // 处理结果...
  11. }

2.2 Entity Framework Core实现

  1. // 使用EF Core的Like扩展方法(需安装System.Linq.Dynamic.Core)
  2. var searchTerm = "test";
  3. var query = dbContext.Products
  4. .Where(p => EF.Functions.Like(p.Name, $"%{searchTerm}%"))
  5. .ToList();
  6. // 或使用原生LINQ结合Contains(自动转换为LIKE)
  7. var result = dbContext.Products
  8. .Where(p => p.Name.Contains(searchTerm))
  9. .ToList();

2.3 参数化处理的注意事项

  1. 通配符位置:参数值应包含完整模式,而非在SQL中拼接

    1. // 正确:参数包含通配符
    2. cmd.Parameters.AddWithValue("@Pattern", "%" + userInput + "%");
    3. // 错误:SQL中包含通配符
    4. string sql = "SELECT * FROM Table WHERE Name LIKE '%' + @term + '%'";
  2. 输入验证:即使使用参数化查询,仍需验证输入长度和内容

    1. if (string.IsNullOrEmpty(searchTerm) || searchTerm.Length > 50)
    2. {
    3. throw new ArgumentException("Invalid search term");
    4. }

三、性能优化策略

3.1 索引优化方案

  1. 全文索引:对大规模文本搜索,优先使用数据库全文索引功能

    1. -- SQL Server示例
    2. CREATE FULLTEXT CATALOG ftCatalog AS DEFAULT;
    3. CREATE FULLTEXT INDEX ON Products(Name) KEY INDEX PK_Products;
    4. -- 查询时使用
    5. SELECT * FROM Products WHERE CONTAINS(Name, '"test*"');
  2. 函数索引:某些数据库支持对表达式创建索引

    1. -- PostgreSQL示例
    2. CREATE INDEX idx_products_name_search ON Products(LOWER(name));

3.2 查询重构技巧

  1. 分页处理:结合TOP/LIMIT和OFFSET实现高效分页

    1. int pageSize = 20;
    2. int pageNumber = 1;
    3. var query = dbContext.Products
    4. .Where(p => p.Name.Contains(searchTerm))
    5. .Skip((pageNumber - 1) * pageSize)
    6. .Take(pageSize)
    7. .ToList();
  2. 选择性列查询:避免SELECT *,仅查询必要字段

    1. SELECT Id, Name, Price FROM Products WHERE Name LIKE @Pattern

四、安全防护进阶

4.1 输入消毒处理

  1. string SanitizeInput(string input)
  2. {
  3. // 移除潜在危险字符
  4. var invalidChars = new[] { "'", "\"", ";", "--" };
  5. foreach (var c in invalidChars)
  6. {
  7. input = input.Replace(c, string.Empty);
  8. }
  9. // 限制长度
  10. return input.Length > 50 ? input.Substring(0, 50) : input;
  11. }

4.2 最小权限原则

数据库账户应仅授予必要权限:

  1. -- 仅授予SELECT权限
  2. GRANT SELECT ON Products TO WebAppUser;

五、典型场景解决方案

5.1 多条件模糊查询

  1. var query = dbContext.Products
  2. .Where(p =>
  3. (string.IsNullOrEmpty(searchTerm) || p.Name.Contains(searchTerm)) &&
  4. (categoryId == 0 || p.CategoryId == categoryId)
  5. )
  6. .ToList();

5.2 动态LINQ查询构建

  1. // 使用System.Linq.Dynamic.Core实现动态查询
  2. var predicate = PredicateBuilder.New<Product>(true);
  3. if (!string.IsNullOrEmpty(searchTerm))
  4. {
  5. predicate = predicate.And(p => p.Name.Contains(searchTerm));
  6. }
  7. if (categoryId > 0)
  8. {
  9. predicate = predicate.And(p => p.CategoryId == categoryId);
  10. }
  11. var result = dbContext.Products.Where(predicate).ToList();

六、最佳实践总结

  1. 始终参数化:所有用户输入必须通过参数传递
  2. 验证先行:实施输入长度、格式和内容的三重验证
  3. 索引优先:为常用搜索字段建立适当索引
  4. 分页必备:大数据集必须实现分页机制
  5. 日志监控:记录可疑查询模式,建立安全预警

通过系统应用这些实践,开发者可以构建出既安全又高效的数据库查询方案。在实际项目中,建议结合具体数据库特性(如SQL Server的全文搜索、PostgreSQL的trgm扩展等)进一步优化实现。