【问题标题】:Select Tag Helper in ASP.NET Core MVC comes empty after post when need to go back to viewSelect Tag Helper in ASP.NET Core MVC post 后需要返回查看时为空
【发布时间】:2019-07-14 04:16:55
【问题描述】:

正如描述所说,当我将数据从我的方法的 get 传递到视图时,一切都很好,但是,当我点击保存按钮并转到服务器以验证模型是否有效时,如果它确实有效一切都很好,但是,如果模型无效,他会返回相同的视图(就像应该做的那样),但是当它进入服务器时它丢失了集合,我认为再次填充它太愚蠢了,所以,有什么方法可以只为选择列表填充一次模型?

我有那些动作

public IActionResult Create()
{
    var types = _context.Types;

    var vm = new EventViewModel
    {
        Types = new SelectList(types, "Id", "Name")
    };

    return View(vm);
}

[Authorize]
[HttpPost]
public IActionResult Create(EventViewModel vm)
{
    //when it enter here **Types** comes empty  
    //if is not valid
    if (!ModelState.IsValid)
    {
            //var types = _context.Types;
            //**I WANT TO AVOID THIS**
            //vm.Types = new SelectList(types, "Id", "Name", vm.TypeId);
            return View(vm);
    }

    var ev = new Event
    {
        DateTime = vm.GetDateTime(),
        TypeId = vm.TypeId,
        Venue = vm.Venue,
        CoachId = User.FindFirstValue(ClaimTypes.NameIdentifier)
    };

    _context.Add(ev);

    _context.SaveChanges();
    return RedirectToAction("Index", "Home");

}

这是我的看法

@model EcCoach.Web.ViewModels.EventViewModel
@{
    ViewData["Title"] = "Create";
}

<h1>Create</h1>
<form asp-action="Create">
    <p class="alert alert-info">
        All fields are <strong>required</strong>
    </p>

    <div class="form-group">
        <label asp-for="Venue"> </label>
        <input asp-for="Venue" class="form-control" placeholder="Enter a 
        venue" autofocus="autofocus">
        <span asp-validation-for="Venue"></span>
    </div>
    <div class="form-group">
        <label asp-for="Date"> </label>
        <input asp-for="Date" class="form-control" placeholder="eg 
        15/12/2017">
    </div>
    <div class="form-group">
        <label asp-for="Time"> </label>
        <input asp-for="Time" class="form-control">
    </div>
    <div class="form-group">
        <label asp-for="TypeId"> </label>
        <select asp-for="TypeId" asp-items="@Model.Types" class="form- 
            control">
            <option value="0">Choose One</option>
        </select>          
    </div>
    <button type="submit" class="btn btn-primary">
        Save
    </button>

</form>

最后这是我的 ViewModel

    public class EventViewModel
    {
        [Required]
        [FutureDate]
        public string Date { get; set; }

        [Required]
        [ValidTime]
        public string Time { get; set; }

        [Required]
        public byte TypeId { get; set; }

        [Required]
        [StringLength(5)]
        public string Venue { get; set; }

        public DateTime GetDateTime()
        {
            return DateTime.Parse($"{Date} {Time}");
        }

        public IEnumerable<SelectListItem> Types { get; set; }
    }

【问题讨论】:

    标签: c# asp.net-core html-helper


    【解决方案1】:

    您几乎没有办法做到这一点。 最糟糕的方法是将此值存储在 UI 上表单中的隐藏输入中,以便将其添加到 POST 请求中,并且您将在 post 方法中将它们作为 VM 的一部分接收。这很糟糕,因为每个人都可以改变它们,而且它可能是漏洞的来源。你永远不应该相信用户的输入。 第二种方法是将原始 VM 存储在会话中。如果您不需要无状态 - 这是最好的解决方案。因此,在 GET 操作中,您应该在返回客户端之前将 VM 存储在会话中。在发布请求中,您将收到 requestVM 作为方法参数(来自帖子)。它将仅包含用户输入。然后,您应该从会话中获取 sessionVM,将用户输入从 requestVM 映射到 sessionVM。最后,在 sessionVM 中,您将拥有 GET 上的所有值 + 来自用户输入的更新字段。然后您应该验证 sessionVM 并使用其中的值来更新数据库。如果验证失败,您应该以 sessionVM 作为模型返回视图。

    【讨论】:

      【解决方案2】:

      尝试使用IMemoryCache将类型列表保存到缓存中。

      1.在startup.cs中

      public void ConfigureServices(IServiceCollection services)
      {
        services.AddMemoryCache();
        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
      }
      

      2.ApplicationDbContext:

      public DbSet<Type> Types {get;set;}
      

      3.控制器:

      public class MyController : Controller
      {
          private readonly ApplicationDbContext _context;
      
          private IMemoryCache _cache;
      
      
          public MyController(ApplicationDbContext context, IMemoryCache memoryCache)
          {
              _context = context;
              _cache = memoryCache;
      
          }
      
      
          public IActionResult Create()
          {           
              var types = _context.Types.AsNoTracking().ToList();
      
              var vm = new EventViewModel
              {
                  Types = new SelectList(types, "Id", "Name")
              };
      
              _cache.Set("types",types);//cache data
              return View(vm);
          }
      
      
          [HttpPost]
          public IActionResult Create(EventViewModel vm)
          {
              if (!ModelState.IsValid)
              {
      
                 var types = _cache.Get<List<Type>>("types");//get cached data
      
                 vm.Types = new SelectList(types, "Id", "Name", vm.TypeId);
                 return View(vm);
              }
      
      
              return RedirectToAction("Index", "Home");
      
          }
      }
      

      【讨论】:

      • 在这种情况下,我将做与我想避免的事情相同的事情,而不是再次进入数据库,因此,它不适用于我
      • @sgrysoft 嗨,我现在知道您的考虑了。我更改了使用 IMemoryCache 存储数据的答案。
      【解决方案3】:

      这个案例也可以在微软的文档中看到。

      Documentation

      部分示例:

      if (await TryUpdateModelAsync<Instructor>(
        newInstructor,
        "Instructor",
        i => i.FirstMidName, i => i.LastName,
        i => i.HireDate, i => i.OfficeAssignment))
      {
        _context.Instructors.Add(newInstructor);                
        await _context.SaveChangesAsync();
        return RedirectToPage("./Index");
      }
      PopulateAssignedCourseData(_context, newInstructor);
      return Page();
      

      如果验证失败(或其他原因),将再次调用重新创建数据的方法PopulateAssignedCourseData(_context, newInstructor);

      【讨论】:

        猜你喜欢
        • 2016-04-10
        • 1970-01-01
        • 1970-01-01
        • 2017-11-16
        • 1970-01-01
        • 2015-11-27
        • 2017-11-17
        相关资源
        最近更新 更多