如何在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
相关产品推荐
相关产品推荐

