C#教程

关注公众号 jb51net

关闭
首页 > 软件编程 > C#教程 > C#  SQL 查询引擎

C# 类型系统上实现一个 SQL 查询引擎原型

作者:码云数智-园园

在C#类型系统上实现一个SQL查询引擎,核心目标是利用 C# 的强类型、泛型、表达式树(Expression Trees)等特性,构建一个类型安全、可组合、可编译为 SQL 的查询系统,本文介绍C# 类型系统上实现一个 SQL 查询引擎,感兴趣的朋友一起看看吧

在 C# 类型系统上实现一个 SQL 查询引擎,核心目标是:利用 C# 的强类型、泛型、表达式树(Expression Trees)等特性,构建一个类型安全、可组合、可编译为 SQL 的查询系统。这类似于 Entity Framework Core 或 LINQ to SQL 的核心机制。

下面我们将从零开始,逐步设计并实现一个简化但功能完整的 类型化 SQL 查询引擎原型

一、目标与范围

我们要实现的功能:

不实现(简化):

二、整体架构设计

// 1. 实体类(用户定义)
public class User 
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
}
// 2. 查询入口:DbSet<T>
var users = new DbSet<User>("users");
// 3. LINQ 风格链式调用
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

三、核心组件实现

1.DbSet<T>:查询起点

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>:查询构建器(支持链式调用)

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:表达式树 → SQL

这是最核心的部分!我们遍历表达式树,生成 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("'", "''")}'", // 简单转义
            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)

五、关键设计亮点

特性实现方式优势
类型安全泛型 + 表达式树编译时检查字段名、类型
链式 APIFluent Builder符合 LINQ 习惯
SQL 生成表达式树遍历避免字符串拼接错误
投影支持MemberInitExpression 解析支持匿名对象选择

六、可扩展方向

  1. 参数化查询
    • FormatValue 中改为返回参数占位符(如 @p0),并收集参数值
  2. JOIN 支持
    • 引入 Join<TOuter, TInner, TResult> 方法,解析 lambda 中的关联条件
  3. 更多运算符
    • 支持 Contains(→ IN)、StartsWith(→ LIKE
  4. 方言适配
    • 抽象 ISqlDialect,支持 MySQL/PostgreSQL/SQLite 不同语法
  5. 执行层
    • 添加 .ToList(),用 DbCommand 执行 SQL 并反序列化结果

七、为什么这很“C#”?

💡 这正是 Entity Framework、Dapper.Linq 等 ORM 的底层思想!

总结

我们用 不到 200 行核心代码,在 C# 类型系统上构建了一个:

的 SQL 查询引擎原型。它展示了 如何将语言特性(表达式树)与领域问题(SQL 生成)优雅结合——这正是现代 C# 高级库的设计精髓。

到此这篇关于C# 类型系统上实现一个 SQL 查询引擎原型的文章就介绍到这了,更多相关C#  SQL 查询引擎内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文