0

Let's say I have a form with the following "business object" in mind:

public class MyObject
{
    public string Name { get; set; }
    public User OtherUser { get; set; }
    public DateTime CreateDate { get; set; }
    public DateTime? StartDate { get; set; }
    public DateTime? EndDate { get; set; }
}

When the user enters data into any of the fields, I need to round-trip what they entered in order to allow them to correct any fat-fingerings and the like, without having to retype their response.

Because I cannot round-trip a value like 'Feb 31, 2011' in a DateTime field, I end up using an View Model object like this:

public class MyObjectViewModel
{
    public string Name { get; set; }
    public string OtherUserName { get; set; }
    public string CreateDate { get; set; }
    public string StartDate { get; set; }
    public string EndDate { get; set; }
}

Ok, all is fine and dandy, I can get this to render exactly the same as the bare business object and the values round-trip when using the following pattern:

public ActionResult Create(FormCollection form)
{
    var model = new MyObjectViewModel();
    if (TryUpdateModel(model, form) && ModelState.IsValid)
    {
         // ...
    }

    return View(model);
}

I having the following questions:

  1. What is the cleanest way to get the data out of the View Model and into the Business Object? (i.e. What fills out the rest of the method above?)
  2. Can I reduce the duplication of field names between the objects, so as to consolidate my changes?
  3. In the above example, the field OtherUserName is a string that needs to be converted into a User object. Whose responsibility is this? The Controller? The ViewModel? A model binder?

3 Answers 3

2
  1. AutoMapper
  2. You don't need to.
  3. The mapper

Example:

[HttpPut]
public ActionResult Create(MyObjectViewModel viewModel)
{
    if (!ModelState.IsValid)
    {
        // there are validation errors => redisplay the view
        return View(viewModel);
    }
    var model = Mapper.Map<MyObjectViewModel, MyObject>(viewModel);
    _repository.DoSomethingWithTheModel(model);
    return RedirectToAction("Success")
}
Sign up to request clarification or add additional context in comments.

5 Comments

+100 for AutoMapper. To reinforce #2, eliminating duplicate, imo, should not be the strongest motivation. I seem to find duplication in certain points - ViewModels as an application layer boundary crossing being one such point - has a net reduction in complexity. That is, yes having some objects that mirror each other adds complexity. But that complexity is relatively small. Reducing the coupling between Views and Domains has a larger effect, again imo.
Interesting... Will automapper update an existing object's properties?
@John, AutoMapper automatically maps properties with the same names. If there are different names/rules you need to define them.
Also, what would #3 look like? I'm already using my own reflection-based mechanism to do mapping, and I was dissatisfied with how the work was being done.
@John, this will depend on how the User object relates to a string.
0

Your problem is caused mostly by allowing the view model to contain invalid data. Use the same strong type on your view model as on your data class, add a little javascript validation and "Feb 31" will never make it to the server. Parsing the types is definitely the responsibility of the model binder - you don't need to do anything, your controller action just gets passed a strongly typed model object. User may be a little different though - the primary difference may be that your viewmodel has a field named username and the data model needs a user object with information that wasn't on the form. Your controller would need to fetch/create the appropriate user object and add it to the data class.

You will still have some validation happening server side after the model is parsed, but that will be the more custom stuff like checking that fields are consistent with each other rather than simple string format issues.

For reducing duplication, I posted my style of models on another question - asp.mvc model design

Comments

-1

1) You can strongly type your view so that the view passes the controller a "MyObject" type. 2) Not sure I understand what you are asking 3) Why not make this field an ID that contains the foreign key of the linked ID?

Also - there is no reason why a datetime field cant be passed to the view and back left as is. You don't need to pass it as a string and convert.

PS - I'd recommend looking at this tutorial - should clarify my answers for you.

http://www.asp.net/mvc/videos/what-is-aspnet-mvc-80-minute-technical-video-for-developers-building-nerddinner

2 Comments

1) First, my views are strongly typed. Also, no, if I do that, the data doesn't round-trip and I can't update an existing database object that way. 3) You want me to make the user type in the ID of objects in the database? I don't think that that is going to go over well.
1) Then the cleanest way is to tell your controller to expect a "MyObject" type instead of a FormCollection. If you are using a strongly typed view, binding is as easy as that. 3) No, I typically retreive a friendly name via JSON/AJAX and bind it to a dropdown. THe user can then select that, but the ID is passed to the controller (you need some javascript help to do this - its needed in the view to bind the selected ID to a hidden model ID field).

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.