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

WPF C#调用SQL Server存储过程报@start标量变量未声明错误

问题原因

你遇到的必须声明标量变量"@start"错误,核心原因是EF Core FromSqlRaw 的参数占位符规则不匹配:

  • 你直接在SQL字符串里写@start、@end,但EF Core默认不会把这类自定义名称和你后续传入的参数值做绑定,它只会识别按传入顺序编号的@p0、@p1、@p2格式占位符。最终发给SQL Server的执行语句里@start和@end是未定义的变量,直接触发报错。
  • 你手动在SSMS里执行存储过程能跑通,是因为手动执行时你明确给这三个参数都赋了值,不存在参数未声明的问题。
修复方案

方案1:调整FromSqlRaw占位符为顺序编号(改动最小)

直接把SQL语句里的自定义参数名换成对应顺序的@p编号即可,建议加上EXEC关键字明确是执行存储过程:

public static List<Total> getTotalPriceForSelectedTruckModel( string model, DateTime start, DateTime end)
{
    using (DAD_TatianaContext ctx = new DAD_TatianaContext())
    {               
        // 三个参数按传入顺序依次对应@p0(model)、@p1(start)、@p2(end)
        return ctx.Total.FromSqlRaw("EXEC getTotalRentForTruckModel @p0, @p1, @p2", model, start, end).ToList();
    }
}

方案2:显式传入SqlParameter对象(支持自定义参数名)

如果你想保留存储过程里定义的@start、@end参数名写法,就不要直接传原生值,而是显式构造SqlParameter对象传入,EF Core会自动按参数名匹配绑定:

public static List<Total> getTotalPriceForSelectedTruckModel( string model, DateTime start, DateTime end)
{
    using (DAD_TatianaContext ctx = new DAD_TatianaContext())
    {
        var parameters = new[]
        {
            new SqlParameter("@p0", model),
            new SqlParameter("@start", start),
            new SqlParameter("@end", end)
        };
        return ctx.Total.FromSqlRaw("EXEC getTotalRentForTruckModel @p0, @start, @end", parameters).ToList();
    }
}
无需映射实体类直接获取返回值的方法

完全可以不创建Total这类映射实体,根据你的返回需求选对应实现即可:

  • 如果只需要单个计算值(比如总租金金额):直接用ADO.NET原生的ExecuteScalar方法,是最轻量的实现,不需要任何ORM映射配置:
    public static decimal GetTotalRentWithoutEntity(string model, DateTime start, DateTime end)
    {
        using (var ctx = new DAD_TatianaContext())
        using (var cmd = ctx.Database.GetDbConnection().CreateCommand())
        {
            cmd.CommandText = "getTotalRentForTruckModel";
            cmd.CommandType = System.Data.CommandType.StoredProcedure;
            // 按存储过程定义添加参数
            cmd.Parameters.Add(new SqlParameter("@p0", model));
            cmd.Parameters.Add(new SqlParameter("@start", start));
            cmd.Parameters.Add(new SqlParameter("@end", end));
    
            ctx.Database.OpenConnection();
            // ExecuteScalar直接返回查询结果第一行第一列的值
            var result = cmd.ExecuteScalar();
            return result == DBNull.Value ? 0m : Convert.ToDecimal(result);
        }
    }
    
  • 如果你不想建实体但需要读取多列/多行结果,可以用SqlDataReader逐行读取,或者用EF Core的无键实体、动态类型映射,不过对于你当前只需要总租金的场景,上面的ExecuteScalar方案足够用。
  • 额外优化:你可以把现有存储过程简化为直接返回总租金标量,不需要返回ModelID、做分组(只要车型名和ModelID是一对一对应关系,分组完全多余),简化后的存储过程:
    ALTER PROCEDURE [dbo].[getTotalRentForTruckModel] 
        @p0 varchar(50),
        @start date,
        @end date
    AS
    BEGIN
        SET NOCOUNT ON;
        SELECT ISNULL(SUM([TruckRental-PB].TotalPrice), 0)
        FROM
            [TruckRental-PB]
        INNER JOIN 
            [IndividualTruck-PB] ON [TruckRental-PB].TruckID = [IndividualTruck-PB].TruckID 
        INNER JOIN 
            [TruckModel-PB] ON [IndividualTruck-PB].TruckModelID = [TruckModel-PB].ModelID
        WHERE
            [TruckModel-PB].Model = @p0 
            AND [TruckRental-PB].RentDate >= @start 
            AND [TruckRental-PB].ReturnDate <= @end
    END
    
    改完后配合上面的ExecuteScalar方法,直接就能拿到计算好的总金额,不需要任何额外实体类。

内容的提问来源于stack exchange,提问作者Tatiana Pasechnik

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 02:54:20