Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET MVC 3 Viewmodel Pattern

I am trying to work out the best way of using a viewmodel in the case of creating a new object.

I have a very simple view model that contains a contact object and a select list of companies.

private ICompanyService _Service;
public SelectList ContactCompanyList { get; private set; }
public Contact contact { get; private set; }

public ContactCompanyViewModel(Contact _Contact)
{
    _Service = new CompanyService();
    contact = _Contact;
    ContactCompanyList = GetCompanyList();
}

private SelectList GetCompanyList()
{
    IEnumerable<Company> _CompanyList = _Service.GetAll();
    return new SelectList(_CompanyList, "id", "name");       
}

I then have contact controller that uses this viewmodel and enable me to select a related company for my contact.

[Authorize]
public ActionResult Create()
{                       
    return View(new ContactCompanyViewModel(new Contact()));
}

My issue is with the create method on the controller.

[Authorize]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Contact _Contact)
{
    try
    {
        _Service.Save(_Contact);
        return RedirectToAction("Index");
    }
    catch
    {
       return View();
    }
}

The problem is that the view returns an empty contact object, but! the company id is populated, this is because the dropdown list explicitly declares its field name.

 @Html.DropDownList("parent_company_id",Model.ContactCompanyList)

The standard html form fields pass the objects values back in the format of contact.forename when using the HTML.EditorFor helper...

@Html.EditorFor(model => model.contact.forename)

I can access them if I use a FormCollection as my create action method paremeter and then explicitly search for contact.value but I cannot use a Contact object as a parameter to keep my code nice and clean and not have to build a new contact object each time.

I tried passing the actual view model object back as a parameter but that simply blows up with a constructor error (Which is confusing seeing as the view is bound to the view model not the contact object).

Is there a way that I can define the name of the Html.EditFor field so that the value maps correctly back to the contact object when passed back to the create action method on my controller? Or Have I made some FUBAR mistake somewhere (that is the most likely explanation seeing as this is a learning exercise!).

like image 447
David Abraham Avatar asked Mar 04 '12 10:03

David Abraham


People also ask

Is Razor pages MVC or MVVM?

The view model pattern is used extensively in MVC application development, where it mainly represents data, but typically little behaviour. In Razor Pages, the PageModel is also the view model. Razor Pages is sometimes described as implementing the MVVM (Model, View ViewModel) pattern.

Can I use ViewModel in MVC?

The concept of ViewModels is not only for ASP.NET MVC. It is also used in MVC, MVP, and MVVM design patterns. In this article, you'll learn what are ViewModels and how can we manage and clean the code with ViewModels.

Is ASP.NET MVC or MVVM?

They're all MVC and you pretty much just incorporate a view model if you want one. With ASP.NET MVC, in particular, you just create a class, generally with a name in the form of [Model Name]ViewModel or [Model Name]VM .

Can a ViewModel have multiple models?

ViewModel is nothing but a single class that may have multiple models. It contains multiple models as a property. It should not contain any method. In the above example, we have the required View model with two properties.


1 Answers

Your view model seems wrong. View models should not reference any services. View models should not reference any domain models. View models should have parameterless constructors so that they could be used as POST action parameters.

So here's a more realistic view model for your scenario:

public class ContactCompanyViewModel
{
    public string SelectedCompanyId { get; set; }
    public IEnumerable<SelectListItem> CompanyList { get; set; }

    ... other properties that the view requires
}

and then you could have a GET action that will prepare and populate this view model:

public ActionResult Create()
{
    var model = new ContactCompanyViewModel();
    model.CompanyList = _Service.GetAll().ToList().Select(x => new SelectListItem
    {
        Value = x.id.ToString(),
        Text = x.name
    });
    return View(model);
}

and a POST action:

[HttpPost]
public ActionResult Create(ContactCompanyViewModel model)
{
    try
    {
        // TODO: to avoid this manual mapping you could use a mapper tool
        // such as AutoMapper
        var contact = new Contact
        {
            ... map the contact domain model properties from the view model
        };
        _Service.Save(contact);
        return RedirectToAction("Index");
    }
    catch 
    {
        model.CompanyList = _Service.GetAll().ToList().Select(x => new SelectListItem
        {
            Value = x.id.ToString(),
            Text = x.name
        });
        return View(model);
    }
}

and now in your view you work with your view model:

@model ContactCompanyViewModel
@using (Html.BeginForm())
{
    @Html.DropDownListFor(x => x.SelectedCompanyId, Model.CompanyList)

    ... other input fields for other properties

    <button type="submit">Create</button>
}
like image 153
Darin Dimitrov Avatar answered Oct 05 '22 08:10

Darin Dimitrov