【问题标题】:How can I keep my model in my View after OnPost?OnPost 后如何将模型保留在视图中?
【发布时间】:2018-10-13 19:02:26
【问题描述】:

我正在使用 .Net Core 进行项目,并且正在使用 ASP Razor Pages。 在我的模型中,我有一个 OnGet,它可以在我的视图中加载我需要的所有数据。

public IList<Projet> Projets { get; set; }
public ActionResult OnGet()
{
    Projets = _serviceSelect.getProjets();
    return Page();
}

然后,在我提交表单时激活的 OnPost 中。

<form method="post">
    <div asp-validation-summary="All" class="text-danger"></div>
    <input type="radio" value="1" asp-for="LoginData.TypeCompte" />choice<br />
    Username: <input asp-for="LoginData.Username" /><br />
    Password: <input asp-for="LoginData.Password" /><br />
    Remember me: <input asp-for="LoginData.RememberMe" type="checkbox" /><br />
    <input asp-page-handler="connexion" type="submit" value="Login" />
    @Html.AntiForgeryToken()
</form>

我想使用我的 ModelState 在我的视图中显示一个错误。

public ActionResult OnPostConnexion()
{
    if (ModelState.IsValid)
    {
       // Do stuff 
    }
    else
    {
        ModelState.AddModelError("", "username or password is blank");
        return Page();
    }
}

但是,当我返回 Page() 时,就像重新加载模型一样,当我尝试访问我的数据时,我的对象为空。

 Object reference not set to an instance of an object.
@foreach (var item in Model.Projets)

如何在不丢失模型中包含的数据的情况下更新视图?

【问题讨论】:

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


    【解决方案1】:

    我通过在public ActionResult OnPostConnexion() 中将return Page(); 替换为return this.OnGet(); 解决了这个问题

    这样,您在 OnGet 中填充 PageModel 属性的任何逻辑都会被重用。

    所以我的完整示例如下所示:

        public IEnumerable<SelectListItem> CompanyListing { get; private set; }
    
        public async Task<IActionResult> OnGet()
        {
            if (!string.IsNullOrEmpty(this.ErrorMessage))
            {
                this.ModelState.AddModelError(string.Empty, this.ErrorMessage);
            }
    
            this.CompanyListing = await this.PopulateCompanyList();
            return this.Page();
        }
    
        public async Task<IActionResult> OnPostAsync()
        {
            if (!this.ModelState.IsValid)
            {
                // Something failed. Redisplay the form.
                return await this.OnGet();
            }
    
            return this.RedirectToPage("/");
        }
    

    【讨论】:

      【解决方案2】:

      您收到对象引用错误的原因是返回视图时尚未填充 Model.Projects,因此无法在 foreach 循环中进行迭代。

      根据您现有的代码,您可以在返回页面之前再次填充您的模型。

      public ActionResult OnPostConnexion()
      {
          if (ModelState.IsValid)
          {
             // Do stuff 
          }
          else
          {
              Projets = _serviceSelect.getProjets();
      
              ModelState.AddModelError("", "username or password is blank");
              return Page();
          }
      }
      

      更好的解决方案是:

      public ActionResult OnPostConnexion(viewModel model)
      {
          if (!ModelState.IsValid)
          {
             return View(model);
          }
          else
          {
              //do stuff
          }
      }
      

      【讨论】:

      • 是的,我曾想过这样做,但不能简单地保持模型不变吗?或者asp总是在一个方法之后重新加载模型?
      • 你应该返回 View("ViewName", model)
      • 因为是 Razor pages 项目,无法返回 "View()"
      【解决方案3】:

      当您调用“Page()”时,这是在请求一个新的 Page 对象,当您返回该对象时,它将是空白的。页面和视图是不同的,通常使用 ASP 您将使用视图,MSDN 上有一些关于差异以及为什么的好文章,但现在只要将 Page() 更改为 View() 应该可以解决问题。

      【讨论】:

      • 我假设这是一个 MVC 项目
      • 我不能“返回 View()”,因为这是一个 Razor 页面项目
      • [TempData] public string Message { get;放; } 公共异步任务 OnPostAsync() { if (!ModelState.IsValid) { return Page(); } _db.Customers.Add(客户);等待_db.SaveChangesAsync(); Message = $"添加了客户 {Customer.Name}"; return RedirectToPage("./Index"); }
      【解决方案4】:

      您需要在代码中修复两件事:

      首先,您需要使用 [BindProperty] 标记您的 Projets 属性,如下所示:

      [BindProperty]
      public IList<Projet> Projets { get; set; }
      

      然后您需要重新编码您的页面,以便 Projet 的字段位于 form 标记内,以便将它们发布回页面,然后您需要重写输出它们的方式:

      <form method="post">
          <div asp-validation-summary="All" class="text-danger"></div>
          <input type="radio" value="1" asp-for="LoginData.TypeCompte" />choice<br />
              Username: <input asp-for="LoginData.Username" /><br />
              Password: <input asp-for="LoginData.Password" /><br />
              Remember me: <input asp-for="LoginData.RememberMe" type="checkbox" /><br />
          <input asp-page-handler="connexion" type="submit" value="Login" />
          @Html.AntiForgeryToken()
      
          <!-- SEE REMARK BELOW! -->
          @{
              for (int i = 0; i < Model.Projets.Count; i++)
              {
                  <input asp-for="@Model.Projets[i].FieldOne" />
                  <br />
                  <input asp-for="@Model.Projets[i].FieldTwo" />
              }
          }
      
      </form>
      

      备注 您需要在列表中呈现您的字段,以便它们获得唯一的 ID,以便 ModelBinder 可以在 Post 上检索它们。在我的例子中,我将一个字段命名为 FieldOne,这就是它的呈现方式:

      <input type="text" id="Projets_0__FieldTwo" name="Projets[0].FieldTwo" value="one">
      

      【讨论】:

      • 使用你的提案和@j9070749的解决方案有区别吗?因为如果我这样做,我只是再次填充我的模型,但在视图中?
      • 以我的方式,您只需将数据从 html 发布回 Page,无需像 @j9070749 答案那样查询您的数据库。
      【解决方案5】:

      我遇到了同样的问题,建议的解决方案中没有人工作。 我通过简单地使用帮助类为我保留模型来解决它。 该解决方案并不优雅但有效:)

      public interface ISessionHelper
          {
              void AddRenewItem(string key, object item);
              object GetItem(string key);
          }
      public class SessionHelper : ISessionHelper
      {
          private readonly Dictionary<string, object> _sessionBag;
      
          public SessionHelper()
          {
              _sessionBag = new Dictionary<string, object>();
          }
      
          public void AddRenewItem(string key, object item)
          {
              if (_sessionBag.Keys.Contains(key)) _sessionBag.Remove(key);
              _sessionBag.Add(key, item);
          }
      
          public object GetItem(string key)
          {
              if (_sessionBag.Keys.Contains(key))
              {
                  return _sessionBag[key];
              }
              return null;
          }
      }
      

      在我的 get 方法中,我将一个项目添加到字典中:

       public async Task<IActionResult> OnGetAsync(int? id)
          {
            ...
              _sessionHelper.AddRenewItem(this.ToString(), this);
              ...
      
          }
      

      在我的 post 方法中,我从字典中取回模型并为当前模型分配所需的属性:

       public async Task<IActionResult> OnPostApplyGuess(int itemId)
          {
              GameModel model = (GameModel) _sessionHelper.GetItem(this.ToString());
              MyProp1= model.MyProp1;
              MyProp2 = model.MyProp2 ;
              MyProp3= model.MyProp3;
      
              if (!ModelState.IsValid)
              {
                  return Page();
              }
      
              return Page();
          }
      

      不要忘记将服务添加为单例:

      services.AddSingleton<ISessionHelper, SessionHelper>();
      

      我希望它对某人有所帮助:)

      【讨论】:

        【解决方案6】:

        尝试使用 TempData。


        更多信息:http://www.tutorialsteacher.com/mvc/tempdata-in-asp.net-mvc

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2010-12-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-05-16
          • 2018-12-07
          • 1970-01-01
          • 2016-07-18
          相关资源
          最近更新 更多