Click here to Skip to main content
15,881,852 members
Articles / Web Development / ASP.NET / ASP.NETvNext

How do I post a populated model back to a controller in MVC?

Rate me:
Please Sign up or sign in to vote.
5.00/5 (1 vote)
5 Jan 2016CPOL4 min read 6.6K   5   1
Article discussing how to handle posted data in an MVC controller action by using commands and handlers

In previous posts I've covered different ways of creating and populating view models. Thus far I've only focused on presenting data from the server within a view. But what if I were posting a populated model back to the controller? Well, I could use a view model for that too. The nice thing about MVC is that I don't have to. The default model binder will bind to any type with matching properties. This means that we can create a clear separation around the direction the data travels. We can differentiate between data we display in the view and data we pass back to the server. In this post I'll discuss an alternative approach to passing data back to the server. The Command Pattern.

The theory

The command pattern is a pattern within a compound pattern known as CQRS. This stands for Command Query Responsibility Segregation. The pattern builds on the idea that we can use a different model to read data than we use to update data. For an MVC application, this means we split the way we handle query data from the way we handle command data. Data heading towards the view from the domain is query data. We've queried it from a data source and encapsulate it within a view model. Data heading towards the domain from the view is command data. We encapsulate this within a command. We then use a command handler to execute that command. The handler handles pulling the data out of the command and updating it within the domain.

For more detailed discussions around CQRS, Event Sourcing and DDD in general, take a look at Daniel Whittaker’s blog

The Players

public abstract class Command
{
}

public interface ICommandHandler<in TCommand>
     where TCommand : Command
{
    void Execute(TCommand command);
}

public interface ICommandHandler<in TCommand, out TReturn>
     where TCommand : Command
{
    TReturn Execute(TCommand command);
}

The command pattern I discuss here differs somewhat from the Gang of Four Command Pattern. The main difference is that here the handler contains the execute method. The command itself is just a dumb collection of properties. We should use it exactly like we use a view model. We could add an Execute() method to the command itself, but let’s keep the command simple.

A Practical Example

In this example we’ll focus on a trimmed down form. This form has a DropDownList and a TextBox. Here it is:

Edit user wireframe

Even in this small example we've got some data that is display-specific. We could just use a view model for both display and update purposes. If we did that here we’d have to ignore the display data when performing the update. No big deal if it’s only 1 or 2 properties. But what if the form is long and complex? What if the page has a lot of display logic? We can ignore all of that if we use commands and handlers for our update pipeline. It doesn't even need to be there.

Here’s the ViewModel for our example:

public class EditUserViewModel
{
    public string UserId { get; set; }
    public string FullName { get; set; }
    public string EmailAddress { get; set; }
    public IEnumerable<string> AgeRanges { get; set; }
    public string SelectedAgeRange { get; set; }
}

Here’s the associated Command:

public class EditUserCommand : Command
{
    public string UserId { get; set; }
    public string EmailAddress { get; set; }
    public string SelectedAgeRange { get; set; }
}

Even in this small example, you can see that we're ignoring some of the view model properties. We're just interested in the ones we'll be updating.

Let’s add a handler for this command. Inside the handler, we’re going to find the user by id and update its properties. We’ll then pass it to a UserService for saving. I'm not focusing on the UserService in this article. Let's just assume we have one and it talks to a data store somewhere.

public class EditUserCommandHandler : ICommandHandler<EditUserCommand>
{
    private readonly IUserService userService;

    public EditUserCommandHandler(IUserService userService)
    {
        this.userService = userService;
    }

    public void Execute(EditUserCommand command)
    {
        var user = userService.FindById(command.UserId);
        user.EmailAddress = command.EmailAddress;
        user.AgeRange = command.SelectedAgeRange;
        userService.Update(user);
    }
}

Now that we have our command and handler, let’s hook everything up within a Controller. Our UserController has Edit actions for getting and posting our user data. For the Get action, we’re going to use a factory to create our view model. If you missed my earlier articles on using factories, here’s a recap:

public class UserController : Controller
{
    private readonly IViewModelFactory<string, EditUserViewModel> editUserViewModelFactory;
    private readonly ICommandHandler<EditUserCommand> editUserCommandHandler;

    public UserController(IViewModelFactory<string, EditUserViewModel> editUserViewModelFactory, ICommandHandler<EditUserCommand> editUserCommandHandler)
    {
        this.editUserViewModelFactory = editUserViewModelFactory;
        this.editUserCommandHandler = editUserCommandHandler;
    }

    public ActionResult EditUser(string userId)
    {
        var model = editUserViewModelFactory.Create(userId);
        return View(model);
    }

    [HttpPost]
    public ActionResult EditUser(EditUserCommand command)
    {
        editUserCommandHandler.Execute(command);
        return RedirectToAction("EditUser", command.UserId);
    }
}

For the Post action, we're going to let the default model binder work its magic. We're passing an EditUserCommand into the Action instead of an EditUserViewModel. Our command has UserId, EmailAddress and SelectedAgeRange properties just like the view model. Because of this the model binder creates an EditUserCommand with those properties populated. We can then pass that straight over to the handler to execute it.

Brucey Bonuses

This is a simple example at the moment. More than likely we would have validation in a real application. We might also want to display a success or error message to the user once the handler has executed. I'll leave validation for another article. It's trivial to recreate the view model if validation fails though.

[HttpPost]
public ActionResult EditUser(EditUserCommand command)
{
    if (!ModelState.IsValid)
    {
        var model = editUserViewModelFactory.Create(command.UserId);
        return View(model);
    }

    editUserCommandHandler.Execute(command);
    return RedirectToAction("EditUser", command.UserId);
}

Notice that we're not re-mapping the values from the command to the view model. The default model binder remembers them all for us.

As far as displaying a message in the view goes, we can use TempData for that. Here's an article explaining just how to do that:

Wrapping up

We've covered a lot here, so let's recap:

  • We've looked at CQRS, an approach for splitting our read data from our update data.
  • To enable this in MVC, we've implemented the Command pattern.
  • We've created an EditUserCommand and EditUserCommandHandler to update our user data.
  • We've passed the command to the post action of our controller and executed it via the handler.
  • We've also looked at repopulating our view via the factory if we have validation errors.

View original article

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Technical Lead Levelnis Ltd
United Kingdom United Kingdom
Follow along my journey as I create a newsletter about launching websites. Just message me with "I'm in" and I'll add you

Comments and Discussions

 
GeneralGood idea, but... Pin
Klaus Luedenscheidt2-Jan-16 18:01
Klaus Luedenscheidt2-Jan-16 18:01 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.