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

EF Core 6如何禁止查询特定Always Encrypted加密列?

EF Core 6 拦截查询并检测加密列抛出异常的实现方案

可以通过EF Core的查询拦截器实现需求,在查询发送到数据库前检测是否包含加密列,直接抛出异常阻止执行。以下是具体实现步骤:

1. 标记加密列(两种方式选其一)

方式一:自定义特性标记

创建特性类标记实体中的加密属性:

[AttributeUsage(AttributeTargets.Property)]
public class EncryptedColumnAttribute : Attribute
{
}

在实体类的加密属性上添加标记:

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    
    [EncryptedColumn]
    public string SSN { get; set; } // Always Encrypted加密列
}

方式二:通过EF模型注解标记

在OnModelCreating中配置加密列时添加自定义注解:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    // 配置Always Encrypted列
    modelBuilder.Entity<User>()
        .Property(u => u.SSN)
        .HasColumnType("nvarchar(11) ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = CEK_Auto1, ENCRYPTION_TYPE = Deterministic, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256')")
        .AddAnnotation("IsEncrypted", true); // 添加加密标记注解
}

2. 实现查询表达式拦截器

继承IQueryExpressionInterceptor,通过遍历查询表达式树检测是否引用加密列:

public class EncryptedColumnQueryInterceptor : IQueryExpressionInterceptor
{
    private readonly IModel _model;

    public EncryptedColumnQueryInterceptor(IModel model)
    {
        _model = model;
    }

    public Expression QueryCompilationStarting(Expression queryExpression, QueryExpressionEventData eventData)
    {
        var visitor = new EncryptedColumnChecker(_model);
        visitor.Visit(queryExpression);
        
        if (visitor.DetectedEncryptedColumn)
        {
            throw new InvalidOperationException("查询包含加密列,禁止执行此类操作。");
        }

        return queryExpression;
    }

    private class EncryptedColumnChecker : ExpressionVisitor
    {
        private readonly IModel _model;
        public bool DetectedEncryptedColumn { get; private set; }

        public EncryptedColumnChecker(IModel model)
        {
            _model = model;
        }

        protected override Expression VisitMember(MemberExpression node)
        {
            if (node.Member is PropertyInfo property && node.Member.DeclaringType != null)
            {
                // 检查特性标记(对应方式一)
                if (Attribute.IsDefined(property, typeof(EncryptedColumnAttribute)))
                {
                    DetectedEncryptedColumn = true;
                    return base.VisitMember(node);
                }

                // 检查模型注解标记(对应方式二)
                var entityType = _model.FindEntityType(node.Member.DeclaringType);
                var propertyEntry = entityType?.FindProperty(property);
                if (propertyEntry?.FindAnnotation("IsEncrypted")?.Value is true)
                {
                    DetectedEncryptedColumn = true;
                }
            }

            return base.VisitMember(node);
        }
    }
}

3. 注册拦截器到DbContext

在OnConfiguring方法中添加拦截器:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder
        .UseSqlServer("你的数据库连接字符串")
        .AddInterceptors(new EncryptedColumnQueryInterceptor(this.Model));
}

说明

  • 该方案在查询编译阶段检测,比拦截SQL命令更可靠,避免了别名、同名列导致的误判。
  • 一旦检测到查询引用加密列,会直接抛出InvalidOperationException,阻止查询发送到数据库。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 04:15:43