.NET6升级.NET8后EF Core 8缺失IRelationalValueBufferFactoryFactory接口问题
解决方案:EF Core 8下存储过程实体化的替代实现
EF Core 8移除了IRelationalValueBufferFactoryFactory和LegacyTypeMaterializationInfo等底层API,推荐使用官方原生API替代,以下是三种可行方案:
方案1:针对已注册的实体类型(DbSet)
如果返回类型是已在DbContext中注册的实体,直接使用FromSql方法,自动完成实体化和状态跟踪:
public async Task<IEnumerable<T>> ExecuteStoredProcedureAsync<T>(string procName, params SqlParameter[] parameters) where T : class { var dbSet = _dbContext.Set<T>(); var parameterPlaceholders = string.Join(", ", parameters.Select(p => $"@{p.ParameterName}")); var query = dbSet.FromSql($"EXEC {procName} {parameterPlaceholders}", parameters); // 需要跟踪实体状态直接返回ToListAsync,不需要则添加.AsNoTracking() return await query.ToListAsync(); }
方案2:针对非实体DTO类型
如果返回结果是自定义DTO(未在DbContext中注册),使用EF Core 8新增的SqlQuery方法:
public async Task<IEnumerable<T>> ExecuteStoredProcedureForDtoAsync<T>(string procName, params SqlParameter[] parameters) { var parameterPlaceholders = string.Join(", ", parameters.Select(p => $"@{p.ParameterName}")); var query = _dbContext.Database.SqlQuery<T>($"EXEC {procName} {parameterPlaceholders}", parameters); return await query.ToListAsync(); }
方案3:保留手动实体化逻辑的替代实现
如果必须保留类似原代码的手动控制流程,可使用RelationalDataReader替代原有逻辑,无需依赖已移除的接口:
public class StoredProcedureExecutor { private readonly DbContext _dbContext; private readonly IEntityMaterializerSource _materializerSource; private readonly IStateManager _stateManager; private readonly DbCommand _command; public StoredProcedureExecutor(DbContext dbContext, string procName) { _dbContext = dbContext; _materializerSource = dbContext.GetService<IEntityMaterializerSource>(); _stateManager = dbContext.GetService<IStateManager>(); _command = dbContext.Database.GetDbConnection().CreateCommand(); _command.CommandText = procName; _command.CommandType = CommandType.StoredProcedure; } public async Task<IEnumerable<T>> ReadResultAsync<T>(Type type) { var entityType = _dbContext.Model.FindEntityType(type); if (entityType == null) throw new InvalidOperationException($"Type {type.Name} is not registered as an entity in the DbContext."); var materializer = _materializerSource.GetMaterializer(entityType); var entities = new List<T>(); await _dbContext.Database.OpenConnectionAsync(); using var reader = await _command.ExecuteReaderAsync(); var relationalReader = new RelationalDataReader( _dbContext.GetService<IRelationalConnection>(), reader, _dbContext.GetService<IRelationalTypeMappingSource>() ); while (await relationalReader.ReadAsync()) { var valueBuffer = relationalReader.GetValueBuffer(); var entity = (T)materializer(new MaterializationContext(valueBuffer, _dbContext)); if (entityType.GetKeys().Any()) { _stateManager.StartTrackingFromQuery(entityType, entity, valueBuffer); } entities.Add(entity); } return entities; } }
关键说明
RelationalDataReader已内置列映射、类型转换逻辑,替代了原代码中手动构造LegacyTypeMaterializationInfo和valueBufferFactory的流程- 优先选择方案1和方案2,这是EF Core官方推荐的实现方式,兼容性更好、代码更简洁
- 方案3仅适用于需要精细控制实体化流程的场景,尽量避免直接操作底层API以保证后续版本兼容性
内容的提问来源于stack exchange,提问作者Mukul Garg
相关产品推荐
相关产品推荐

