在后期操作中重用模型数据

时间:2012-02-09 01:52:42

标签: c# asp.net-mvc

在我的viewmodel中,我有一个从数据库中获取的项目列表,然后发送到视图。我想知道是否有可能避免在我点击Post动作时需要重新填充options属性并且需要返回模型(对于验证错误而不是什么)?

在网络表格中,这不是必需的。

编辑:我不清楚。我的问题在于我用于DropDownLists的SelectList选项。一切都被发布,但如果我必须返回视图(模型无效),我必须从数据库重新加载选项!我想知道这是否可以避免。

我的观点模型:

public class TestModel
{
    public TestModel()
    {
        Departments = new List<SelectListItem>();
    }

    public string Name { get; set; }
    public int Department { get; set; }
    public IEnumerable<SelectListItem> Departments { get; set; }
}

我的观点:

@model MvcApplication1.Models.TestModel    
@using (Html.BeginForm())
{
    @Html.TextBoxFor(m => m.Name)

    @Html.DropDownListFor(m => m.Department, Model.Departments)

    <input type=submit value=Submit />
}

我的控制器(请注意 HttpPost 上的评论):

public ActionResult Index()
{
    TestModel model = new TestModel
    {
        Name = "Rafael",
        Department = 1,
        Departments = new List<SelectListItem>
        {
            new SelectListItem { Text = "Sales", Value = "1" },
            new SelectListItem { Text = "Marketing", Value = "2", Selected = true },
            new SelectListItem { Text = "Development", Value = "3" }
        }
    };

    // Departments gets filled from a database.

    return View(model);
}

[HttpPost]
public ActionResult Index(TestModel model)
{
if (!ModelState.IsValid)
{
    //Do I have to fill model.Departments again!?!?!?

    return View(model); 
}
else { ...  }
}

提前致谢。

编辑:仅供参考,我的解决方案是使用Session变量。

3 个答案:

答案 0 :(得分:1)

只需要强烈键入您的视图,并将控制器方法更改为具有该类类型的参数。

即视图

@model MyNamesspace.Models.MyModel
...
@using (Html.BeginForm())
{
    ....
}

你发布的控制器方法。

[HttpPost]
public ActionResult MyAction(MyModel model)
{
    ...
}

编辑:还要确保您有模型的每个属性的表单字段,您需要将其发布到控制器。我的例子是使用Razor BTW。

答案 1 :(得分:0)

我在尝试在MVC中创建Order向导时遇到了类似的问题(向导的每个页面都被实现为AJAX加载的部分视图)。我非常怀疑它是建议的方法,但我解决这个问题的方法是在我的向导调用的每个操作中调用自定义MergeChanges方法:

public Order MergeChanges(Order newOrder)
{
    var sessionHistory = (List<string>)Session["sessionHistory"];

    if (sessionHistory == null || sessionHistory.Count == 0)
    return MergeChanges(newOrder, -1);

    return MergeChanges(newOrder, MasterViewController.GetStepNumberByName(sessionHistory.Last()));
}

public Order MergeChanges(Order newOrder, int step)
{
    PreMerge(newOrder);

    Order result = null;
    try
    {
        ApplyLookups(ref newOrder);
        Order oldOrder = (Order)Session["order"];

        if (oldOrder == null)
        {
             Session["order"] = newOrder;
             result = newOrder;
        }
        else
        {
            List<TypeHelper.DecoratedProperty<ModelPageAttribute>> props = null;
            newOrder.GetType().GetDecoratedProperty<ModelPageAttribute>(ref props);
            props = props.Where(p => (p.Attributes.Count() > 0 && p.Attributes.First().PageNumber.Contains(step))).ToList();
            foreach (var propPair in props)
            {
                object oldObj = oldOrder;
                object newObj = newOrder;
                if (!string.IsNullOrEmpty(propPair.PropertyPath))
                {
                    bool badProp = false;
                    foreach (string propStr in propPair.PropertyPath.Split('\\'))
                    {
                        var prop = oldObj.GetType().GetProperty(propStr);
                        if (prop == null)
                        {
                            badProp = true;
                            break;
                        }

                        oldObj = prop.GetValue(oldObj, BindingFlags.GetProperty, null, null, null);
                        newObj = prop.GetValue(newObj, BindingFlags.GetProperty, null, null, null);
                     }
                     if (badProp)
                          continue;
                 }

                 if (newObj == null)
                     continue;

                 var srcVal = propPair.Property.GetValue(newObj, BindingFlags.GetProperty, null, null, null);
                 var dstVal = propPair.Property.GetValue(oldObj, BindingFlags.GetProperty, null, null, null);

                  var mergeHelperAttr = propPair.Property.GetAttribute<MergeHelperAttribute>();
                   if (mergeHelperAttr == null)
                   {
                        if (newObj != null)
                            propPair.Property.SetValue(oldObj, srcVal, BindingFlags.SetProperty, null, null, null);
                   }
                   else
                   {
                       var mergeHelper = (IMergeHelper)Activator.CreateInstance(mergeHelperAttr.HelperType);
                       if (mergeHelper == null)
                           continue;

                       mergeHelper.Merge(context, HttpContext.Request, newObj, propPair.Property, srcVal, oldObj, propPair.Property, dstVal);
                    }
               }
               result = oldOrder;
          }
    }
    finally
    {
    PostMerge(result);
    }
    return result;
}

由于我的情况是使用向导执行此操作,因此只应用于每个页面的特定值,以便仅考虑向导当前页面已知的属性,我已实现了一些属性,a(无可置疑地过于复杂) ViewController图层和自定义验证图层。我可以分享更多的代码,但如果你没有处于如此复杂的情况,上面的代码会做咕噜咕噜的工作。如果有更好的方法,我希望从这个问题的答案中学习它,因为这是一个PITA。

答案 2 :(得分:0)

我很惊讶这个问题没有经常出现,我也很惊讶,显而易见的(恕我直言)答案现在不是标准做法:几乎所有的POST都应该是基于Ajax的。这解决了一大堆问题,包括

  1. 当您拥有以下内容时,无需重新填充表单数据验证错误或应用程序错误(例外)。当您具有客户端状态时(以真正丰富的Web应用程序方式),这是特别理想的。
  2. 没有强制执行客户端验证。验证可以是100%服务器端(无论如何必须)并且用户体验几乎相同。
  3. 当然,为了构建一个框架,你需要做一些初步的工作,例如,我有一套AjaxUpdate,AjaxNothing,AjaxRedirect,AjaxErrors ... ActionResult类型,它们渲染Json,由它处理一些自定义的Javascript。但是一旦你实现了这一点,它就会顺利进行。