Blazor Server 如何跨多个选项卡和刷新保存数据

Posted

技术标签:

【中文标题】Blazor Server 如何跨多个选项卡和刷新保存数据【英文标题】:Blazor Server How to persist data across multiple tabs and refreshes 【发布时间】:2021-08-28 11:51:22 【问题描述】:

我正在编写一个需要为用户保存数据的 Blazor Server 应用程序。

我试过以下/以下不符合要求:

会话存储 - 因为它的范围是浏览器选项卡,所以数据会在刷新时消失/而不是在新选项卡上。 本地存储 - 跨多个选项卡和刷新工作,但保留以供将来访问该站点(我不希望数据在多次访问中保留) AppState 方法的范围 - 再次基于每个选项卡的每个电路。

我有一些想法,但不确定如何实施/是否是好想法:

使用本地存储,但要么在客户端断开连接时以某种方式清除它,要么在本地存储中添加时间标签,只允许 x 时间的持久性。 可能通过以下方式使用 cookie:Creating and Reading Cookies on Blazor Server Side

除此之外,我对如何实现这一点没有任何其他好的想法,因此欢迎任何想法/建议。

【问题讨论】:

使用作用域依赖注入来保存每个电路的状态。 这适用于每个电路,但据我了解,每个选项卡都有一个新的/不同的电路,刷新也可以让你获得一个新的电路——这两个我都需要坚持。 什么是短暂的? Transient 将使其成为对服务器的每个不满足用例的请求的新服务。 如果您有用户身份,那么您可以将数据与数据库(或类似数据库)中的用户相关联,并在任何选项卡甚至不同浏览器上查找。 【参考方案1】:

嗯,这不是我原创的,但这是我为登录用户保留租户的方式,只要他们处于连接状态。

public class GlobalService

    public event Action<PropertyChangedEventArgs> PropertyChanged;

    Subscriber _Tenant;
    public Subscriber Tenant
    
        get
        
            return _Tenant;
        
        set
        
            if (!object.Equals(_Tenant, value))
            
                var args = new PropertyChangedEventArgs()  Name = "Tenant", NewValue = value, OldValue = _Tenant, IsGlobal = true ;
                _Tenant = value;
                PropertyChanged?.Invoke(args);
            
        
    


public class PropertyChangedEventArgs

    public string Name  get; set; 
    public object NewValue  get; set; 
    public object OldValue  get; set; 
    public bool IsGlobal  get; set; 

我像这样在 ConfigureServices 中注册它

services.TryAddScoped<GlobalService>();

【讨论】:

【参考方案2】:

一种可能的方法是模仿 ASP.net 网络表单的遗留会话变量。这些依赖于没有过期日期的 cookie。 cookie 的生命周期也等于浏览器会话的生命周期。 主要工作是在 _Host.cshtml 中完成,我们将在其中创建一个用作会话标识符的 guid。此 guid 对整个浏览器会话(所有选项卡)都有效。

创建此属性:public Guid NewGuid get; = Guid.NewGuid(); 注入 HttpContextAccessor : public HostModel(IHttpContextAccessor httpContextAccessor) 获取cookie值:
string g = httpContextAccessor.HttpContext.Request.Cookies["SessionGuid"] ?? "";
if (Guid.TryParse(g, out Guid guid))
    request.SessionGuid = guid;
else
    request.SessionGuid = NewGuid

在此代码中,request 是一个级联参数,将传递给所有组件。

在标记中,您将其添加到正文中 &lt;script&gt;createSessionGuid('@Model.NewGuid');&lt;/script&gt;

这使用了这个必须加载的小javascript函数:

function createSessionGuid(newguid) 
    var current = getCookie("SessionGuid");
    if ((current === null) || (current === "")) 
        var guid = newguid;
        document.cookie = "SessionGuid=" + guid + "; path=/";
        console.log("SessionGuid created : " + guid);
    
    else 
        console.log("SessionGuid found : " + current);
    

通过这种方式,您无需使用 JSInterop 即可获得会话 ID,因此无需使用任何异步代码。

故事的其余部分非常简单:只需创建一个会话类,它是一个字典字典(并发字典)。***字典中的键应该是会话 ID。二级字典中的键就是会话变量名。

编辑:如何在_Host.cshtml中使用CascadingValue?

在_Host.cshtml中:见最后一个参数。

<component type="typeof(App)" render-mode="ServerPrerendered" param-Request="Model.request" />

在 _Host.cshtml 的代码隐藏中

public BaseClasses.Request request;
public HostModel(IHttpContextAccessor httpContextAccessor)

    request = new BaseClasses.Request();
    httpContextAccessor.HttpContext.Request.Cookies.Where(kvp => kvp.Key != "SessionGuid").ToList().ForEach(kvp => request.Cookies.Add(kvp.Key, kvp.Value));
    string g = httpContextAccessor.HttpContext.Request.Cookies["SessionGuid"] ?? "";
    if (Guid.TryParse(g, out Guid guid))
        request.SessionGuid = guid;
    else
        request.SessionGuid = NewGuid;

此信息在 MainLayout.razor.cs 中检索并开始级联。

[CascadingParameter]
private BaseClasses.Request Request  get; set; 

【讨论】:

如何在_Host.cshtml或其后面的代码中使用CascadingValue?我认为您显示的代码说明了这一点。

以上是关于Blazor Server 如何跨多个选项卡和刷新保存数据的主要内容,如果未能解决你的问题,请参考以下文章

如何使用同一 Blazor 组件的多个实例

在刷新页面 MVC 上丢失的 JavaScript 中动态创建的内容

使用 glade 在每个选项卡上设置一个带有选项卡和多个 gtk 输入字段的笔记本

如何从服务器端 Blazor 应用程序中的 Blazor 组件调用 razor 页面而不导致页面刷新

如何更改刷新 Blazor PWA 应用程序时出现的“正在授权...”消息?

如何以编程方式更改 Vuetify 选项卡和 vue-router