C# 類型系統(tǒng)上實(shí)現(xiàn)一個 SQL 查詢引擎原型
在 C# 類型系統(tǒng)上實(shí)現(xiàn)一個 SQL 查詢引擎,核心目標(biāo)是:利用 C# 的強(qiáng)類型、泛型、表達(dá)式樹(Expression Trees)等特性,構(gòu)建一個類型安全、可組合、可編譯為 SQL 的查詢系統(tǒng)。這類似于 Entity Framework Core 或 LINQ to SQL 的核心機(jī)制。
下面我們將從零開始,逐步設(shè)計并實(shí)現(xiàn)一個簡化但功能完整的 類型化 SQL 查詢引擎原型。
一、目標(biāo)與范圍
我們要實(shí)現(xiàn)的功能:
- 支持
SELECT,WHERE,ORDER BY,LIMIT - 支持基本比較(
==,>,<)、邏輯運(yùn)算(&&,||) - 支持投影(
Select(x => new { x.Id, x.Name })) - 將 LINQ 表達(dá)式樹翻譯為 SQL 字符串
- 全程類型安全:編譯時報錯,而非運(yùn)行時
不實(shí)現(xiàn)(簡化):
- JOIN、GROUP BY、子查詢、聚合函數(shù)
- 參數(shù)化防注入(但會預(yù)留接口)
- 復(fù)雜類型映射(僅支持簡單 POCO)
二、整體架構(gòu)設(shè)計
// 1. 實(shí)體類(用戶定義)
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
// 2. 查詢?nèi)肟冢篋bSet<T>
var users = new DbSet<User>("users");
// 3. LINQ 風(fēng)格鏈?zhǔn)秸{(diào)用
var query = users
.Where(u => u.Age > 18)
.OrderBy(u => u.Name)
.Select(u => new { u.Id, u.Name })
.Take(10);
// 4. 生成 SQL
string sql = query.ToSql();
// 輸出: SELECT Id, Name FROM users WHERE Age > 18 ORDER BY Name LIMIT 10三、核心組件實(shí)現(xiàn)
1.DbSet<T>:查詢起點(diǎn)
public class DbSet<T>
{
private readonly string _tableName;
public DbSet(string tableName) => _tableName = tableName;
public Query<T> Where(Expression<Func<T, bool>> predicate)
=> new Query<T>(_tableName).Where(predicate);
// 其他方法委托給 Query<T>
}2.Query<T>:查詢構(gòu)建器(支持鏈?zhǔn)秸{(diào)用)
public class Query<T>
{
public string TableName { get; }
public Expression? WhereClause { get; private set; }
public LambdaExpression? OrderByClause { get; private set; }
public LambdaExpression? SelectClause { get; private set; }
public int? Limit { get; private set; }
internal Query(string tableName) => TableName = tableName;
public Query<T> Where(Expression<Func<T, bool>> predicate)
{
WhereClause = predicate;
return this;
}
public Query<T> OrderBy<TKey>(Expression<Func<T, TKey>> keySelector)
{
OrderByClause = keySelector;
return this;
}
public Query<TResult> Select<TResult>(Expression<Func<T, TResult>> selector)
{
return new Query<TResult>(TableName)
{
WhereClause = WhereClause,
OrderByClause = OrderByClause,
SelectClause = selector,
Limit = Limit
};
}
public Query<T> Take(int count)
{
Limit = count;
return this;
}
public string ToSql()
{
return SqlTranslator.Translate(this);
}
}3.SqlTranslator:表達(dá)式樹 → SQL
這是最核心的部分!我們遍歷表達(dá)式樹,生成 SQL 片段。
public static class SqlTranslator
{
public static string Translate<T>(Query<T> query)
{
var select = BuildSelect(query.SelectClause ?? GetDefaultSelect<T>());
var from = $"FROM {query.TableName}";
var where = query.WhereClause != null ? $"WHERE {Visit(query.WhereClause.Body)}" : "";
var orderBy = query.OrderByClause != null ? $"ORDER BY {GetMemberName(query.OrderByClause.Body)}" : "";
var limit = query.Limit.HasValue ? $"LIMIT {query.Limit}" : "";
return $"{select} {from} {where} {orderBy} {limit}".Replace(" ", " ").Trim();
}
private static string BuildSelect(LambdaExpression selector)
{
if (selector.Body is MemberInitExpression init)
{
var members = init.Bindings.OfType<MemberAssignment>()
.Select(b => b.Member.Name);
return "SELECT " + string.Join(", ", members);
}
else if (selector.Body is ParameterExpression)
{
// SELECT * 情況
return "SELECT *";
}
else if (selector.Body is MemberExpression member)
{
return $"SELECT {member.Member.Name}";
}
throw new NotSupportedException("Unsupported select expression");
}
private static string Visit(Expression expr)
{
return expr switch
{
BinaryExpression bin => VisitBinary(bin),
MemberExpression mem => mem.Member.Name,
ConstantExpression con => FormatValue(con.Value),
MethodCallExpression call => throw new NotSupportedException("Method calls not supported"),
_ => throw new NotSupportedException($"Expression {expr.NodeType} not supported")
};
}
private static string VisitBinary(BinaryExpression bin)
{
var left = Visit(bin.Left);
var right = Visit(bin.Right);
var op = bin.NodeType switch
{
ExpressionType.Equal => "=",
ExpressionType.GreaterThan => ">",
ExpressionType.LessThan => "<",
ExpressionType.AndAlso => "AND",
ExpressionType.OrElse => "OR",
_ => throw new NotSupportedException($"Operator {bin.NodeType} not supported")
};
return $"({left} {op} {right})";
}
private static string FormatValue(object? value)
{
return value switch
{
null => "NULL",
string s => $"'{s.Replace("'", "''")}'", // 簡單轉(zhuǎn)義
bool b => b ? "1" : "0",
_ => value.ToString()!
};
}
private static string GetMemberName(Expression expr)
{
return expr switch
{
MemberExpression m => m.Member.Name,
UnaryExpression u when u.NodeType == ExpressionType.Convert => GetMemberName(u.Operand),
_ => throw new ArgumentException("Expected member access")
};
}
private static LambdaExpression GetDefaultSelect<T>()
{
var param = Expression.Parameter(typeof(T), "x");
return Expression.Lambda(param, param);
}
}四、使用示例
var users = new DbSet<User>("users");
// 示例 1:完整查詢
var query1 = users
.Where(u => u.Age > 18 && u.Name == "Alice")
.OrderBy(u => u.Name)
.Select(u => new { u.Id, u.Name })
.Take(5);
Console.WriteLine(query1.ToSql());
// 輸出: SELECT Id, Name FROM users WHERE ((Age > 18) AND (Name = 'Alice')) ORDER BY Name LIMIT 5
// 示例 2:僅 WHERE
var query2 = users.Where(u => u.Id == 100);
Console.WriteLine(query2.ToSql());
// 輸出: SELECT * FROM users WHERE (Id = 100)五、關(guān)鍵設(shè)計亮點(diǎn)
| 特性 | 實(shí)現(xiàn)方式 | 優(yōu)勢 |
|---|---|---|
| 類型安全 | 泛型 + 表達(dá)式樹 | 編譯時檢查字段名、類型 |
| 鏈?zhǔn)?API | Fluent Builder | 符合 LINQ 習(xí)慣 |
| SQL 生成 | 表達(dá)式樹遍歷 | 避免字符串拼接錯誤 |
| 投影支持 | MemberInitExpression 解析 | 支持匿名對象選擇 |
六、可擴(kuò)展方向
- 參數(shù)化查詢
- 在
FormatValue中改為返回參數(shù)占位符(如@p0),并收集參數(shù)值
- 在
- JOIN 支持
- 引入
Join<TOuter, TInner, TResult>方法,解析 lambda 中的關(guān)聯(lián)條件
- 引入
- 更多運(yùn)算符
- 支持
Contains(→IN)、StartsWith(→LIKE)
- 支持
- 方言適配
- 抽象
ISqlDialect,支持 MySQL/PostgreSQL/SQLite 不同語法
- 抽象
- 執(zhí)行層
- 添加
.ToList(),用DbCommand執(zhí)行 SQL 并反序列化結(jié)果
- 添加
七、為什么這很“C#”?
- 表達(dá)式樹(Expression Trees) 是 C# 獨(dú)有的強(qiáng)大特性,允許在運(yùn)行時分析代碼結(jié)構(gòu)。
- 泛型 + 匿名類型 讓投影類型安全且簡潔。
- LINQ 語法糖 使查詢像寫 SQL 一樣自然,但由編譯器保證正確性。
?? 這正是 Entity Framework、Dapper.Linq 等 ORM 的底層思想!
總結(jié)
我們用 不到 200 行核心代碼,在 C# 類型系統(tǒng)上構(gòu)建了一個:
- ? 類型安全
- ? 可組合
- ? 可翻譯為 SQL
- ? 支持基本查詢操作
的 SQL 查詢引擎原型。它展示了 如何將語言特性(表達(dá)式樹)與領(lǐng)域問題(SQL 生成)優(yōu)雅結(jié)合——這正是現(xiàn)代 C# 高級庫的設(shè)計精髓。
到此這篇關(guān)于C# 類型系統(tǒng)上實(shí)現(xiàn)一個 SQL 查詢引擎原型的文章就介紹到這了,更多相關(guān)C# SQL 查詢引擎內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C# WinForm創(chuàng)建Excel文件的實(shí)例
下面小編就為大家?guī)硪黄狢# WinForm創(chuàng)建Excel文件的實(shí)例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-01-01

