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

发布于 2024-10-02 19:38:23 字数 1701 浏览 0 评论 0 原文

我有一个像这样的 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 视图时,他们会看到一个类别列表。但是,如果验证失败,视图将发送回给他们,但这次 Categories 属性为 null。这是可以理解的,因为如果 Categories 不在 POST 请求中,则 ModelBinder 不会保留它。我的问题是,保持 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
    }
}

但这是一个丑陋的解决方案。我还能怎样坚持呢?我无法使用隐藏字段,因为它是一个集合。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

愁以何悠 2024-10-09 19:38:23

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

[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) 的让你的控制器节食视频演示。

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.

耳钉梦 2024-10-09 19:38:23

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

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

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

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.

冷夜 2024-10-09 19:38:23

就我而言,我有一个 BaseModel 类,其中我将所有这些属性列表保留为类属性。

就像下面的示例一样:

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

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

。另一种方法(如果这些列表非常大)是使用一个静态实用程序类,其中包含返回 SelectListItem 的查找表。

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文