.NET Razor 页面,回发时清除作用域依赖项注入属性

.NET Razor pages, scoped dependency injection properties cleared on postback

提问人:nerdalert 提问时间:11/12/2023 最后编辑:nerdalert 更新时间:11/13/2023 访问量:45

问:

我正在尝试在我的 Razor 页面项目上实现持久性存储。我需要的是,在我的页面上回发时,不应清除属性值。我正在通过依赖注入来做到这一点。我将存储属性值的类注入到页面模型的构造函数中,并打算在回发后重新填充页面变量。

我打算使用AddScoped,但似乎在回发时类值被清除了。如果我使用 AddSingleton,那么是的,类值不会被清除,但我不希望它们在用户导航到另一个页面时保持填充状态。我希望在用户位于特定页面上并在该页面上执行回发时填充这些值,并在刷新页面或导航到另一个页面时清除这些值。这样,在进行回发时,我可以用类变量重新填充页面变量。

在程序.cs中

builder.Services.AddScoped<IDataService, DataService>();

我的界面

public interface IDataService
{
    Models.User? User { get; set; }

    byte[]? LogoData { get; set; }

    public byte[]? SignatureData { get; set; }
}

我的页面

public Models.User? User { get; set; }

public byte[]? LogoData { get; set; }

public byte[]? SignatureData { get; set; }

public ProfileModel(ILogger<ProfileModel> loggerService, IDataService dataService)
{
    _loggerService = loggerService;
    _dataService = dataService;
}

当我在页面上提交表单时,会点击以下方法,但是如果我在开头放置一个断点并检查我的_dataService类的内容,则一切都为 null。

public async Task<IActionResult> OnPostRecreateApiKeyAsync()
{
    ...
}

我的整个程序.cs文件:

var builder = WebApplication.CreateBuilder(args);

// If needed, Clear default providers
builder.Logging.ClearProviders();

// Use Serilog
builder.Host.UseSerilog((hostContext, services, configuration) => {
    configuration
        .WriteTo.File("serilog.txt")
        .WriteTo.Console();
});

// Add services to the container.
builder.Services.AddRazorPages();

// register my dbcontext from data project
builder.Services.ConfigureService(builder.Configuration);

// register services
builder.Services.AddTransient<IApiKeyService, ApiKeyService>();
builder.Services.AddTransient<IStripeService, StripeService>();
builder.Services.AddTransient<IInvoiceService, InvoiceService>();
builder.Services.AddTransient<IUserService, UserService>();
builder.Services.AddScoped<IDataService, DataService>();

// add support for Auth0
builder.Services
    .AddAuth0WebAppAuthentication(options => {
        options.Domain = builder.Configuration["Auth0:Domain"];
        options.ClientId = builder.Configuration["Auth0:ClientId"];
        options.Scope = "openid profile email";
    });

// protect these pages
builder.Services.AddRazorPages(options =>
{
    options.Conventions.AuthorizePage("/account/profile");
    options.Conventions.AuthorizePage("/account/usage");
    options.Conventions.AuthorizePage("/account/logout");
    options.Conventions.AuthorizePage("/account/billing/history");
    options.Conventions.AuthorizePage("/account/billing/paymentmethods");
});

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    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.MapRazorPages();

app.Run();
asp.net 剃须刀页面

评论

0赞 Dai 11/12/2023
你应该在你的 ctor 中添加非 null 前提条件 - 这也将帮助您检测何时发生可疑的事情......因为你所描述的内容不应该发生,前提是你的页面是由 Blazor 自己的工厂实例化的,这些工厂与 DI 容器集成。请向我们出示您的/代码或同等文件。StartupConfigureServices
0赞 nerdalert 11/12/2023
我已经用我的整个程序.cs编辑了原始帖子。Razor 页面项目没有启动文件。
0赞 Dai 11/12/2023
我可能会建议从将 DI 验证添加到您的 ,就像这里一样 - 例如 - 这样一来,对于配置错误的服务,您将在启动时获得抢占式异常。builderbuilder.UseDefaultServiceProvider( o => { o.ValidateScopes = true; ValidateOnBuild = true; } )
0赞 nerdalert 11/12/2023
我的服务配置方式真的有问题吗?
0赞 Dai 11/12/2023
可能有 - 这当然是一个可能的原因。使用将有助于消除这种可能性,或者表明存在问题。(我只是希望MSFT默认启用对这些东西的严格验证......ValidateScopes

答:

0赞 Ruikai Feng 11/13/2023 #1

当我在页面上提交表单时,点击了以下方法,但如果我将 开头的断点,检查我的_dataService的内容 class 一切都是 null。

builder.Services.AddScoped<IDataService, DataService>();

在 asp.net 核心中,作用域服务为一个请求保留相同的实例,当您提交表单时,您正在发出另一个请求,您将获得另一个 DataService 实例,而不是您在 GET 处理程序中设置属性的实例

 public void OnGet()
 {
     
     LogoData = ....//read from your other service?
     HttpContext.Session.Set("LogoData", LogoData);

 }

 public void OnPost()
 {
     if(HttpContext.Session.TryGetValue("LogoData",out var logodata))
     {
         LogoData = logodata;
     }
 }

您可以尝试将数据存储到会话中,每次发送时清除会话 GET 请求

添加 Reuired 服务:

builder.Services.AddSession();

中间件:

app.UseSession();
app.Use(async (context, next) =>
{
    //when you refresh the page or navigate to another page,you are always sending get request

   // you could add a hidden input to record your current page,read it from HttpContext.Request.Form and check target page if you want clear session when you send post request to another page
    if (context.Request.Method == "GET")
    {
        context.Session.Clear();
    }
    await next.Invoke();
});