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 ENDExecuteScalar方法,直接就能拿到计算好的总金额,不需要任何额外实体类。
内容的提问来源于stack exchange,提问作者Tatiana Pasechnik
相关产品推荐
相关产品推荐

