开发者

How do I transfer ViewModel data between POST requests in ASP.NET MVC?

I have a ViewModel like so:

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");
    }
}

Then I have two controller methods that uses this model:

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
    }
}

The first time the user goes to the Create view, they are presented with a list of categories. However, if they fail validation, the View is sent back to them, except this time the Categories开发者_如何学运维 property is null. This is understandable because the ModelBinder does not persist Categories if it wasn't in the POST request. My question is, what's the best way of keeping Categories persisted? I can do something like this:

[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
    }
}

But this is an ugly solution. How else can I persist it? I can't use a hidden field because it's a collection.


I would use the repository to fetch whatever data is needed and don't think it's an ugly solution:

[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");
}

To further refactor this I would recommend you watching the excellent Putting Your Controllers on a Diet video presentation by Jimmy Bogard.


I typically implement my lists (for drop downs) as a readonly property. When the View gets the value the property is self contained on what it needs to return the values.

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

If necessary you can grab the currently selected item (i.e. validation failed) from the property containing the id that was posted and bound to the instance of your class.


In my case I have a BaseModel class where I keep all those property list as class attributes.

Like in the following sample:

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

GetCountryList() is a function that ask a Singleton for data. This would only happen once in the app lifecycle

Another way for doing this, and if those lists are pretty big, would be to have a static utility class with the lookup table that returns the SelectListItem.

If you need to access a list that change from time to time then simply dont use a Singleton class.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜