search
top

How to keep your Get & Post ViewModels DRY

I’ve been convinced that I should not be using the same view model class for both loading the view and the posted controller action parameter. The trouble is it allows data to be transferred back which may not be necessary and it needlessly increases our attack surface.

Don’t get me wrong, I’ve always had unit tests specifically monitoring that only necessary view model properties are being pushed back to the entity model, but still, seperate viewmodels will allow me to eliminate that risk completely and even remove those tests.

To be honest, the idea of having 2 view model classes that are nearly identical is not very appealing, especially given that if the structure deviates, the default model binder will no longer work with those properties which are out of sync with its partner view model. Don’t get me wrong, I’m very liberal when it comes to class quantities, but this was enough to make me uncomfortable.

So I’m happy that I realized the post view model class can always be used as a base class for the get view model class. I mean it has to be in order for the default model binder to work.

Using inheritance for these classes will a) keep these classes in sync so the default model binder will always work, b) follow the DRY principle and c) keep my sanity.

In a nutshell, rather than having

1
2
3
4
5
6
7
public class FooViewModel
{
  public int Id { get; set; }
  public string Name { get; set; }
  public FooStatusEnum Status { get; set; }
  public DateTime CreateDate { get; set; }
}

We can instead have

1
2
3
4
5
6
7
8
9
10
11
public class FooPostViewModel
{
  public int Id { get; set; }
  public string Name { get; set; }
}
 
public class FooGetViewModel : FooPostViewModel
{
  public FooStatusEnum Status { get; set; }
  public DateTime CreateDate { get; set; }
}

So FooGetViewModel is loaded and passed into the view.

1
2
3
4
5
6
7
[HttpGet]
public ActionResult Edit(int Id)
{
  var vm = new FooGetViewModel();
  // populate view model
  return View(vm);
}

And FooPostViewModel is the parameter in our controller action, receiving everything necessary, but only what is necessary.

1
2
3
4
5
6
7
[HttpPost, ActionName("Edit")]
public ActionResult EditPost(FooPostViewModel vm)
{
  // get foo
  // update foo with the vm data
  // redirect back to detail 
}

So FooGetViewModel is loaded and passed into the view, and FooPostViewModel is the parameter in our controller action, receiving everything necessary, but only what is necessary.

I like this. It’s clean, simple and elegant.

Leave a Reply

Your email address will not be published.

top