我在尝试使用我在 _Host.cshtml 文件中初始化的范围服务时遇到了一个奇怪的行为。 这是服务,很简单:
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
文件中注册为 Scoped 服务
services.AddScoped<IUserSessionService, UserSessionService>();
该服务在 host.cshtml 页面后面的代码中以下列方式从数据库中获取用户信息:
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 的建议让它工作了。我所做的是:
HttpContext
创建的 HostModel
类中的 _Host.cshtml
获取信息,并将其提供给具有属性的 html 代码public class HostModel : PageModel
{
public UserSessionData UserSessionData { get; private set; }
// ... set the information up
}
@model HostModel
<component type="typeof(App)" render-mode="ServerPrerendered" param-UserSessionData="@Model.UserSessionData" />
[Parameter]
中以 App.razor
身份访问信息@code {
[Parameter]
public UserSessionData UserSessionData { get; set; }
}
OnInitializedAsync()
方法内设置范围服务中的信息 protected override async Task OnInitializedAsync()
{
await UserSessionService.SetUserData(UserSessionData);
}
现在,通过注入 UserSessionData
IUserSessionService
将在应用程序的任何地方可用
答案 0 :(得分:1)
出于您提到的原因,您不应该使用 Singleton。
做public async Task OnGetAsync()
而不是public async Task OnGet()
将数据传递到 Blazor 应用程序的推荐方法是通过组件 html 标记助手提供的参数...
在此处查看complete sample如何操作
注意:文档中还有一个代码示例,说明如何做到这一点。和我的差不多
更新:
以下是我曾经创建的服务。它工作正常。将它与您所做的进行比较,或者甚至更好地使用它,因为它实际上在很大程度上完成了您的服务所做的工作。
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";
}
}
services.AddScoped<UserManager<IdentityUser>>();
services.AddScoped<UserEditService>();
@page "/"
@inject UserEditService UserEditService
<div>Your email: @email</div>
@code
{
private string email;
protected override async Task OnInitializedAsync()
{
email = await UserEditService.GetEmail();
}
}
注意:在我的服务中,我只返回电子邮件字段,但您当然可以返回完整的身份用户...
答案 1 :(得分:0)
我无法回答您的问题,但我可以说,在使用身份验证和服务时,事情会变得有点冒险。我正在尝试类似的事情:创建一个服务,根据身份验证返回用户信息,然后将其注入到任何需要它的控件中——有时一个页面上的多个组件在初始化期间访问成员资格。
这导致了很多冲突——尝试在 DbContext 已经打开时访问它,等等。基本上,竞争条件崩溃了。
我的建议是根本不要这样做。但是,如果您找到了有效的解决方案,我很期待看到它。