You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

GraphQL-NetCore:覆写Where子句参数构建合法SQL表达式

在GraphQL .NET Core中改写加密用户ID的过滤表达式

核心思路

要实现拦截并改写IQueryable的表达式树,核心是通过ExpressionVisitor遍历并替换过滤条件,同时让EF Core能识别解密逻辑并生成合法SQL。具体分三步:注册数据库解密函数、编写表达式树改写器、在GraphQL查询或EF Core全局拦截中应用。

步骤1:注册数据库解密函数到EF Core

首先要让EF Core知道解密逻辑对应的数据库函数,这样才能把C#中的解密方法调用转换成SQL语句。假设数据库中有一个名为DecryptValue的解密函数(比如SQL Server的自定义CLR函数或内置解密函数):

// 静态辅助类,仅用于EF Core的SQL转换标记,不会在内存执行
public static class EncryptionHelper
{
    public static string Decrypt(string encryptedValue)
    {
        throw new NotImplementedException("仅用于EF Core生成SQL,请勿直接调用");
    }
}

// 在DbContext的OnModelCreating中注册函数映射
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.HasDbFunction(typeof(EncryptionHelper).GetMethod(nameof(EncryptionHelper.Decrypt))!)
        .HasName("DecryptValue"); // 对应数据库中的解密函数名称
}

步骤2:编写表达式树访问器

继承ExpressionVisitor,定位到EncryptedUserId == 参数的条件,将其替换为UserId == Decrypt(参数):

public class EncryptedUserIdRewriter : ExpressionVisitor
{
    private readonly ParameterExpression _entityParam;

    public EncryptedUserIdRewriter(ParameterExpression entityParam)
    {
        _entityParam = entityParam;
    }

    protected override Expression VisitBinary(BinaryExpression node)
    {
        // 匹配"实体.EncryptedUserId == 加密值参数"的相等条件
        if (node.NodeType == ExpressionType.Equal &&
            node.Left is MemberExpression leftMember &&
            leftMember.Member.Name == nameof(YourEntity.EncryptedUserId) &&
            leftMember.Expression == _entityParam)
        {
            // 替换为"实体.UserId == Decrypt(加密值参数)"
            var userIdProperty = Expression.Property(_entityParam, nameof(YourEntity.UserId));
            var decryptCall = Expression.Call(
                typeof(EncryptionHelper).GetMethod(nameof(EncryptionHelper.Decrypt))!,
                node.Right);
            
            return Expression.Equal(userIdProperty, decryptCall);
        }

        return base.VisitBinary(node);
    }
}

步骤3:在GraphQL查询中应用改写器

以GraphQL.NET的Resolve方法为例,处理客户端传入的encryptedUserId参数时,改写过滤表达式:

public class YourQuery : ObjectGraphType
{
    public YourQuery(YourDbContext dbContext)
    {
        Field<ListGraphType<YourEntityGraphType>>("entities")
            .Argument<StringGraphType>("encryptedUserId")
            .Resolve(context =>
            {
                var encryptedUserId = context.GetArgument<string>("encryptedUserId");
                var query = dbContext.YourEntities.AsQueryable();

                if (!string.IsNullOrWhiteSpace(encryptedUserId))
                {
                    // 构建原始过滤表达式
                    Expression<Func<YourEntity, bool>> originalFilter = e => e.EncryptedUserId == encryptedUserId;
                    // 改写表达式
                    var rewriter = new EncryptedUserIdRewriter(originalFilter.Parameters[0]);
                    var rewrittenBody = rewriter.Visit(originalFilter.Body);
                    var rewrittenFilter = Expression.Lambda<Func<YourEntity, bool>>(rewrittenBody, originalFilter.Parameters);

                    // 应用改写后的过滤条件
                    query = query.Where(rewrittenFilter);
                }

                return query;
            });
    }
}

进阶:全局EF Core查询拦截

如果要让所有涉及EncryptedUserId的过滤都自动改写,可以用EF Core的查询拦截器,无需在每个GraphQL Resolve中单独处理:

public class EncryptedQueryInterceptor : IQueryExpressionInterceptor
{
    public Expression QueryCompilationStarting(Expression queryExpression, QueryExpressionEventData eventData)
    {
        // 自定义一个能遍历所有参数的访问器(可基于上面的改写器调整)
        var globalRewriter = new GlobalEncryptedUserIdRewriter();
        return globalRewriter.Visit(queryExpression);
    }
}

// 在Program.cs注册拦截器
builder.Services.AddDbContext<YourDbContext>(options =>
    options.UseSqlServer("你的连接字符串")
           .AddInterceptors(new EncryptedQueryInterceptor()));

注意事项

  • 确保数据库中的解密函数与EF注册的函数名、参数完全匹配,否则会生成无效SQL。
  • 如果使用GraphQL的通用过滤输入(比如FilterInput),需要在解析过滤表达式的环节注入改写逻辑(比如自定义过滤提供者)。
  • 若加密逻辑需要密钥,要确保数据库函数能正确获取密钥(比如SQL Server的OPEN SYMMETRIC KEY)。

内容的提问来源于stack exchange,提问作者m1ha1

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 12:31:24