目录
在 Blocks 中管理状态(State In Blocks)
全局状态(Global State)
会话状态(Session State)
无法深拷贝对象:按会话隔离实例
浏览器状态(Browser State)
总结
上一篇
Gradio.Net (https://github.com/feiyun0112/Gradio.Net)是一个开源的 .NET 库,它是 Gradio 的 .NET 移植版本,允许你为机器学习模型、API 或任何 C# 函数快速构建演示或 Web 应用程序,无需任何 JavaScript、CSS 或 Web 开发经验!
在 Blocks 中管理状态(State In Blocks)
当你使用gr.Blocks()构建应用时,通常会遇到“状态”问题:
有些值需要在所有用户之间共享(例如访问计数);
有些值只需要在单个用户会话内保留(例如购物车、聊天历史);
有些值希望刷新页面后仍然保留(例如用户名、偏好设置)。
在 Gradio / Gradio.Net 中,常见有三种状态管理方式:
全局状态(Global State):应用运行期间,所有用户共享。
会话状态(Session State):仅对当前用户会话有效,刷新页面会重置。
浏览器状态(Browser State):保存在浏览器
localStorage,关闭/刷新后仍可恢复。
全局状态(Global State)
全局状态最简单:把变量定义在函数外部即可。下面示例里,visitorCount在所有用户之间共享。
using Gradio.Net; using Gradio.Net.Components; using Gradio.Net.Events; var visitorCount = 0; string IncrementCounter() { visitorCount += 1; return visitorCount.ToString(); } using var demo = gr.Blocks(); var number = gr.Textbox(label: "Total Visitors", value: "Counting..."); _ = gr.On( triggers: (List<EventListener>?)null, fn: IncrementCounter, outputs: number ); await demo.Launch();代码
如果你不希望值在不同用户之间共享,请不要放在全局变量里,而应使用会话状态。
会话状态(Session State)
会话状态会在同一页面会话内持续保留,但不会跨用户共享;用户刷新页面后会重置。
使用步骤:
创建
gr.State()(可传默认值)。在事件监听里把该
State作为输入和输出。在函数参数和返回值中处理该状态。
下面是购物车示例:
using Gradio.Net; using Gradio.Net.Components; List<string> AddItems(List<string>? newItems, List<string>? previousCart) { var cart = new List<string>(previousCart ?? new()); if (newItems != null) { cart.AddRange(newItems); } return cart; } double CartSize(List<string>? cart) { return (cart ?? new List<string>()).Count; } using var demo = gr.Blocks(); var cart = gr.State(value: new List<string>()); var itemsToAdd = gr.CheckboxGroup(new[] { "Cereal", "Milk", "Orange Juice", "Water" }); var addButton = gr.Button("Add Items"); _ = addButton.Click( fn: AddItems, inputs: new object[] { itemsToAdd, cart }, outputs: cart ); var cartSize = gr.Number(label: "Cart Size"); _ = cart.Change( fn: CartSize, inputs: cart, outputs: cartSize ); await demo.Launch();你可以把gr.State理解为一个“不可见组件”:前端不直接显示,但可以参与事件计算。
说明:
gr.State的值需要可深拷贝(deepcopy-able)。如果状态对象本身无法深拷贝,请使用“按会话哈希维护全局字典”的方案。
无法深拷贝对象:按会话隔离实例
当对象(例如包含锁)无法深拷贝时,可以读取request.SessionHash,并在服务端用全局字典保存“每个用户会话”的实例。
using System.Collections.Concurrent; using Gradio.Net; using Gradio.Net.Components; using Gradio.Net.Core; using Gradio.Net.Events; var instances = new ConcurrentDictionary<string, NonDeepCopyable>(); string InitializeInstance(Request request) { if (string.IsNullOrWhiteSpace(request.SessionHash)) { return "Error: session hash missing"; } instances[request.SessionHash] = new NonDeepCopyable(); return "Session initialized!"; } double IncrementCounter(Request request) { if (string.IsNullOrWhiteSpace(request.SessionHash)) { return -1; } if (instances.TryGetValue(request.SessionHash, out var instance)) { return instance.Increment(); } return -1; } using var demo = gr.Blocks(); var output = gr.Textbox(label: "Status"); var counter = gr.Number(label: "Counter Value"); var incrementBtn = gr.Button("Increment Counter"); _ = gr.On( triggers: (List<EventListener>?)null, fn: InitializeInstance, outputs: output ); _ = incrementBtn.Click( fn: IncrementCounter, outputs: counter ); await demo.Launch(); class NonDeepCopyable { private readonly object _lockObj = new(); private int _counter; public int Increment() { lock (_lockObj) { _counter += 1; return _counter; } } }浏览器状态(Browser State)
浏览器状态会写入本地localStorage,适合保存用户偏好、账号输入等信息。
using Gradio.Net; using Gradio.Net.Components; using Gradio.Net.Events; var random = new Random(); string GenerateToken(int len) { const string chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; return new string(Enumerable.Range(0, len).Select(_ => chars[random.Next(chars.Length)]).ToArray()); } object[] GenerateRandomly() { var username = GenerateToken(10); var password = GenerateToken(10); return new object[] { username, password }; } object[] LoadFromLocalStorage(object? savedValues) { if (savedValues is System.Collections.IList list && list.Count >= 2) { return new object[] { list[0]?.ToString() ?? string.Empty, list[1]?.ToString() ?? string.Empty }; } return new object[] { string.Empty, string.Empty }; } object SaveToLocalStorage(string username, string password) { return new object[] { username, password }; } Markdown ShowSavedMessage() { var timestamp = DateTime.Now.ToString("hh:mm:ss tt"); return gr.Markdown($"✅ Saved to local storage at {timestamp}", visible: true); } using var demo = gr.Blocks(); _ = gr.Markdown("Your Username and Password will get saved in the browser's local storage. If you refresh the page, the values will be retained."); var username = gr.Textbox(label: "Username"); var password = gr.Textbox(label: "Password", type: "password"); var btn = gr.Button("Generate Randomly"); var localStorage = gr.BrowserState(defaultValue: new[] { "", "" }); var savedMessage = gr.Markdown("✅ Saved to local storage", visible: false); _ = btn.Click( fn: GenerateRandomly, outputs: new object[] { username, password } ); _ = gr.On( triggers: (List<EventListener>?)null, fn: LoadFromLocalStorage, inputs: localStorage, outputs: new object[] { username, password } ); _ = username.Change( fn: SaveToLocalStorage, inputs: new object[] { username, password }, outputs: localStorage ); _ = password.Change( fn: SaveToLocalStorage, inputs: new object[] { username, password }, outputs: localStorage ); _ = localStorage.Change( fn: ShowSavedMessage, outputs: savedMessage ); await demo.Launch();注意:
gr.BrowserState的值保存在浏览器中。若 Gradio 应用重启,默认随机storage_key/secret会变化,历史值可能无法继续读取。要跨重启读取,请固定storage_key与secret,并确保在可信应用场景中使用。
总结
这一章你学会了三种状态管理方式:
全局状态:适合跨用户共享的运行期数据。
会话状态:适合单用户会话内的临时数据。
浏览器状态:适合需要在刷新后保留的前端数据。
引入地址