首次POST后,隐藏字段的值不正确

时间:2014-05-23 21:50:51

标签: c# html asp.net-mvc

我有一个视图,用户可以使用HTML表单记录在Activity上花费的时间。因此,该视图循环遍历所有活动的列表,并为每个活动生成一个日志时间表(包含在_LogTime部分视图中)。从Index视图传递到局部视图的唯一信息是ActivityId,它以隐藏的形式放置。其余所需信息由用户通过from提供。

我遇到的问题是,一旦我提交了其中一个表单,所有表单的隐藏字段都将设置为我刚刚提交的表单的ActivityId。值得注意的是,当页面首次加载时(在我提交任何表单之前),隐藏字段是正确的,当我第一次提交表单时,正确的活动会将时间记录到它(并且没有其他错误地获取时间记录)。但之后的任何表单提交只会记录我第一次提交表单的活动的时间。

知道这里发生了什么吗?为什么所有隐藏字段都设置为相同的ActivityId?为什么只有在第一次POST后呢?如果您需要澄清问题,请告诉我。

型号:

public class Activity
{
    public int ActivityId { get; set; }
    public string Name { get; set; }
}

public class UserActivity
{
    public int UserId { get; set; }
    public int ActivityId { get; set; }
    public int Duration { get; set; }
    public DateTime Date { get; set; }
}

查看:

// Index View
@foreach (Activity activity in Model)
{
    @Html.Partial("_LogTime", new UserActivity(activity.ActivityId))
}

// _LogTime Partial View
@using (Html.BeginForm())
{
    <fieldset>
        @Html.HiddenFor(model => model.ActivityId)
        @Html.EditorFor(model => model.Duration)
        @Html.EditorFor(model => model.Date)

        <input type="submit" value="LOG TIME" />
    </fieldset>
}

控制器:

public class ActivityController : Controller
{
    private readonly DbContext _db = new DbContext();

    public ActionResult Index()
    {
        return View(_db.Activities.ToList());
    }

    [HttpPost]
    public ActionResult Index(UserActivity activity)
    {
        if (ModelState.IsValid)
        {
            _db.UserActivities.Add(activity);
            _db.SaveChanges();
        }

        return View(_db.Activities.ToList());
    }
}

2 个答案:

答案 0 :(得分:2)

您遇到的是由于html帮助器方法自动更新具有相同名称的post变量的表单元素。值存储在ModelState中。解决此问题的一种方法是从ModelState中删除违规条目。

另一种可能的解决方法是改为重定向。

[HttpPost]
public ActionResult Index(UserActivity activity)
{
    if (ModelState.IsValid)
    {
        _db.UserActivities.Add(activity);
        _db.SaveChanges();
    }

    // Remove the ActivityId from your ModelState before returning the View.
    ModelState.Remove("ActivityId")

    return View(_db.Activities.ToList());
}

正如下面的评论所见,使用Remove方法可以指示应用程序流程的更深层次问题。我同意Erik的观点。正如他指出的那样,重新设计应用程序的流程可能是一项耗时的任务。

当遇到问题所指示的行为时,如果有办法在不修改ModelState的情况下解决问题,那么这将是首选解决方案。一个恰当的例子可能是这个问题影响了多个元素。

为了完整起见,这是另一种解决方案:

[HttpPost]
public ActionResult Index(UserActivity activity)
{
    if (ModelState.IsValid)
    {
        _db.UserActivities.Add(activity);
        _db.SaveChanges();
    }

    return RedirectToAction("Index");
}

在我的评论家沉默的最后,这是他无法想出的改写。

// Index View
@using (Html.BeginForm())
{

    @for (var i = 0; i < Model.Count; i++)
    {
        <div>
            @Html.HiddenFor(model => model[i].ActivityId)
            @Html.EditorFor(model => model[i].Duration)
            @Html.EditorFor(model => model[i].Date)
        </div>
    }
    <input type="submit" value="LOG TIME ENTRIES" />

}

// Controller Post Method
[HttpPost]
public ActionResult Index(List<UserActivity> activities)
{
    if (ModelState.IsValid)
    {
        foreach( var activity in activities ) 
        {
            var first = _db.UserActivities
                  .FirstOrDefault(row => row.ActivityId == activity.ActivityId );
            if ( first == null ) {
               _db.UserActivities.Add(activity);
            } else {
               first.Duration = activity.Duration;
               first.Date = activity.Date;
            }
        }
        _db.SaveChanges();

        return RedirectToAction("index");
    }

    // when the ModelState is invalid, we want to 
    // retain posted values and display errors.
    return View(_db.Activities.ToList());
}

答案 1 :(得分:0)

我从不在Controller中使用全局变量。 我宁愿把所有隐藏的值,也就是那些在foreach局部视图中的值放在表单中。

这样,您传递整个列表并在此之后添加一个。 现在我认为你传递一个空行并将最后一行添加到那里。

可以肯定的是,你可以在post函数中添加一个断点。

@using (Html.BeginForm())
{
    // Index View
    @foreach (Activity activity in Model)
    {
        @Html.Partial("_LogTime", new UserActivity(activity.ActivityId))
    }

    // _LogTime Partial View

    <fieldset>
        @Html.HiddenFor(model => model.ActivityId)
        @Html.EditorFor(model => model.Duration)
        @Html.EditorFor(model => model.Date)

        <input type="submit" value="LOG TIME" />
    </fieldset>
}