繁体   English   中英

无限等待使用带有 blazor 服务器应用程序的 UserManager

[英]Infinite await using UserManager with blazor server app

我实际上正在使用 EF Core 6 开发 blazor 服务器项目,但我遇到了问题。 实际上,我想使用自定义服务 class 在我的应用程序中注册用户,但是,在注册新用户时,UserManager class 似乎冻结了。 一开始想到死锁,于是用dotnet-dump命令分析线程数据,但是好像没有线程无限期保持锁。

点阻塞在RegisterService.cs就行了:

await _userManager.SetUserNameAsync(newUser, registerModel.Username);

我尝试使用以下方法手动设置用户 object 数据,例如用户名和 Email:

newUser.UserName = registerModel.Username;

但是冻结出现在以下行中:

IdentityResult result = await _userManager.CreateAsync(newUser, registerModel.Password);

请注意,调用 UserManager 的 function IsEmailUsed 工作正常。

我没有办法尝试解决我的问题,所以我终于在这里问了。 这是我的代码:

启动.cs:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddRazorPages();
        services.AddServerSideBlazor();
        services.AddMudServices();
        services.AddDbContext<Rpg_AgendaContext>(options => options.UseNpgsql(Configuration.GetConnectionString("DefaultConnection")));

        services.AddDefaultIdentity<User>(options => options.SignIn.RequireConfirmedAccount = true)
                .AddEntityFrameworkStores<Rpg_AgendaContext>();

        services.AddScoped<SignInManager<User>>();
        services.AddScoped<UserManager<User>>();
        
        services.AddScoped<RegisterService>();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Error");
            // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();

        app.UseRouting();
        app.UseAuthentication();
        app.UseAuthorization();

        app.UseMiddleware<LoginMiddleware<User>>();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapBlazorHub();
            endpoints.MapFallbackToPage("/_Host");
        });
    }
}

寄存器.razor:

@page "/register"
@using System.Text.RegularExpressions
@using Microsoft.AspNetCore.Identity
@using RpgAgenda.Data.Entities
@using Rpg_Agenda.Pages.Shared
@using Rpg_Agenda.Service.RegisterService.Models
@using Rpg_Agenda.Services
@using Rpg_Agenda.Services.RegisterService

@inject NavigationManager navMgr
@inject RegisterService registerService

<MudGrid Justify="Justify.Center">
    <MudItem xs="4">
        <MudPaper Class="pa-4" Elevation="3">
            <MudForm @ref="form">
                <ErrorField errorValue="@registerModel.Error"></ErrorField>

                <MudTextField T="string" Label="Username" Required="true" RequiredError="Username is required" @ref="username" />
                <MudTextField T="string" Label="Email" Required="true" Validation="@(new Func<string, string>(EmailCorrect))" RequiredError="Email is required" @ref="email" />
                <MudTextField T="string" Label="Confirm email" Required="true" Validation="@(new Func<string, string>(EmailMatch))" RequiredError="Email confirmation is required" />
                <MudTextField T="string" InputType="InputType.Password" Validation="@(new Func<string, string>(PasswordStrength))" Label="Password" Required="true" RequiredError="Password is required" @ref="password"/>
                <MudTextField T="string" InputType="InputType.Password" Validation="@(new Func<string, string>(PasswordMatch))" Label="Confirm password" Required="true" RequiredError="Password confirmation is required" />

                <div class="d-flex align-center justify-center mt-6">
                    <MudButton FullWidth="true" OnClick="RegisterClicked" Variant="Variant.Filled" Color="Color.Primary">Register</MudButton>
                </div>
            </MudForm>
        </MudPaper>
    </MudItem>
</MudGrid>

@code {
    private MudForm form;
    private MudTextField<string> username;
    private MudTextField<string> email;
    private MudTextField<string> password;
    private RegisterModel registerModel = new();

    private async Task RegisterClicked()
    {
        await form.Validate();

        if(form.IsValid)
        {
            registerModel.Username = username.Value;
            registerModel.Email = email.Value;
            registerModel.Password =  password.Value;
            bool succeed = registerService.RegisterUser(registerModel).Result;
            if (succeed)
                navMgr.NavigateTo("/");
        }
    }

    private string PasswordStrength(string password)
    {
        if (password == null)
            return null;
        if (password.Length < 10)
            return "Password must be at least 10 characters";
        if (!Regex.IsMatch(password, @"[A-Z]"))
            return "Password must contain at least one capital letter";
        if (!Regex.IsMatch(password, @"[a-z]"))
            return "Password must contain at least one lowercase letter";
        if (!Regex.IsMatch(password, @"[0-9]"))
            return "Password must contain at least one digit letter";
        if (!Regex.IsMatch(password, @"[^a-zA-Z0-9]"))
            return "Password must contain at least one special character";
        return null;
    }

    private string PasswordMatch(string passwordConfirmation) => password.Value == passwordConfirmation ? null : "Password doesn't match";

    private string EmailMatch(string emailConfirmation) => email.Value == emailConfirmation  ? null : "Email doesn't match";

    private string EmailCorrect(string email)
    {
        if (email == null)
            return null;
        if (Regex.Match(email, @"^([\w\.\-]+)@([\w\-]+)((\.(\w){2,3})+)$", RegexOptions.IgnoreCase).Success)
        {
            if (registerService.IsEmailUsed(email).Result)
                return "Email already used !";
            return null;
        }
        else
            return "Email format is incorrect";
    }
}

注册服务.cs:

public class RegisterService
{
    private readonly UserManager<User> _userManager;
    private readonly SignInManager<User> _signInManager;

    public RegisterService(UserManager<User> userManager, SignInManager<User> signInManager)
    {
        _userManager = userManager;
        _signInManager = signInManager;
    }

    public async Task<bool> IsEmailUsed(string email) => await _userManager.FindByEmailAsync(email) != null;

    public async Task<bool> RegisterUser(RegisterModel registerModel)
    {
        User newUser = CreateUser(registerModel).Result;
        IdentityResult result = await _userManager.CreateAsync(newUser, registerModel.Password);
        if (!result.Succeeded)
        {
            registerModel.Error = result.Errors.Select(error => error.Description).ToArray().Aggregate((current, next) => $"{current}\n{next}");
        }

        return result.Succeeded;
    }

    private async Task<User> CreateUser(RegisterModel registerModel)
    {
        User newUser = new User();

        Debug.WriteLine($"ID : {newUser.Id}, UN : {newUser.UserName}, @ : {newUser.Email}");

        await _userManager.SetUserNameAsync(newUser, registerModel.Username);
        await _userManager.SetEmailAsync(newUser, registerModel.Email);
        return newUser;
    }
}

感谢你们对我的帮助。

改变

bool succeed = registerService.RegisterUser(registerModel).Result;

bool succeed = await registerService.RegisterUser(registerModel);

并寻找.Result.Wait()的其他用途。 删除它们,它们可能会死锁。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM