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

如何在EF Core中更新实体属性值并保存解密数据至DbContext

解决EF Core更新解密后实体属性并保存的问题

核心思路

要完成解密后的值更新,关键是利用EF Core的实体元数据定位目标属性,处理类型转换后设置值,同时确保EF Core能跟踪到实体的变更状态。

TableDecrypt类的完整实现

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;
using System.Reflection;

public class TableDecrypt
{
    private readonly DbContext _dbContext;

    public TableDecrypt(DbContext dbContext)
    {
        _dbContext = dbContext;
    }

    // 已实现的获取属性值方法
    public object GetPropertyValue(object entity, string propertyName)
    {
        var propertyInfo = GetEntityProperty(entity.GetType(), propertyName);
        return propertyInfo.GetValue(entity);
    }

    // 完成后的设置属性值方法
    public void SetPropertyValue(object entity, string propertyName, object decryptedValue)
    {
        var propertyInfo = GetEntityProperty(entity.GetType(), propertyName);
        // 处理类型转换:将解密后的值转为属性对应的原始类型
        var convertedValue = Convert.ChangeType(decryptedValue, propertyInfo.PropertyType);
        // 设置实体属性值
        propertyInfo.SetValue(entity, convertedValue);
        
        // 通知EF Core标记属性变更(实体未跟踪时先附加)
        var entry = _dbContext.Entry(entity);
        if (entry.State == EntityState.Detached)
        {
            _dbContext.Attach(entity);
        }
        entry.Property(propertyName).IsModified = true;
    }

    // 从EF Core模型中获取实体属性元数据,避免反射出错
    private PropertyInfo GetEntityProperty(Type entityType, string propertyName)
    {
        var entityTypeMetadata = _dbContext.Model.FindEntityType(entityType);
        var propertyMetadata = entityTypeMetadata?.FindProperty(propertyName);
        
        if (propertyMetadata == null)
        {
            throw new ArgumentException($"实体{entityType.Name}中不存在属性{propertyName}");
        }

        return entityType.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance);
    }

    // 批量处理指定实体类型的解密更新(适配大表分批操作)
    public async Task ProcessEntitiesAsync<TEntity>(List<string> encryptedPropertyNames, Func<object, string, object> decryptFunc) where TEntity : class
    {
        var batchSize = 1000;
        var skip = 0;

        while (true)
        {
            var entities = await _dbContext.Set<TEntity>()
                                           .Skip(skip)
                                           .Take(batchSize)
                                           .ToListAsync();

            if (!entities.Any())
                break;

            foreach (var entity in entities)
            {
                foreach (var propName in encryptedPropertyNames)
                {
                    var encryptedValue = GetPropertyValue(entity, propName);
                    if (encryptedValue != null)
                    {
                        var decryptedValue = decryptFunc(encryptedValue, propName);
                        SetPropertyValue(entity, propName, decryptedValue);
                    }
                }
            }

            await _dbContext.SaveChangesAsync();
            // 清空跟踪器,避免后续批次的变更冲突
            _dbContext.ChangeTracker.Clear();

            skip += batchSize;
        }
    }
}

使用示例

// 假设你的DbContext是LegacyDbContext
using var dbContext = new LegacyDbContext();
var decryptor = new TableDecrypt(dbContext);

// 定义要解密的属性列表和解密逻辑
var encryptedProps = new List<string> { "EncryptedName", "EncryptedAddress" };
Func<object, string, object> decryptLogic = (encryptedVal, propName) => 
{
    // 替换成你已实现的解密方法
    return YourExistingDecryptMethod(encryptedVal.ToString());
};

// 处理Customer表的解密更新
await decryptor.ProcessEntitiesAsync<Customer>(encryptedProps, decryptLogic);

关键注意事项

  • 分批处理:针对大数据表必须分批操作,避免内存溢出。
  • 类型转换:解密后的值多为字符串,需转换为属性对应的原始类型(如int、DateTime),否则会抛出类型不匹配异常。
  • 变更跟踪:查询出来的实体默认被EF Core跟踪,若为手动创建的实体,需先附加到上下文再标记属性修改。
  • 数据一致性:可根据需求在批次保存时添加事务包裹,确保更新操作的原子性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 13:20:06