首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Blazor Server:范围服务的问题

Blazor Server:范围服务的问题
EN

Stack Overflow用户
提问于 2021-03-22 18:52:22
回答 2查看 1.4K关注 0票数 1

在尝试使用在_Host.cshtml文件中初始化的范围服务时,我遇到了一种奇怪的行为。这是一个非常简单的服务:

代码语言:javascript
复制
public interface IUserSessionService
{
    void SetUserData(UserSessionData userData);
    UserSessionData GetUserData();
}
public class UserSessionService : IUserSessionService
{
    private UserSessionData userSessionData;
    public void SetUserData(UserSessionData userData)
    {
        userSessionData = userData;
    }
    public UserSessionData GetUserData()
    {
        return userSessionData;
    }
}

该服务在Startup.cs文件中注册为作用域服务。

代码语言:javascript
复制
services.AddScoped<IUserSessionService, UserSessionService>();

该服务以以下方式在host.cshtml页面后面的代码中从数据库中获取用户信息:

代码语言:javascript
复制
public class _Host : PageModel
{
    private readonly IUserSessionService userSessionService;

    public _Host(IUserSessionService userSessionService)
    {
        this.userSessionService = userSessionService;
    }
    public async Task OnGet()
    {
        var authenticatedUser = Request.HttpContext.User;
        var userData = await GetUserDataFromDb(authenticatedUser);
        await userSessionService.SetUserData(userData);
    }
}

理想情况下,我希望这将初始化服务,以便从现在起,我可以将其注入组件中,并在需要时访问用户的信息。

但是,这是行不通的:当页面加载时,我能够在屏幕上看到用户信息一秒钟,然后它就消失了。最初我认为这是由页面呈现模式ServerPrerendered引起的,但是在Server模式下问题仍然存在。

我能找到的唯一解决办法是将服务注册为Singleton;这并不理想,因为这将在所有用户之间共享(至少这是我的理解!)

有更好的地方可以储存这些信息吗?

更新

这个问题的主要原因是我对Blazor电路的理解不好:)我让它按照@enet的建议工作。我所做的是:

  1. 在为_Host.cshtml创建的HostModel类中从HttpContext获取信息,并将其提供给带有属性

的html代码。

代码语言:javascript
复制
public class HostModel : PageModel
{
    public UserSessionData UserSessionData { get; private set; }
    // ... set the information up
}

  1. 在ComponentTagHelper中创建一个参数并传递属性数据

代码语言:javascript
复制
@model HostModel
<component type="typeof(App)" render-mode="ServerPrerendered" param-UserSessionData="@Model.UserSessionData" />

App.razor中的

  1. [Parameter]的形式访问信息

代码语言:javascript
复制
@code { 
    [Parameter]
    public UserSessionData UserSessionData { get; set; }
}

  1. OnInitializedAsync()方法

中的作用域服务中设置信息。

代码语言:javascript
复制
    protected override async Task OnInitializedAsync()
    {
        await UserSessionService.SetUserData(UserSessionData);
    }

现在,通过注入UserSessionData,应用程序中的任何地方都可以使用IUserSessionService

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2021-03-22 19:32:41

你不应该像你提到的那样使用Singleton。

public async Task OnGetAsync()而不是public async Task OnGet()

将数据传递给Blazor应用程序的推荐方法是通过组件html标记助手提供的参数.

  1. 将您的服务添加为作用域。
  2. 在App组件中定义参数属性,将您的服务注入组件,并且当App组件初始化时,将传递的参数读取到注入的service...Now中。
  3. 将参数属性添加到_Host.cshtml文件中的组件html标记助手中。

参见complete sample如何在这里这样做

注意:文档中还有一个代码示例,说明如何做到这一点。它或多或少和我的一样

更新:

下面是我曾经创建的服务。效果很好。将其与您所做的进行比较,或者更好地使用它,因为它实际上在很大程度上完成了您的服务。

UserEditService.cs

代码语言:javascript
复制
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Identity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Security.Claims;


public class UserEditService
    {
        private readonly AuthenticationStateProvider authProvider;
        private readonly UserManager<IdentityUser> userManager;
        public UserEditService ( AuthenticationStateProvider 
                                                authProvider,
                         UserManager<IdentityUser> userManager)
        {
        // Execute only when the app is served. The app is served 
       // when it is initially accessed, or after a user has logged-in
            
            this.authProvider = authProvider;
            this.userManager = userManager;

        }

        public async Task<string> GetEmail()
        {
            var authenticationStateTask = await 
                   authProvider.GetAuthenticationStateAsync();
                     
            var user = authenticationStateTask.User;
            // Executed whenever the GetMail method is called 
            
            if (user.Identity.IsAuthenticated)
            {
                // Executed only when the user is authenticated
                var userId = user.Claims.Where(c => c.Type == 
                     ClaimTypes.NameIdentifier).FirstOrDefault();

                var applicationUser = await 
                         userManager.FindByIdAsync(userId.Value);

                return applicationUser.Email;
            }

            return "User not authenticated";
               
        }
    } 

Startup.ConfigureServices

代码语言:javascript
复制
 services.AddScoped<UserManager<IdentityUser>>();
 services.AddScoped<UserEditService>();

用法

代码语言:javascript
复制
@page "/"
@inject UserEditService UserEditService


<div>Your email: @email</div>


@code
{
    private string email;

    protected override async Task OnInitializedAsync()
    {
        email = await UserEditService.GetEmail();
    }
   
}

注意:在我的服务中,我只返回电子邮件字段,但您当然可以返回完整的身份用户.

票数 1
EN

Stack Overflow用户

发布于 2021-03-22 20:49:11

我不能回答你的问题,但我可以说,当涉及到使用身份验证和服务时,事情变得有点危险。我尝试了一种类似的方法:创建一个基于身份验证的返回用户信息的服务,然后将它注入到任何需要它的控件中--有时一个页面上的多个组件在初始化时访问成员身份。

这导致了很多冲突--试图在DbContext已经打开的时候访问它,等等。基本上,种族条件崩溃。

我的建议是根本不做这件事。但如果你找到一个可行的解决方案,我期待着看到它。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/66752175

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档