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

.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;
    }
}

关键说明

  1. RelationalDataReader已内置列映射、类型转换逻辑,替代了原代码中手动构造LegacyTypeMaterializationInfo和valueBufferFactory的流程
  2. 优先选择方案1和方案2,这是EF Core官方推荐的实现方式,兼容性更好、代码更简洁
  3. 方案3仅适用于需要精细控制实体化流程的场景,尽量避免直接操作底层API以保证后续版本兼容性

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 10:35:07