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

Razor Pages中存储过程返回Null值问题求助

存储过程在Razor Pages调用返回Null的排查解决方案

问题背景

现有两个SQL Server存储过程AvailableHourSlots和AvailableHalfAnHourSlots,在SQL Server Management Studio(SSMS)中执行可正常返回结果,但在Razor Pages中通过EF Core调用时返回Null值。相关代码如下:

存储过程代码

AvailableHourSlots

CREATE PROCEDURE [dbo].[AvailableHourSlots] 
    @ActivityName nvarchar(max) ,
    @BookedDate datetime2(7)
AS  
    SELECT DISTINCT HourlyBasedTime 
    FROM ManageBooking AS mb, HourlyBased AS hb  
    WHERE bookingdate = @BookedDate 
      AND ActivityName = @ActivityName
      AND (SUBSTRING(HourlyBasedTime, 1, 2) <> SUBSTRING(mb.PreferredTimeslot, 1, 2))

AvailableHalfAnHourSlots

CREATE PROCEDURE [dbo].[AvailableHalfAnHourSlots] 
    @ActivityName nvarchar(max) ,
    @BookedDate datetime2(7)
AS  
    SELECT DISTINCT hh.halfanhourtime
    FROM ManageBooking AS mb, Halfanhour hh
    WHERE bookingdate = @BookedDate
      AND ActivityName = @ActivityName 
      AND (SUBSTRING(hh.halfanhourtime, 1, 5) <> SUBSTRING(mb.PreferredTimeslot, 1, 5))
      AND (RIGHT(hh.halfanhourtime, 5) <> RIGHT(mb.PreferredTimeslot, 5))

实体模型

namespace ActivityBookingSystem.Models
{
    public class HourlyBasedView
    {
        public string HourlyBasedTime { get; set; }   
    }
}

EF Core上下文配置

protected override void OnModelCreating(ModelBuilder modelBuilder)
{            
    modelBuilder.Entity<NonOperatingDaysView>().HasNoKey();
    modelBuilder.Entity<AvailableOneHourSlots>().HasNoKey();
    modelBuilder.Entity<HourlyBasedView>().HasNoKey();
}

public DbSet<ActivityBookingSystem.Models.HourlyBasedView> HourlyBasedView { get; set; }

前端AJAX调用

function GetAvailableSlots(e) {
        var rootPath = '@Url.Content("~")';
        var ActivityList = document.getElementById("DrpDwnActivityList");
        var ActivityListValue = ActivityList.options[ActivityList.selectedIndex].value;
        var BookedDate = document.getElementById("DateBookedDate").value;
        
        $.ajax({
            type: "Get",
            url: rootPath + "/ManageBooking/Create?handler=GetAvailableSlots",
            headers: { "RequestVerificationToken": $('input[name="__RequestVerificationToken"]').val() },
            data: { Duration: e.value,ActivityName:ActivityListValue,BookedDate:BookedDate },
            dataType: "json",
            success: function (result) {
                alert(result);
            },
            error: function (data) {
                alert(data);
            }
        });
    }

后端Razor Page处理代码(修改后)

public async Task<JsonResult> OnGetGetAvailableSlotsAsync(string ActivityName, string BookedDate, string Duration)
{
    Console.WriteLine(Duration);
    DateTime BookingDate = Convert.ToDateTime(BookedDate);
    ActivityName = ActivityName.ToString();
            
    if (Duration == "1 Hour")
    {
        var LstPreferredTimeSlot = _context.HourlyBasedView.FromSqlRaw("EXEC dbo.AvailableHourSlots @BookedDate = {0}, @ActivityName = {1}", BookingDate, ActivityName)
                    .AsNoTracking().ToList();
    }

    return new JsonResult(LstPreferredTimeSlot);
}

排查与解决步骤

1. 修复变量作用域问题

当前代码中,LstPreferredTimeSlot仅在if块内部声明,外部return语句无法访问该变量,会导致编译错误或返回未初始化的Null。需将变量声明移至if块外部:

public async Task<JsonResult> OnGetGetAvailableSlotsAsync(string ActivityName, string BookedDate, string Duration)
{
    Console.WriteLine(Duration);
    // 提前声明并初始化变量,避免返回Null
    var LstPreferredTimeSlot = new List<HourlyBasedView>();
    
    // 安全转换日期,处理格式错误情况
    if (!DateTime.TryParse(BookedDate, out DateTime BookingDate))
    {
        return new JsonResult(new { success = false, message = "日期格式无效" });
    }
    
    // 校验活动名称非空
    if (string.IsNullOrEmpty(ActivityName))
    {
        return new JsonResult(new { success = false, message = "活动名称不能为空" });
    }
            
    if (Duration == "1 Hour")
    {
        // 匹配存储过程参数顺序,避免绑定错误
        LstPreferredTimeSlot = _context.HourlyBasedView
            .FromSqlRaw("EXEC dbo.AvailableHourSlots @ActivityName = {0}, @BookedDate = {1}", ActivityName, BookingDate)
            .AsNoTracking()
            .ToList();
    }
    // 新增半小时时段的处理分支
    else if (Duration == "30 Minutes")
    {
        // 调用AvailableHalfAnHourSlots的逻辑
        // LstPreferredTimeSlot = _context.xxx.FromSqlRaw(...).ToList();
    }

    return new JsonResult(LstPreferredTimeSlot);
}

2. 匹配存储过程参数顺序与命名

存储过程AvailableHourSlots的参数定义顺序为@ActivityName在前、@BookedDate在后,即使使用命名参数,建议保持传递顺序与定义一致,避免潜在的参数绑定错误。

3. 验证日期转换的正确性

前端传入的日期字符串可能存在格式差异,使用DateTime.TryParse或DateTime.TryParseExact进行安全转换,避免转换失败导致参数错误:

if (!DateTime.TryParse(BookedDate, System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out DateTime BookingDate))
{
    return new JsonResult(new { success = false, message = "请输入有效的日期格式" });
}

4. 确认EF Core无键实体映射

确保实体类HourlyBasedView的属性名与存储过程返回的列名完全匹配,可显式添加[Column]属性强制映射:

using System.ComponentModel.DataAnnotations.Schema;

namespace ActivityBookingSystem.Models
{
    public class HourlyBasedView
    {
        [Column("HourlyBasedTime")]
        public string HourlyBasedTime { get; set; }   
    }
}

5. 启用EF Core日志查看执行SQL

在Program.cs中添加日志配置,查看实际执行的SQL语句和参数值,对比SSMS中的测试语句是否一致:

builder.Logging.AddFilter("Microsoft.EntityFrameworkCore.Database.Command", Microsoft.Extensions.Logging.LogLevel.Information);

日志会输出完整的执行SQL,可直接复制到SSMS中运行,验证是否返回结果。

6. 校验前端参数传递正确性

在后端添加参数日志输出,确认前端传递的值与SSMS测试值一致:

Console.WriteLine($"接收参数:Duration={Duration}, ActivityName={ActivityName}, BookedDate={BookedDate}");

7. 优化存储过程逻辑

原存储过程使用隐式连接,可能在无匹配预订记录时返回空。建议改用LEFT JOIN改写,确保无预订时也能返回所有可用时段:

CREATE PROCEDURE [dbo].[AvailableHourSlots] 
    @ActivityName nvarchar(max) ,
    @BookedDate datetime2(7)
AS  
    SELECT DISTINCT hb.HourlyBasedTime 
    FROM HourlyBased AS hb
    LEFT JOIN ManageBooking AS mb 
        ON SUBSTRING(hb.HourlyBasedTime, 1, 2) = SUBSTRING(mb.PreferredTimeslot, 1, 2)
        AND mb.bookingdate = @BookedDate 
        AND mb.ActivityName = @ActivityName
    WHERE mb.PreferredTimeslot IS NULL

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 13:10:24