4

I have an application with a Company model. The Company model has a navigation property to an Address model (one-to-one relationship):

Company.cs

public class Company
{
    public int CompanyID { get; set; }
    public string Name { get; set; }

    // Snip...

    public virtual Address Address { get; set; }
}

I've created a view model to handle the edit, detail, and create actions:

CompanyViewModel.cs

public class CompanyViewModel
{
    public int CompanyID { get; set; }

    [Required]
    [StringLength(75, ErrorMessage = "Company Name cannot exceed 75 characters")]
    public string Name { get; set; }

    // Snip...

    public Address Address { get; set; }
}

I'm using AutoMapper in my controller to map back and forth between the model and view model, and everything is working properly. However, I now want to use validation on the address object - I do not want a company to be created without an address being present.

My first thought was the simple route - I tried putting a '[Required]' annotation on the Address property. This didn't do anything.

I then thought it would be better to do away with the Address property and abstract that data in the view model, so I added properties to the view model for all the properties in my Address class:

public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public string PostalCode { get; set; }
// etc....

This seemed like good practice, but now my AutoMapper can't map these properties to the Company class' Address object, so I had to manually map in the controller:

public ActionResult Details(int id = 0)
{
    // Snip code retrieving company from DB

    CompanyViewModel viewModel = new CompanyViewModel();
    viewModel.Name = company.Name;
    viewModel.Address1 = company.Address.Address1;

    // Snip...    

    return View(viewModel);
}

This leads to a lot of extra code in my controller instead of a nice one-line AutoMapper statement...so what's the right way to deal with this (validation of nested models in a view model)?

Is it good practice to expose the Address property directly in the view model, or better to abstract it out with separate properties like I have done?

Can AutoMapper work in a situation where source and destination are not exact matches?

1

1 Answer 1

2

if you want automapper to be able to map your properties from model to your viewmodel without specifying the mappings explicitly, you've got to use the "flattenting convention" : means that you must concatenate the navigation property's name with its property names.

So your ViewModel should contain

public int CompanyID { get; set; }

    [Required]
    [StringLength(75, ErrorMessage = "Company Name cannot exceed 75 characters")]
    public string Name { get; set; }

    // Snip...
    //Address is the navigation property in Company, Address1 is the desired property from Address
    public string AddressAddress1 { get; set; }
    public string AddressAddress2 { get; set; }
    public string AddressCity { get; set; }
    public string AddressPostalCode { get; set; }
}

by the way, you can also tell AutoMapper to map properties which don't respect the naming convention explicitly :

Mapper.CreateMap<Company, CompanyViewModel>()
.ForMember(dest => dest.Address1, opt => opt.MapFrom(src => src.Address.Address1));
Sign up to request clarification or add additional context in comments.

4 Comments

This seems like what I need...I changed my property names as specified (AddressAddress1, AddressAddress2, etc...) -- this works for reading the customer data, but when I edit and save, my changes are not being applied. I don't get any errors or anything, I just lose the changes made to the address (changes to company name, etc... still work fine)...any ideas?
FYI, further testing shows the mapping works when displaying info (company's address is displayed) and deleting a company (associated address is deleted)...but when I edit the address no changes are saved and when I create a new company it is created w/o an address...do I need to change my controller save method somehow?
@Jim well, yes, you've got to "unmap" your viewModel to your model. You may do this with automapper, but you won't get automatic "unflattening"...
Ahhhh, I thought it worked out of the box in both directions. Thanks for shedding some light on this. I went ahead and explicitly defined my map (with ForMember declarations) and it is all working now. +1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.