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

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

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

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

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

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

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

【问题讨论】:

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

标签: c# asp.net blazor blazor-server-side


【解决方案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?我认为您显示的代码说明了这一点。
    猜你喜欢
    • 2012-03-20
    • 2018-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-15
    相关资源
    最近更新 更多