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

EF Core 8预览版调用存储过程:仅返回部分列报错的解决诉求

问题场景

在.NET 6 API中使用EF Core 8预览版通过原生SQL调用存储过程时,若存储过程返回的列包含模型所有带公共setter的属性,程序运行正常;但如果仅返回部分列却复用同一模型,会抛出错误:

The required column 'colname' was not present in the results of a 'FromSql' operation.

当前调用存储过程的实现代码如下:

public IEnumerable<T> ExecStoredProc<T>(string procName, IDictionary<string, object> parameters)
{
    try
    {
        var inputParameter = new List<SqlParameter>(parameters.Count);

        foreach (var parameter in parameters)
        {
            inputParameter.Add(new SqlParameter
            {
                ParameterName = parameter.Key,
                Value = parameter.Value ?? DBNull.Value
            });
        }

        string procWithParameter = string.Format("exec {0} {1}", procName, string.Join(", ", inputParameter.Select(p => p.ParameterName)));

        var result = _dbContext.Database.SqlQueryRaw<T>(procWithParameter, inputParameter.ToArray());

        return result.ToList();
    }
    catch (System.Exception ex)
    {
        throw ex;
    }
}
解决方案

方案1:将可选属性设为可空类型

把模型中允许缺失的属性改为可空值类型(值类型加?,引用类型默认支持空值),EF Core会自动忽略缺失的列,并将对应属性设为null。

示例模型修改:

public class YourModel
{
    // 必须返回的列,保持非可空
    public int Id { get; set; }
    // 允许缺失的列,改为可空类型
    public string? Description { get; set; }
    public int? Age { get; set; }
}

方案2:使用无实体类型(EF Core 7+支持)

定义一个仅包含存储过程返回列的无实体类,规避原模型的字段约束。

  1. 创建无实体模型类:
public class PartialProcResult
{
    public int Id { get; set; }
    public string Name { get; set; }
    // 仅包含存储过程实际返回的列
}
  1. 在DbContext中配置该类为无实体类型:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<PartialProcResult>().HasNoKey();
}
  1. 调用存储过程时指定该类型:
var result = _dbContext.Database.SqlQueryRaw<PartialProcResult>(procWithParameter, inputParameter.ToArray());

方案3:手动读取DataReader映射结果

直接通过DbDataReader读取数据,手动映射到目标模型,跳过缺失的列。修改原方法如下:

public IEnumerable<T> ExecStoredProc<T>(string procName, IDictionary<string, object> parameters) where T : new()
{
    try
    {
        var inputParameter = new List<SqlParameter>(parameters.Count);
        foreach (var parameter in parameters)
        {
            inputParameter.Add(new SqlParameter
            {
                ParameterName = parameter.Key,
                Value = parameter.Value ?? DBNull.Value
            });
        }

        using var command = _dbContext.Database.GetDbConnection().CreateCommand();
        command.CommandText = $"exec {procName} {string.Join(", ", inputParameter.Select(p => p.ParameterName))}";
        command.Parameters.AddRange(inputParameter.ToArray());

        _dbContext.Database.OpenConnection();
        using var reader = command.ExecuteReader();

        var results = new List<T>();
        var writableProps = typeof(T).GetProperties().Where(p => p.CanWrite).ToList();

        while (reader.Read())
        {
            var item = new T();
            foreach (var prop in writableProps)
            {
                try
                {
                    var ordinal = reader.GetOrdinal(prop.Name);
                    if (!reader.IsDBNull(ordinal))
                    {
                        var value = reader.GetValue(ordinal);
                        prop.SetValue(item, value);
                    }
                }
                catch (IndexOutOfRangeException)
                {
                    // 列缺失时跳过,保持属性默认值
                    continue;
                }
            }
            results.Add(item);
        }

        return results;
    }
    catch (Exception ex)
    {
        throw ex;
    }
    finally
    {
        if (_dbContext.Database.GetDbConnection().State == System.Data.ConnectionState.Open)
        {
            _dbContext.Database.CloseConnection();
        }
    }
}

该方法通过反射获取可写属性,遍历DataReader时跳过不存在的列,无需修改原模型结构。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 15:50:39