C#.NET SqlKata 使用详解:优雅构建动态 SQL 查询

作者:唐青枫日期:2025/10/23

简介

  • 在复杂项目中,为了保持 SQL 灵活性与可读性,开发者往往需要手写大量拼接字符串或使用 ORMs 附带的 LINQ,但两者各有局限:手写拼接易出错、难以维护;LINQ 在某些场景下生成的 SQL 不够直观或性能不佳。
  • SqlKata 是一款轻量级、数据库无关的查询构建器(Query Builder),提供——
    • 流式 API,链式调用拼装 SQL
    • 可切换编译器,支持多种数据库方言(SQL Server、PostgreSQL、MySQL、SQLite、Oracle 等)
    • 语法可读,生成的 SQL 与手写风格接近,便于调试和维护

支持环境与安装

  • 目标框架:.NET Standard 2.0+,兼容 .NET Framework 4.6.1 及更高、.NET Core 2.1+、.NET 5/6/7/8+
  • 安装 NuGet 包:
1Install-Package SqlKata
2
  • 若需内置执行支持(与 Dapper 集成),可安装:
1Install-Package SqlKata.Execution
2
  • 在代码中引入命名空间:
1using SqlKata;
2using SqlKata.Compilers;
3using SqlKata.Execution;
4

数据库驱动

根据目标数据库,安装对应的 ADO.NET 提供程序:

  • SQL Server:System.Data.SqlClient
  • MySQL:MySql.DataMysqlConnector
  • PostgreSQL:Npgsql
  • SQLite:System.Data.SQLite
  • Oracle:Oracle.ManagedDataAccess
  • Firebird:FirebirdSql.Data.FirebirdClient

项目配置

ASP.NET Core 项目中,可以通过依赖注入(DI)配置 QueryFactory

1using Microsoft.Extensions.DependencyInjection;
2using SqlKata;
3using SqlKata.Execution;
4using System.Data.SqlClient;
5
6public class Startup
7{
8    public void ConfigureServices(IServiceCollection services)
9    {
10        services.AddTransient<QueryFactory>(sp =>
11        {
12            var connection = new SqlConnection("你的连接字符串");
13            var compiler = new SqlServerCompiler();
14            return new QueryFactory(connection, compiler);
15        });
16    }
17}
18

核心功能

Query Builder

  • 基本查询:
1var query = new Query("Users")
2    .Select("Id", "Name", "Email")
3    .Where("IsActive", true)
4    .OrderByDesc("CreatedAt")
5    .Limit(10, 20);  // OFFSET 10 ROWS FETCH NEXT 20 ROWS
6
  • 链式拼装:支持 .Where(), .OrWhere(), .WhereIn(), .WhereBetween(), .Join(), .GroupBy(), .Having() 等常用子句。
  • 条件查询

使用 Where 方法添加条件:

1var cars = await db.Query("cars")
2    .Where("price", ">", 20000)
3    .Where("name", "like", "%Audi%")
4    .GetAsync<Car>();
5
  • 动态查询

根据条件动态构建查询:

1public async Task<IEnumerable<Car>> SearchCars(string searchText, int? maxPrice)
2{
3    var query = db.Query("cars");
4
5    if (!string.IsNullOrEmpty(searchText))
6    {
7        query.WhereLike("name", $"%{searchText}%");
8    }
9
10    if (maxPrice.HasValue)
11    {
12        query.Where("price", "<=", maxPrice.Value);
13    }
14
15    return await query.GetAsync<Car>();
16}
17
  • 连接(JOIN

支持多种连接类型(如内连接、左连接):

1var query = db.Query("Course")
2    .Join("Department", "Department.ID", "Course.DepartmentID")
3    .LeftJoin("Instructor", "Instructor.ID", "Course.InstructorID")
4    .Select("Course.Title", "Department.Name as DepartmentName")
5    .Where("Department.ID", 1);
6
7var results = await query.GetAsync();
8
  • 子查询和复杂查询

支持子查询和嵌套条件:

1var lastPurchaseQuery = db.Query("Transactions")
2    .Where("Type", "Purchase")
3    .GroupBy("UserId")
4    .SelectRaw("MAX([Date]) as LastPurchaseDate");
5
6var users = await db.Query("Users")
7    .Include("LastPurchase", lastPurchaseQuery)
8    .ForPage(1, 10)
9    .GetAsync();
10
  • 插入、更新和删除

SqlKata 也可以执行插入、更新和删除操作:

1// 插入
2var insertQuery = db.Query("cars").AsInsert(new { name = "BMW", price = 30000 });
3await insertQuery.ExecuteAsync();
4
5// 更新
6var updateQuery = db.Query("cars")
7    .Where("id", 1)
8    .AsUpdate(new { price = 35000 });
9await updateQuery.ExecuteAsync();
10
11// 删除
12var deleteQuery = db.Query("cars")
13    .Where("id", 1)
14    .AsDelete();
15await deleteQuery.ExecuteAsync();
16

编译器(Compiler)

  • 多数据库方言:根据目标数据库,选择对应 Compiler,生成符合方言的 SQL 与参数。
1var compiler = new SqlServerCompiler();
2var sqlResult = compiler.Compile(query);
3
4// sqlResult.Sql => "SELECT [Id], [Name], [Email] FROM [Users] WHERE [IsActive] = @p0 ORDER BY [CreatedAt] DESC OFFSET @p1 ROWS FETCH NEXT @p2 ROWS ONLY"
5// sqlResult.NamedBindings => { p0 = true, p1 = 10, p2 = 20 }
6
  • 参数化安全:所有输入自动转为参数,防止 SQL 注入。

扩展执行层(Execution)

  • Dapper 整合:通过 QueryFactory,可直接执行并映射结果。
1// 创建连接与工厂
2using var connection = new SqlConnection(connectionString);
3var compiler = new SqlServerCompiler();
4var db = new QueryFactory(connection, compiler);
5
6// 查询单条
7var user = await db.Query("Users")
8                   .Where("Id", 123)
9                   .FirstOrDefaultAsync<User>();
10
11// 查询列表
12var activeUsers = await db.Query("Users")
13                          .Where("IsActive", true)
14                          .GetAsync<User>();
15
16// 插入并返回自增 ID
17var newId = await db.Query("Users")
18                    .InsertGetIdAsync<int>(new {
19                        Name = "Bob",
20                        Email = "[email protected]",
21                        CreatedAt = DateTime.UtcNow
22                    });
23
  • 事务支持:在 QueryFactory 上使用 db.Transaction(...) 或手动传入 IDbTransaction

原生 SQL 混用

  • Raw SQL:可在查询中插入原生片段,或完全执行自定义语句。
1var query = new Query()
2    .FromRaw("Users u INNER JOIN Orders o ON u.Id = o.UserId")
3    .SelectRaw("u.Id, u.Name, COUNT(o.Id) AS OrderCount")
4    .GroupByRaw("u.Id, u.Name");
5
  • 自定义函数:原样插入函数调用,例如 WhereRaw("DATEDIFF(day, CreatedAt, GETDATE()) < 30")

常用 API 详解

API说明
new Query(table)创建针对指定表的查询对象
.Select(cols…)指定要查询的列
.Where(col, op, val)添加 WHERE 条件(支持省略 op,默认为 =)
.OrWhere(...)添加 OR 条件
.WhereIn(col, array)WHERE col IN (…)
.WhereBetween(col, lo, hi)WHERE col BETWEEN lo AND hi
.Join(table, c1, op, c2)内连接
.LeftJoin(…)/.RightJoin左/右连接
.GroupBy(cols…)分组
.Having(...)HAVING 子句
.OrderBy(col)升序排序
.OrderByDesc(col)降序排序
.Limit(offset, count)分页(SQL Server 使用 OFFSET…FETCH,MySQL/PG 使用 LIMIT)
.Compile(compiler)生成 SQL 文本与参数
QueryFactory.GetAsync<T>()执行查询并映射为实体列表
InsertAsync(object)插入新记录
InsertGetIdAsync<T>(object)插入并返回自增主键
UpdateAsync(object)根据实体的主键更新记录
DeleteAsync(object)根据实体的主键删除记录
QueryFactory.StatementAsync(sql, params)执行任意 SQL 语句

实现原理

  • 查询构建:SqlKata 使用 Query 类表示 SQL 查询,通过链式方法构造查询树(Query Tree)。
  • 编译器:Compiler(如 SqlServerCompiler、PostgresCompiler)将查询树转换为特定数据库的 SQL 语句,并生成参数化查询。
  • 执行:SqlKata.Execution 包通过 XQuery 类和 Dapper 执行编译后的 SQL,映射结果到 C# 对象。
  • 参数化:SqlKata 默认使用参数化查询,防止 SQL 注入。

性能与对比

  • 与手写 SQL
    • SqlKata 在拼装 SQL 和生成参数时有非常轻量的开销,实际运行时与 Dapper + 手写 SQL 相差极小。
    • 优势在于可读性与可维护性,更少的拼接错误和参数绑定麻烦。
  • LINQ to SQL / EF Core
    • LINQ 在复杂联表、子查询场景下生成的 SQL 往往较冗长,且性能优化受限。
    • SqlKata 生成的 SQL 与手写几乎一致,开发者可更精细地控制索引使用和执行计划。
  • 跨数据库兼容性:同一套查询构建逻辑,通过不同 Compiler 可无缝切换数据库,减少重复代码和维护成本。

实战项目

1using Microsoft.AspNetCore.Mvc;
2using SqlKata;
3using SqlKata.Execution;
4using System.Data.SqlClient;
5using System.Threading.Tasks;
6
7public class Car
8{
9    public int Id { get; set; }
10    public string Name { get; set; }
11    public int Price { get; set; }
12}
13
14[ApiController]
15[Route("api/cars")]
16public class CarsController : ControllerBase
17{
18    private readonly QueryFactory _db;
19
20    public CarsController(QueryFactory db)
21    {
22        _db = db;
23    }
24
25    [HttpGet]
26    public async Task<IActionResult> Search([FromQuery] string searchText, [FromQuery] int? maxPrice)
27    {
28        var query = _db.Query("cars");
29
30        if (!string.IsNullOrEmpty(searchText))
31        {
32            query.WhereLike("name", $"%{searchText}%");
33        }
34
35        if (maxPrice.HasValue)
36        {
37            query.Where("price", "<=", maxPrice.Value);
38        }
39
40        var cars = await query.GetAsync<Car>();
41        return Ok(cars);
42    }
43}
44

启动配置:

1builder.Services.AddTransient<QueryFactory>(sp =>
2{
3    var connection = new MySqlConnection("Server=localhost;Database=master;User Id=root;Password=");
4    var compiler = new MySqlCompiler();
5    return new QueryFactory(connection, compiler);
6});
7

资源和文档


C#.NET SqlKata 使用详解:优雅构建动态 SQL 查询》 是转载文章,点击查看原文


相关推荐


深度解析 DNS服务:从基础原理到自建 DNS 服务器完整指南
coder4_2025/10/22

目录 一、DNS服务基础知识 1、DNS 2、域名 ①域名组成 ②域名管理与结构 3、DNS服务工作原理 ①主机中DNS客户端配置 ②DNS服务解析域名流程 4、DNS查询类型 ①递归查询 ②迭代查询 5、DNS解析类型 6、hosts文件 7、完整的域名查询请求流程 二、DNS检测工具 1、dig命令 2、host命令 3、nslookup命令 4、rndc命令 5、whois命令 三、自建DNS服务 1、ubuntu中的systemd-reso


Python爬虫入门:从零到数据采集
Moniane2025/10/21

Python爬虫基础指南 Python爬虫是自动化获取网络数据的技术,广泛应用于数据采集、市场分析等领域。以下是核心实现步骤: 1. 核心库选择 import requests # 发送HTTP请求 from bs4 import BeautifulSoup # HTML解析 import pandas as pd # 数据存储 2. 基础爬取流程 # 发送请求 response = requests.get("https://example.com/books") res


GPT-2 中的残差权重初始化
mwq301232025/10/19

GPT-2 中的残差权重初始化 1. 概述 在深度残差网络中,每一层的输出都会被加到其输入上。如果不对这些层的权重进行特殊处理,随着网络深度的增加,残差路径上累加的信号方差可能会不断增大,导致数值不稳定和训练发散。 为了解决这个问题,GPT-2 的作者在论文中提出了一种针对残差连接路径上的层进行特殊缩放的初始化方法。 2. 哪些是“残差权重”? 在 GPT-2 的一个 Decoder Block 中,有两个子层的输出会直接被加到残差流(Residual Stream)上。因此,这两层的权重就是我


SpringBoot启动优化7板斧:砍掉70%启动时间的魔鬼实践
皮皮林5512025/10/18

1.延迟初始化:按需加载的智慧 2.组件扫描精准打击:告别无差别扫描 3.JVM参数调优:启动加速的隐藏开关 4.自动配置瘦身:砍掉Spring Boot的"赘肉" 5.类加载优化:让JVM轻装上阵 6.数据库连接优化:断开启动时的枷锁 7.编译优化:AOT与分层编译的威力 综合优化案例:电商平台实战 启动优化检查清单 各优化手段效果对比图 1. 延迟初始化:按需加载的智慧 实践方案: # application.properties spring.main.lazy-initializat


除了JSON/XML,你还应该了解的数据描述语言ASN.1 —— 附《SpringBoot实现ASN.1在线解析工具》
风象南2025/10/17

前言 在日常开发中,我们经常接触JSON、XML等数据格式,但你是否听说过ASN.1?这种在通信、安全、物联网领域广泛使用的数据描述语言。 可能对一部分开发者来说有些陌生,但在特定场景下却有着不可替代的作用。今天,我们就来深入了解一下ASN.1,并用SpringBoot实现一个在线解析工具。 什么是ASN.1? 基本概念 ASN.1(Abstract Syntax Notation One)是一种标准化的数据描述语言,由ITU-T(国际电信联盟)和ISO(国际标准化组织)共同制定。它提供了一种平


Redis(62)如何优化Redis的连接数?
Victor3562025/10/15

优化 Redis 的连接数可以通过以下策略实现: 使用连接池。 合理配置 Redis。 调整操作系统限制。 使用客户端连接复用。 实现批量操作。 分布式架构及读写分离。 以下将详细探讨这些策略,并提供相应代码示例。 1. 使用连接池 连接池可以重用现有连接,减少连接创建和释放的开销,从而提升性能。 Java 示例(Jedis 连接池) 添加 Jedis 依赖: 确保在 pom.xml 文件中添加 Jedis 依赖: <dependency> <groupId>redis.clie


nginx反向代理与缓存功能
Justin_192025/10/14

目录 一,正向代理和反向代理 1,正向代理概述 2,反向代理概述 二,配置实战 1,实现反向代理负载均衡 三,nginx配置跨域cors 1,跨域的定义 2,同源的定义 3,不同源的限制 4,nginx解决跨域的原理 5,案例 一,正向代理和反向代理 1,正向代理概述 正向代理是一个位于客户端和目标服务器之间的代理服务器。为了从目标服务器得到目标,客户端向代理服务器发送一个请求,并且指定目标服务器,再代理向目标服务器转发请求,将得到的目标返回给客户端。 正向


前端人必看!3个技巧让你彻底搞懂JS条件判断与循环
良山有风来2025/10/13

那些年我们踩过的坑 还记得刚学JavaScript的时候吗?遇到复杂的条件判断就头晕,写循环时总是搞不清该用for还是while。最尴尬的是,代码跑起来总是不按预期的来,bug找得头都大了! 说实话,这些困扰我都经历过。但后来我发现,只要掌握了几个核心技巧,这些看似复杂的问题都能轻松解决。 今天我就把压箱底的干货拿出来,用最通俗易懂的方式,带你彻底搞懂JS的条件判断和循环控制。看完这篇文章,你不仅能写出更优雅的代码,还能避开90%新手都会踩的坑! 条件判断:让你的代码更“聪明” 条件判断就像给代


(1)100天python从入门到拿捏
C嘎嘎嵌入式开发2025/10/11

官方文档 python教程 window系统下查看python版本 win+R键打开cmd输入下面的命令 python --version 或者 python -V 类似c语言以.c为后缀,c++以.cpp为后缀,python文件的扩展名以.py结尾 Jupyter Notebook文件以.ipynb为后缀,该类文件通常包含代码、文本和可视化结果 Python 3简介 Python 3 是一种高级、解释型的编程语言,广泛用于 Web 开发、数据科学、机器学习、自


前端梳理体系从常问问题去完善-框架篇(react生态)
大前端helloworld2025/10/9

前言 国庆去趟了杭州,但是人太多了,走路都觉得空气很闷,天气也很热,玩了两天就回宿舍躺了,感觉人太多,看不到风景,而且消费也很高,性价比不是很值得,就呆在公寓,看了两本书,有一本是名著,《呼啸山庄》虽然是写的是爱情,但爱情背后是人性。爱情啊,这个课题本来就是让人很难读懂得,关于爱,也看了一篇文章。关于爱上人渣得,爱上人渣,或是那些求而不得甚至是受制于禁忌的爱,本质上也是在追求这种刺激,或者说正是因为这样的对象能给自己麻木的感官更大的刺激,从而误以为这就是「爱」的本质,就像是人们虽然知道「吊桥效应

首页编辑器站点地图

Copyright © 2025 聚合阅读

License: CC BY-SA 4.0