【问题标题】:Repopulating certain property values if ModelState.IsValid is false如果 ModelState.IsValid 为 false,则重新填充某些属性值
【发布时间】:2011-08-02 10:20:42
【问题描述】:

我有以下动作方法(部分代码):

[HttpPost]
public ActionResult Create(EditGrantApplicationViewModel editGrantApplicationViewModel)
{
   if (!ModelState.IsValid)
   {
      return View("Create", editGrantApplicationViewModel);
   }

   return View("Index");
}

EditGrantApplicationViewModel 看起来像这样(部分代码):

public class EditGrantApplicationViewModel
{
   public IEnumerable<Title> Titles { get; set; }
   public int TitleId { get; set; }
   public IEnumerable<Bank> Banks { get; set; }
   public int BankId { get; set; }
   public IEnumerable<AccountType> AccountTypes { get; set; }
   public int AccountTypeId { get; set; }
}

当第一次请求这个 Create 视图时,我会在我的服务层中填充 Titles 并返回一个 EditGrantApplicationViewModel 的实例,如下所示:

public ActionResult Create()
{
   EditGrantApplicationViewModel editGrantApplicationViewModel = grantApplicationService.CreateEditGrantApplicationViewModel();

   return View(editGrantApplicationViewModel);
}

我的服务层中的CreateEditGrantApplicationViewModel

public EditGrantApplicationViewModel CreateEditGrantApplicationViewModel()
{
   EditGrantApplicationViewModel editGrantApplicationViewModel = new EditGrantApplicationViewModel
   {
      Titles = titleRepository
         .GetAll()
         .Where(x => x.Active)
         .OrderBy(x => x.Name)
   };

   return editGrantApplicationViewModel;
}

当我单击提交按钮时,它将进入发布操作Create 方法。它接收EditGrantApplicationViewModel 类型的editGrantApplicationViewModel 参数。为什么 Titles 属性设置为 null?我认为它会保留它的价值吗?

现在假设有一个错误,ModelState.IsValid 是错误的。所以这意味着我将不得不重新填充Titles 属性。鉴于已在editGrantApplicationViewModel 的表单中设置的属性值,我现在将如何填充 Titles 属性?我假设我需要在我的服务层中使用另一种方法来填充它?最好的方法是什么?

任何源代码将不胜感激。

2011-04-11 更新

在我看来,我有 3 个下拉菜单。头衔、银行和账户类型。这就是为什么我的视图模型中有 3 个列表。我有一个服务类来处理插入、更新和获取项目。例如,在我的银行服务类中,我会有与银行相关的 Insert、Update、GetAll、GetById 等方法。我会在标题和帐户类型服务中拥有类似的服务。

这是我目前在控制器类中的方式:

private IGrantApplicationService grantApplicationService;
private ITitleService titleService;
private IBankService bankService;
private IAccountTypeService accountTypeService;

public GrantApplicationController(IGrantApplicationService grantApplicationService, ITitleService titleService, IBankService bankService, IAccountTypeService accountTypeService)
{
   this.grantApplicationService = grantApplicationService;
   this.titleService = titleService;
   this.bankService = bankService;
   this.accountTypeService = accountTypeService;
}

public ActionResult Create()
{
   EditGrantApplicationViewModel editGrantApplicationViewModel = new EditGrantApplicationViewModel
   {
      // Populate the dropdown lists
      Titles = titleService
         .GetAll()
         .Where(x => x.Active)
         .OrderBy(x => x.Name),
      Banks = bankService
         .GetAll()
         .Where(x => x.Active)
         .OrderBy(x => x.Name),
      AccountTypes = accountTypeService
         .GetAll()
         .Where(x => x.Active)
         .OrderBy(x => x.Name)
   };

   return View(editGrantApplicationViewModel);
}

我们不久前谈过,您说最好为控制器提供一项服务。就我而言,我需要从 3 个不同的数据库表中填充 3 个列表。您能否提供一些代码来说明您将如何做到这一点。如果需要更多详细信息,请告诉我。

【问题讨论】:

    标签: c# asp.net-mvc asp.net-mvc-2 asp.net-mvc-3 viewmodel


    【解决方案1】:

    服务层不应该返回视图模型,它应该与模型一起使用。所以让你的服务层返回这些标题:

    public IEnumerable<Title> GetTitles()
    {
        return titleRepository
            .GetAll()
            .Where(x => x.Active)
            .OrderBy(x => x.Name)
        };
    }
    

    然后将实例化视图模型的责任留给控制器:

    public ActionResult Create()
    {
        var model = new EditGrantApplicationViewModel 
        {
            Titles = grantApplicationService.GetTitles()
        };
        return View(model);
    }
    
    [HttpPost]
    public ActionResult Create(EditGrantApplicationViewModel model)
    {
        if (!ModelState.IsValid)
        {
            // Reload the Titles as we are redisplaying the same view
            // and they were not part of the view model that was submitted
            model.Titles = grantApplicationService.GetTitles();
            return View("Create", model);
        }
        return View("Index");
    }
    

    【讨论】:

    • @Darin:前几天你提到聚合,你是什么意思?如果我需要填充 AccountTypes 列表,现在会发生什么?我有 AccountTypeService.GetAll 和 TitlesService.GetAll 的服务方法。我应该在我的grantApplicationService 中复制这两种方法吗?这听起来对我来说是不必要的。
    • @Brendan Vogt,如果不了解有关您的方案的更多详细信息,很难回答这样的问题。那些AccountTitle 实体之间有什么关系吗?是什么证明您选择有两个服务类:AccountTypeServiceTitlesService?我的印象是您正在复制您的存储库。
    • @Darin:我已经更新了我的帖子,并添加了一个更新部分。如果我需要提供更多代码,请告诉我。
    • @Brendan Vogt,编写一个服务方法如何返回一些包含 3 个集合属性的模型:Titles、Banks 和 AccountTypes。然后设置服务方法返回的模型和控制器中的视图模型之间的映射。
    • @Darin:我遇到的另一个问题是当 ModelState.IsValid 为 false 时,我的 3 个列表为空,这意味着需要重新填充它。鉴于已经包含表单值的视图模型,我需要如何以及在哪里重新填充这些列表(根据您上面的答案)?如果您能提供一些代码,我们将不胜感激。
    【解决方案2】:

    由于 mvc 是无状态的(在大多数情况下,您可以使用 TempData 和 session 来维护某些状态)。在初始请求和显示之后,titles 属性基本上丢失了。

    要重新建立 Titles,您需要在表单上以某种方式显示这些值,以便 ModelBinder 可以补充您的 EditGrantApplicationViewModel

    您很可能需要这样做:

    <% foreach(Title title in Model.Titles { %>
      <%: Html.HIddenFor(m => title) %>
    <% } %>
    

    请注意我的语法可能有点不对劲,因为我的机器上没有 Visual Studio。

    此外,我不确定 ModelBinder 将如何处理您的 IEnumberable&lt;T&gt;,您可能必须将其更改为 List&lt;T&gt;,以便 ModelBinder 可以正确构建类型。

    有关如何绑定到您的 Titles 列表的其他信息。

    1. Post from Phil Haack about Model Binding To A List
    2. A slightly smiler question about mvc and IEnumerable&lt;T&gt;

    【讨论】:

    • 在这个控制器的构造函数中,我通过构造函数注入传递了一个 IGrantApplicationService 实例。我想我也可以传递一个 ITitleService 实例,但我试图只将应用程序服务传递给相应的控制器。希望这是有道理的?
    • @Brendan,我误读了这个问题,似乎Titles 用于下拉列表,而 SelectedTitle 是必填字段。您将需要以某种方式将信息保留在表单上,​​或者如您所说的无效模型状态,使用 ITitleService 重新填充错误时的标题。
    猜你喜欢
    • 1970-01-01
    • 2012-01-03
    • 2021-12-09
    • 1970-01-01
    • 2019-05-20
    • 2023-03-04
    • 1970-01-01
    • 1970-01-01
    • 2017-11-05
    相关资源
    最近更新 更多