Monday, October 17, 2011

Viewmodel extractors in ASP.NET MVC


Last week, I wrote something on assembling viewmodels in ASP.NET MVC. In that post, I said it would be nice to have a layer between my controller and my domain services that would assemble viewmodels for me. This would work one-way. In the other direction - from controller to domain services - I would just take a piece of my composite viewmodel and pass that directly to my domain services.

Well, that last part didn't really work out eventually. I found it hard to find real-world scenarios where I could just pick a piece of my viewmodel and directly pass it to the domain services.

I was in need of something that could extract my domain models from my dumb viewmodels. I really didn't want this logic to be a fixed part of my viewmodel, nor did I want to make helper classes for these utility methods. Looking for a place to put this, I thought of a set of extension methods that pulls out every useful domain model per viewmodel.
public static class AddEntryViewModelExtractors
{
    public static Entry ExtractEntry(this AddEntryViewModel addEntryViewModel) 
    {
        var entry = new Entry();
 
        entry.Activity = new Activity();
        entry.Activity.Name = addEntryViewModel.ActivityName;            
        entry.Meta = addEntryViewModel.Meta;
 
        return entry;
    }
}

This makes it possible to do something like this in my controller.

public ActionResult Add(AddEntryViewModel addEntryViewModel)
{
    if (ModelState.IsValid)
    {
        _entryService.AddEntry(addEntryViewModel.ExtractEntry());
 
        return RedirectToAction("Index", "Home");
    }
    else
    {
        return View(addEntryViewModel);
    }
}

So far, I'm liking this approach, pushing code away from the controller, helping me to keep my controllers as lean as possible. I also enjoy that it's trivial to test these extractor methods.

Oh btw, I had a chance to look at AutoMapper, but I haven't decided yet whether I find it helpful or not. I hardly came across scenarios where simple mappings were sufficient.

As always, I welcome your feedback and thoughts!

6 comments:

  1. I've implemented something similar, but we use automapper inside your ExtractEntity model. That way you can get the benefit of Automapper for mapping x in one class to x in another, whilst still maintaining control to perform multiple mappings in sequence and other hard-coded mappings, etc. Automapper is a great timesaver once you get your head around when/where to use it.

    ReplyDelete
  2. I've experienced this need many times. Like the first commenter, I opted to use an automapper approach. Further, since I don't like adding utility classes just to perform mapping operations, I usually just add a method on my view model to do the mapping. For example, if I have a CustomerViewModel that eventually maps back to a Customer domain object, I'll simply add a ToCustomer() method to the CustomerViewModel class that uses automapper to map the properties and return an instance of the Customer object. I also toyed around with adding a constructor to the Customer object that accepted a CustomerViewModel instance and performed the mappings there, but soon realized that my Customer object should be ignorant of the presentation layer and abandoned that approach.

    ReplyDelete
  3. @Paul I get your drift. I feel like I probably just don't get Automapper yet.

    @Chris Glad to hear that you're using somewhat the same approach. I thought about naming the methods ToEntity as well, but that comes over to me as the ViewModel only contains one entity. And like you said, the other way around is just wrong.

    ReplyDelete
  4. Make both AddEntryViewModel and your Entry domain class implement an interface (IEntry), and have _entryService.AddEntry() take IEntry instead of Entry.

    In some cases, it might add a little logic to your view model, but it would eliminate having to eliminate parallel classes.

    ReplyDelete
  5. I struggled with just these issues as well!

    I chose to make a ViewModelFactory layer whose job was to map service layer objects to view model objects and an EntityModelFactory to do the reverse.

    So a controller would call a service interface(s) to "get some data" and then used the
    ViewModel Factory layer to map entity items to UI ViewModel instances.

    It kept it all quite clean and I had used POCO with EF 4 so Db stuff was no where near the UI!

    The biggest issue has been keeping any data retrieval out of the factory layer, typically for issues with refreshing drop-downs etc after a form input error!

    ReplyDelete
  6. Use automapper or value injector. Not need for this.

    ReplyDelete