ASP.NET MVC - Proper Use of the View Model and Command

I wrote ASP.NET MVC applications for some time, and I found them a good place to use the command template: we present each user request as a command - a set of input parameters - then this command is processed (processing includes verification and other domain logic), and the result is sent back to the user.

Another thing I used in my applications is view models. I found this to be a more convenient way to pass data to a view than using domain objects as models or populating a ViewData / ViewBag.

These 2 concepts are great for separating data that is displayed to the user from user input and its processing, but they do not quite agree with each other in ASP.NET MVC.

Let's say I want to use commands and view models when developing a simple web store where users browse products and can order a product by specifying their name and email address:

class ProductViewModel 
{
    public ProductViewModel(int id) { /* init */ }
    public int Id { get; set; }
    public string Name { get; set; }
    // a LOT of other properties (let say 50)
}

class OrderProductCommand
{
    public int ProductId { get; set; }

    [Required(ErrorMessage = "Name not specified")]
    public string Name { get; set; }

    [Required(ErrorMessage ="E-Mail not specified")]
    public string Email { get; set; }

    public CommandResult Process() { /* validate, save to DB, send email, etc. */ }
}

When viewing textbooks, etc. I have seen people suggest several ways to do this.

Option 1

Controller:

[HttpGet]
public ActionResult Product(int id)
{
    return View(new ProductViewModel(id));
}

[HttpPost]
public ActionResult Product(OrderProductCommand command)
{
    if (ModelState.IsValid)
    {
        var result = command.Process();
        if(result.Success)
            return View("ThankYou");
        else
            result.CopyErrorsToModelState(ModelState);
    }
    return Product(command.Id);
}

View:

@using (Html.BeginForm())
{
    @Html.Hidden("ProductId", Model.Id)
    @Html.TextBox("Name")
    @Html.TextBox("Email")
    <input type="submit" value="Place order" />
}

Pros : the model and the viewing command are separate from each other, the method HttpPostlooks clean

Cons : I cannot use convenient HTML helpers such as @Html.TextBoxFor(model => model.Email), I cannot use client validation (see my other question )

Option 2

Id, Name Email command viewModel.

:

[HttpPost]    
public ActionResult Product(ProductViewModel viewModel)
{
        var command = new OrderProductCommand();
        command.Id = viewModel.Id;
        command.Name = viewModel.Name;
        command.Email = viewModel.Email;        
        if (ModelState.IsValid)
        // ...
}

:

@Html.TextBoxFor(m => m.Email)
...

: 1

: (, 50?), Name Email ( command, ), POST (. )

3

command viewModel.

:

[HttpPost]
public ActionResult Product(ProductViewModel viewModel)
{
        var command = viewModel.Command;
        if (ModelState.IsValid)
        // ...
}

:

@Html.TextBoxFor(m => m.Command.Email)
...

: 1

: , ( command ), POST (. )

-

2 3, , POST. ( 2 + 1), 50 , . POST POST.

(, , -, ): 4 , ? , ?

+4
2

, - OrderProductCommand .

Product.cshtml:

@model ProductViewModel
...
@Html.Partial("Product_OrderForm", new OrderProductCommand { ProductId = Model.Id })
...

Product_OrderForm.cshtml:

@model OrderProductCommand
...
@using (Html.BeginForm("Product", "Home"))
{
    @Html.HiddenFor(cmd => cmd.ProductId)
    @Html.TextBoxFor(cmd => cmd.Name)
    @Html.TextBoxFor(cmd => cmd.Email)
    <input type="submit" value="Place order" />
}
...

, -, , 1:

[HttpGet]
public ActionResult Product(int id)
{
    return View(new ProductViewModel(id));
}

[HttpPost]
public ActionResult Product(OrderProductCommand command)
{
    // process command...
}
+3

viewModel, 4, .

, , , , , ..

.

, , ViewModel, .

public ActionResult Product(PreOrderProductCommand command)

, Mvc , , formcollection . , ProductViewModel, PreOrderProductCommand.

+1

Source: https://habr.com/ru/post/1613789/


All Articles