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

Blazor应用跨页面维持学校下拉选择状态的实现方案咨询

Blazor 中跨标签页保留学校选中状态的解决方案

针对你的需求,以下是几种可行的实现方案,覆盖不同场景和技术选型:

方案一:使用浏览器本地存储(LocalStorage)

适合 Blazor WebAssembly 和 Server 模式,通过将选中状态存在客户端本地存储,实现跨标签页共享状态。

实现步骤

  1. 安装 Blazored.LocalStorage NuGet 包(封装了本地存储的 JS 互操作逻辑)
  2. 注册服务和本地存储服务
  3. 编写 Scoped 服务管理选中状态,同步本地存储
  4. 在应用初始化时加载状态
  5. 编写下拉组件绑定状态

代码示例

Program.cs 配置

// 添加本地存储服务
builder.Services.AddBlazoredLocalStorage();
// 注册学校选择服务为 Scoped
builder.Services.AddScoped<SchoolSelectionService>();

SchoolSelectionService.cs

public class SchoolSelectionService
{
    private readonly ILocalStorageService _localStorage;
    private readonly AuthenticationStateProvider _authStateProvider;
    private int? _selectedSchoolId;

    public int? SelectedSchoolId
    {
        get => _selectedSchoolId;
        set
        {
            _selectedSchoolId = value;
            // 同步到本地存储
            _ = value.HasValue 
                ? _localStorage.SetItemAsync("SelectedSchoolId", value.Value) 
                : _localStorage.RemoveItemAsync("SelectedSchoolId");
        }
    }

    public SchoolSelectionService(ILocalStorageService localStorage, AuthenticationStateProvider authStateProvider)
    {
        _localStorage = localStorage;
        _authStateProvider = authStateProvider;
    }

    public async Task InitializeAsync()
    {
        var authState = await _authStateProvider.GetAuthenticationStateAsync();
        var user = authState.User;

        // 优先从本地存储读取已选学校
        var savedSchoolId = await _localStorage.GetItemAsync<int?>("SelectedSchoolId");
        if (savedSchoolId.HasValue)
        {
            _selectedSchoolId = savedSchoolId;
            return;
        }

        // 本地存储无数据时,使用用户默认学校(从 Claims 获取)
        if (int.TryParse(user.FindFirst("SchoolId")?.Value, out int defaultSchoolId))
        {
            _selectedSchoolId = defaultSchoolId;
            await _localStorage.SetItemAsync("SelectedSchoolId", defaultSchoolId);
        }
    }
}

App.razor 初始化服务

@inject SchoolSelectionService SchoolService

@code {
    protected override async Task OnInitializedAsync()
    {
        await SchoolService.InitializeAsync();
    }
}

学校下拉组件(SchoolSelector.razor)

@inject SchoolSelectionService SchoolService
@inject AuthenticationStateProvider AuthStateProvider

<select @bind="SelectedSchoolId">
    @foreach (var school in Schools)
    {
        <option value="@school.Id">@school.Name</option>
    }
</select>

@code {
    private List<School> Schools { get; set; } = new();
    private int? SelectedSchoolId
    {
        get => SchoolService.SelectedSchoolId;
        set => SchoolService.SelectedSchoolId = value;
    }

    protected override async Task OnInitializedAsync()
    {
        var authState = await AuthStateProvider.GetAuthenticationStateAsync();
        var user = authState.User;

        // 根据角色加载学校列表
        Schools = user.IsInRole("Admin") 
            ? await GetAllSchoolsAsync() 
            : await GetUserSchoolsAsync(SelectedSchoolId.Value);
    }

    // 模拟数据接口,实际替换为你的API调用
    private Task<List<School>> GetAllSchoolsAsync() => Task.FromResult(new List<School>
    {
        new School { Id = 1, Name = "第一中学" },
        new School { Id = 2, Name = "第二中学" },
        new School { Id = 3, Name = "第三中学" }
    });

    private Task<List<School>> GetUserSchoolsAsync(int schoolId) => Task.FromResult(new List<School>
    {
        new School { Id = schoolId, Name = $"第{schoolId}中学" }
    });

    public class School
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
}

方案二:服务器端 Session(仅 Blazor Server)

如果你使用的是 Blazor Server,可以直接复用 ASP.NET Core 的 Session 机制,和传统 Web Forms 的 Session 逻辑一致,数据存储在服务器端,安全性更高。

实现步骤

  1. 配置 Session 服务
  2. 注册 IHttpContextAccessor(用于访问 Session)
  3. 编写 Scoped 服务管理状态并同步 Session
  4. 初始化服务并绑定组件

代码示例

Program.cs 配置

// 添加 Session 服务
builder.Services.AddSession(options =>
{
    options.IdleTimeout = TimeSpan.FromHours(2);
    options.Cookie.HttpOnly = true;
    options.Cookie.IsEssential = true;
});

// 添加 HttpContext 访问器
builder.Services.AddHttpContextAccessor();
// 注册学校选择服务
builder.Services.AddScoped<SchoolSelectionService>();

// 注意中间件顺序:UseSession 要在 UseRouting 之前
app.UseSession();
app.UseRouting();

SchoolSelectionService.cs

public class SchoolSelectionService
{
    private readonly IHttpContextAccessor _httpContextAccessor;
    private readonly AuthenticationStateProvider _authStateProvider;
    private int? _selectedSchoolId;

    public int? SelectedSchoolId
    {
        get => _selectedSchoolId;
        set
        {
            _selectedSchoolId = value;
            var session = _httpContextAccessor.HttpContext?.Session;
            if (session == null) return;

            // 同步到 Session
            if (value.HasValue)
                session.SetInt32("SelectedSchoolId", value.Value);
            else
                session.Remove("SelectedSchoolId");
        }
    }

    public SchoolSelectionService(IHttpContextAccessor httpContextAccessor, AuthenticationStateProvider authStateProvider)
    {
        _httpContextAccessor = httpContextAccessor;
        _authStateProvider = authStateProvider;
    }

    public async Task InitializeAsync()
    {
        var authState = await _authStateProvider.GetAuthenticationStateAsync();
        var user = authState.User;
        var session = _httpContextAccessor.HttpContext?.Session;

        // 从 Session 读取已选学校
        if (session != null)
        {
            var savedSchoolId = session.GetInt32("SelectedSchoolId");
            if (savedSchoolId.HasValue)
            {
                _selectedSchoolId = savedSchoolId;
                return;
            }
        }

        // 加载用户默认学校
        if (int.TryParse(user.FindFirst("SchoolId")?.Value, out int defaultSchoolId))
        {
            _selectedSchoolId = defaultSchoolId;
            session?.SetInt32("SelectedSchoolId", defaultSchoolId);
        }
    }
}

组件部分与方案一的下拉组件一致,只需确保在 App.razor 初始化服务即可。

方案三:URL 查询参数

将选中的学校 ID 作为 URL 查询参数(如 ?schoolId=1),状态与 URL 绑定,跨标签页打开时自动读取参数恢复状态,还支持分享链接保留当前状态。

代码示例(下拉组件)

@inject NavigationManager NavManager
@inject SchoolSelectionService SchoolService
@inject AuthenticationStateProvider AuthStateProvider

<select @bind="SelectedSchoolId" @onchange="UpdateUrl">
    @foreach (var school in Schools)
    {
        <option value="@school.Id">@school.Name</option>
    }
</select>

@code {
    private List<School> Schools { get; set; } = new();
    private int? SelectedSchoolId { get; set; }

    protected override async Task OnInitializedAsync()
    {
        var authState = await AuthStateProvider.GetAuthenticationStateAsync();
        var user = authState.User;

        // 从 URL 参数读取已选学校
        var uri = new Uri(NavManager.Uri);
        var schoolIdParam = Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(uri.Query).GetValue("schoolId");
        if (int.TryParse(schoolIdParam, out int savedSchoolId))
        {
            SelectedSchoolId = savedSchoolId;
            SchoolService.SelectedSchoolId = savedSchoolId;
        }
        else
        {
            // 使用用户默认学校并更新 URL
            var defaultSchoolId = int.Parse(user.FindFirst("SchoolId").Value);
            SelectedSchoolId = defaultSchoolId;
            SchoolService.SelectedSchoolId = defaultSchoolId;
            UpdateUrl(default);
        }

        // 加载学校列表
        Schools = user.IsInRole("Admin") 
            ? await GetAllSchoolsAsync() 
            : await GetUserSchoolsAsync(SelectedSchoolId.Value);
    }

    private void UpdateUrl(ChangeEventArgs? e = null)
    {
        if (!SelectedSchoolId.HasValue) return;

        // 更新 URL 查询参数,replace: true 避免历史记录堆积
        var newUri = Microsoft.AspNetCore.WebUtilities.QueryHelpers.AddQueryString(
            NavManager.Uri.Split('?')[0], 
            "schoolId", 
            SelectedSchoolId.Value.ToString());
        NavManager.NavigateTo(newUri, replace: true);
    }

    // 模拟数据接口
    private Task<List<School>> GetAllSchoolsAsync() => Task.FromResult(new List<School>
    {
        new School { Id = 1, Name = "第一中学" },
        new School { Id = 2, Name = "第二中学" },
        new School { Id = 3, Name = "第三中学" }
    });

    private Task<List<School>> GetUserSchoolsAsync(int schoolId) => Task.FromResult(new List<School>
    {
        new School { Id = schoolId, Name = $"第{schoolId}中学" }
    });

    public class School
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
}

方案对比

方案适用模式优点缺点
本地存储(LocalStorage)WebAssembly/Server客户端存储,跨标签页共享,无服务器压力数据存在客户端,有篡改风险,不适合存储敏感数据
服务器端 SessionServer 仅数据安全,和 Web Forms 逻辑一致仅支持 Blazor Server,占用服务器资源
URL 查询参数WebAssembly/Server状态与 URL 绑定,支持分享链接暴露学校 ID,URL 会随选择频繁变化

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 10:44:52