【问题标题】:ASP.NET Core Razor Pages - Complex model property is null after returning Page()ASP.NET Core Razor Pages - 返回 Page() 后复杂模型属性为空
【发布时间】:2021-02-10 17:37:30
【问题描述】:

我显然不了解 Razor Pages 中模型绑定复杂属性的基本知识。当我的模型无效时,我 return Page(); 但在引用模型的复杂属性时出现异常。这是我所拥有的精简版:

型号:

public class Movie
{
    public int Id { get; set; }
    public string Title { get; set; }
    public Director Director { get; set; }
    public string Description { get; set; }
}

public class Director
{
    public int Id { get; set; }
    public string Name { get; set; }
}

索引.cshtml.cs:

[BindProperty]
public Movie Movie { get; set; }

[BindProperty, Required]
public string Description { get; set; }

public IActionResult OnGet()
{
    // Pretend we're loading from the DB...
    Movie = new Movie
    {
        Id = 1,
        Title = "Citizen Kane",
        Director = new Director
        {
            Id = 101,
            Name = "Orson Wells"
        }
    };

    return Page();
}

public IActionResult OnPost()
{
    if (!ModelState.IsValid)
        return Page();

    Movie.Description = Description;

    //Save to DB
    // ...

    return RedirectToPage("/Index");
}

Index.cshtml:

@page
@model IndexModel

<h5>@Model.Movie.Title</h5>
<h6>Directed by @Model.Movie.Director.Name</h6>

<form method="post">
    <div asp-validation-summary="ModelOnly" class="text-danger"></div>
    <input type="hidden" asp-for="Movie.Id" />
    <textarea asp-for="Description" placeholder="Enter Description"></textarea>
    <button type="submit">Submit</button>
</form>

当我将描述留空时,OnPost 中的模型无效并按预期返回Page()。但后来我在这里得到了System.NullReferenceException; Object reference not set to an instance of an object

<h6>Directed by @Model.Movie.Director.Name</h6>

为什么Movie.Director 在这里为空?返回Page()时是否需要再次获取数据?我以为它会再次触发OnGet(),但事实并非如此。我做错了什么?

【问题讨论】:

  • OnGet() 应该被解雇。我想知道它也不起作用的原因+1

标签: c# asp.net-core razor-pages


【解决方案1】:

为什么Movie.Director 在这里为空?返回Page()时是否需要再次获取数据?我原以为它会再次触发OnGet(),但事实并非如此。

不,OnGet 方法不会在您的 OnPost 方法实际执行时自动调用。这两个处理程序彼此独立,它们唯一共享的是底层 Razor 视图和页面模型。如果您的视图需要将数据加载到模型中,那么您也需要为 OnPost 执行此操作。

可以从您的 POST 方法中调用 GET 方法,但取决于您在 OnGet 中实际执行的操作,这可能会覆盖由 OnPost 明确设置的数据。我建议改为添加一个额外的方法,OnGetOnPost 都会调用该方法来设置视图需要渲染的 共享 模型属性。

public IActionResult OnGet()
{
    Movie = LoadMovie();
    return Page();
}

public IActionResult OnPost()
{
    if (!ModelState.IsValid)
    {
        Movie = LoadMovie();
        return Page();
    }

    // …
    return RedirectToPage("/Index");
}

private Movie LoadMovie()
{
    // …
}

请注意,如果您确实需要渲染页面视图,也只需要执行此方法。


混淆可能源于OnGetOnPost 方法的用途:对于Razor 页面,这些方法是入口点。当请求与 Razor 页面匹配的路由时,它们由 ASP.NET Core 框架调用。如果是 GET 请求,则调用 OnGet 方法;如果是 POST 请求,则为 OnPost 方法。除了共享相同的视图(cshtml)和模型之外,这些方法之间没有直接关系。

由于这些方法是入口点,POST 请求不会执行OnGet 方法,GET 请求也不会执行OnPost 方法——当然,除非您自己在这些方法中进行这些调用。

现在,当您返回 Page() 时,唯一发生的事情是您告诉 ASP.NET Core 框架然后使用您在页面模型中准备的数据呈现您的 Razor 视图。所以它只是渲染 cshtml 传递页面模型作为视图的模型。但是,此时不会导致对OnGetOnPost 的任何调用,因为它们已经运行以进入 Razor 页面执行。

这意味着,如果您希望始终在呈现视图之前执行某些逻辑,那么您需要在返回 Page() 之前从页面处理程序中显式执行该逻辑。

【讨论】:

  • 为什么他一开始不会得到一个空引用异常?
  • @snr 开始是什么意思?哪个开头? Movie.Title?由于Movie 属性具有[BindProperty] 属性,因此该属性将由 Razor 页面创建(但不填充)。所以Movie 不是空的,但是Movie.TitleMovie.Director 是;但只有 Movie.Director.Name 会导致 NRE。
  • 'Movie.Director.Name' 的意思。他不是在第一次运行时就遇到它,即'index.cshtml'吗?
  • “首次运行”是指 GET 请求吗?在那里,OnGet 将显式设置Movie 属性。只有在提交表单时才会发出 POST 请求,如果模型状态无效,则不会再次显式设置 Movie 来呈现视图。
  • 返回Page() 不会调用OnGetOnGetOnPost 是 Razor 页面的入口点,如果对 Razor 页面的路由发出 GET 或 POST 请求,它们将由 ASP.NET Core 框架调用。在这些方法中返回Page() 只是告诉框架然后渲染 Razor 页面的Razor 视图。它不会触发 Razor 页面模型中的其他方法调用。
猜你喜欢
  • 2018-07-16
  • 2019-05-10
  • 2020-10-04
  • 2021-05-14
  • 2020-11-27
  • 2022-07-14
  • 2018-10-29
  • 2021-01-09
  • 1970-01-01
相关资源
最近更新 更多