如何在ASP.NET MVC中的POST请求之间传输ViewModel数据?

时间:2010-11-16 03:06:58

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

我有一个像这样的ViewModel:

public class ProductEditModel
{
    public string Name { get; set; }
    public int CategoryId { get; set; }
    public SelectList Categories { get; set; }

    public ProductEditModel()
    {
        var categories = Database.GetCategories(); // made-up method
        Categories = new SelectList(categories, "Key", "Value");
    }
}

然后我有两个使用此模型的控制器方法:

public ActionResult Create()
{
    var model = new ProductEditModel();
    return View(model);
}

[HttpPost]
public ActionResult Create(ProductEditModel model)
{
    if (ModelState.IsValid)
    {
        // convert the model to the actual entity
        var product = Mapper.Map(model, new Product());
        Database.Save(product);
        return View("Success");
    }
    else
    {
        return View(model); // this is where it fails
    }
}

用户第一次进入Create视图时会看到一个类别列表。但是,如果它们验证失败,则会将View发送回它们,但这次Categories属性为null。这是可以理解的,因为ModelBinder如果不在POST请求中则不会持久Categories。我的问题是,保持Categories的最佳方法是什么?我可以这样做:

[HttpPost]
public ActionResult Create(ProductEditModel model)
{
    if (ModelState.IsValid)
    {
        // convert the model to the actual entity
        var product = Mapper.Map(model, new Product());
        Database.Save(product);
        return View("Success");
    }
    else
    {
        // manually populate Categories again if validation failed
        model.Categories = new SelectList(categories, "Key", "Value");
        return View(model); // this is where it fails
    }
}

但这是一个丑陋的解决方案。我还能坚持下去吗?我不能使用隐藏字段,因为它是一个集合。

3 个答案:

答案 0 :(得分:2)

我通常将我的列表(用于下拉列表)实现为只读属性。当View获取值时,属性自包含在返回值所需的内容上。

public SelectList Categories
{
    get
    {
        var categories = Database.GetCategories(); // made-up method
        return new SelectList(categories, "Key", "Value");
    }
}

如果有必要,您可以从包含已发布并绑定到您的班级实例的ID的属性中获取当前所选项目(即验证失败)。

答案 1 :(得分:2)

我会使用存储库来获取所需的任何数据,并且不认为这是一个丑陋的解决方案:

[HttpPost]
public ActionResult Create(ProductEditModel model)
{
    if (!ModelState.IsValid)
    {
        // manually populate Categories again if validation failed
        model.Categories = Repository.GetCategories();
        return View(model);
    }

    // convert the model to the actual entity
    var product = Mapper.Map(model, new Product());
    Database.Save(product);

    // I would recommend you to redirect here
    return RedirectToAction("Success");
}

为了进一步重构,我建议你观看Jimmy Bogard的精彩Putting Your Controllers on a Diet视频演示。

答案 2 :(得分:0)

在我的情况下,我有一个BaseModel类,我将所有这些属性列表保存为类属性。

如下例所示:

public IEnumerable<SelectListItem> CountryList
{
    get
    {
        return GetCountryList().Select(
            t => new SelectListItem { Text = t.Name, Value = Convert.ToString(t.CountryID) });
    }
}

GetCountryList()是一个向Singleton请求数据的函数。这只会在应用程序生命周期中发生一次

执行此操作的另一种方法是,如果这些列表非常大,那么将使用带有返回SelectListItem的查找表的静态实用程序类。

如果您需要访问不时更改的列表,那么就不要使用Singleton类。