ASP.NET MVC 5 Html.HiddenFor呈现不正确的属性值,但Model.Property呈现正确的值

时间:2013-12-05 15:23:57

标签: asp.net-mvc razor asp.net-mvc-5

  • Visual Studio 2013 Professional
  • C#
  • .NET 4.5 Framework
  • ASP.NET MVC 5

在我的视图模型类中,我有一个名为SerializedSelf的属性,用于将自身表示为序列化字符串并反序列化,但仅限于尚未初始化的任何属性。我使用它来通过使用具有此值的单个隐藏输入来简化视图模型的往返。在传递给控制器​​方法之前,框架将在模型中填充与模型相关的任何其他输入。

我遇到的问题是,在我的剃刀视图代码中,这不能正确呈现:

 @Html.HiddenFor(model => model.SerializedSelf)

我还没有确定它是否正在序列化一个完全未初始化的MyViewModel对象,或者它是否是来自其他地方的实例。

但这可以正常运作:

 <input type="hidden" name="SerializedSelf" id="SerializedSelf" value="@Model.SerializedSelf" />

所以我猜它可能与lambda表达式和附件有关???当我在源代码中单步执行时,SerializedSelf在调用return View("myView", model)之前就具有正确的值。

以下是视图模型代码:

using Newtonsoft.Json;
using System;
using System.Text;

public class MyViewModel
{
    [JsonIgnore]
    public string SerializedSelf
    {
        get
        {
            return JsonConvert.SerializeObject(this);
        }
        set
        {
            if (string.IsNullOrWhiteSpace(value)) return;

            string json = value;
            MyViewModel copy = JsonConvert.DeserializeObject<MyViewModel>(json);
            if (Message == null) Message = copy.Message;
            if (Phone == null) Phone = copy.Phone;
            // ...
        }
    }

    public string Message { get; set; }
    public string Phone { get; set; }
    // ...

}

之间有什么区别:

@Html.HiddenFor(model => model.SerializedSelf)

@Model.SerializedSelf

什么会导致前者不准确?

更新2013/12/5:我从SerializedSelf删除了逻辑,并在适当的时间显式序列化/反序列化为SerializedSelf,以测试是否有任何影响。它没有任何影响,行为如上所述。

更新2013/12/5:删除了HTML编码和解码。从之前的解决问题的尝试中遗留下来。

更新2013/12/5:我今天没有时间研究这个问题,但我想到[JsonIgnore]上的SerializedSelf属性可能是{{1}}属性是罪魁祸首,它干扰了MVC框架中的某些东西。我会尽快调查并更新。

3 个答案:

答案 0 :(得分:11)

如果在呈现视图以响应POST请求后发生这种情况,您应该知道HTML帮助程序使用旧值,因为它们认为存在验证错误。您可以在此blog post中找到详细说明。

答案 1 :(得分:4)

这不仅发生在POST请求中,也发生在GET请求中。一个例子:

// Create a duplicate of the provided vacancy
[HttpGet]
public ActionResult EmployerVacancyDuplicate(Guid vacancyID, string returnUrl)
{
     // lets imagine that vacancyID = '48B3D78D-8D1C-48C6-999E-62CB170D39F7'

     ...
     // Get vacancy item from repository
     Vacancy vacancy = VacancyRepository.GetUserVacancy(userID, vacancyID);
     // vacancy.ID = '48B3D78D-8D1C-48C6-999E-62CB170D39F7'

     // Create model for vacancy editing from provided vacancy item
     EditVacancyModel model = EditVacancyModel.CreateItem(vacancy);
     // model.ID = '48B3D78D-8D1C-48C6-999E-62CB170D39F7'

     // Reset the ID in the model 
     model.ID = new Guid();
     // model.ID = '00000000-0000-0000-0000-000000000000'

     return View(model);
}

执行后,如果使用lambda表达式

@Html.HiddenFor(model => model.ID)

你会看到ID的值等于'48B3D78D-8D1C-48C6-999E-62CB170D39F7'

<input id="RID" name="RID" type="hidden" value="48B3D78D-8D1C-48C6-999E-62CB170D39F7" />

因此即使是GET请求,系统也会使用模型的初始状态(初始属性值)。

正如链接中所提到的,UfukHacıoğulları提供的解决方案是:

ModelState.Clear();
在View(模型)调用之前

;

// Create a duplicate of the provided vacancy
[HttpGet]
public ActionResult EmployerVacancyDuplicate(Guid vacancyID, string returnUrl)
{
     // lets imagine that vacancyID = '48B3D78D-8D1C-48C6-999E-62CB170D39F7'

     ...
     // Get vacancy item from repository
     Vacancy vacancy = VacancyRepository.GetUserVacancy(userID, vacancyID);
     // vacancy.ID = '48B3D78D-8D1C-48C6-999E-62CB170D39F7'

     // Create model for vacancy editing from provided vacancy item
     EditVacancyModel model = EditVacancyModel.CreateItem(vacancy);
     // model.ID = '48B3D78D-8D1C-48C6-999E-62CB170D39F7'

     // Reset the ID in the model 
     model.ID = new Guid();
     // model.ID = '00000000-0000-0000-0000-000000000000'

     // Clear the state of the model
     ModelState.Clear();

     return View(model);
}

答案 2 :(得分:1)

似乎HTML Helpers正在提取路线值以及模型值。

我为极其常见的public static MvcHtmlString HiddenIdFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression) { var value = ModelMetadata.FromLambdaExpression(expression, html.ViewData).Model.ToString(); var builder = new TagBuilder("input"); builder.MergeAttribute("type", "hidden"); builder.MergeAttribute("name", ExpressionHelper.GetExpressionText(expression)); builder.MergeAttribute("value", value); return MvcHtmlString.Create(builder.ToString(TagRenderMode.SelfClosing)); } 案例创建了这个助手:

.option100{
    width:100px !important;
} 

<select id="fieldOfInterestSelect" name="fieldOfInterest" class="form-control" required="">
  <option value="-1">SELECT ONE</option>
  <option value="4893" class="option100">Actual(R)rrrrrrrrrrrrrrrr</option>
  <option value="4891" class="option100">Customerrrrrrrrrrrrrr</option>
  <option value="4892" class="option100">Daterrrrrrr</option>
  <option value="4894" class="option100">Forecast(R)RRRRRR</option>
</select>