Building a Blog Redux - Mapping View Models to Entities Using AutoMapper (Part 6)

Monday, June 4, 2012

This is the sixth post in a series of posts about how I went about building my blogging application.

  1. Building a Blog Redux - Why Torture Myself (Part 1)
  2. Building a Blog Redux - The tools for the trade (Part 2)
  3. Building a Blog Redux - Entity Framework Code First (Part 3)
  4. Building a Blog Redux - Web fonts with @font-face and CSS3 (Part 4)
  5. Building a Blog Redux - Goodreads feed using Backbone.js (Part 5)

In the basic building parts of a MVC web application there are the models, the views, and the controller. The models represent the data that will be placed in the view. The controller is the guy that goes and gets the data (the model) and puts it in the view. Then finally, view is responsible for presenting the data to the user. If the data schema is simple, you could use an ORM like Entity Framework, nHibernate, LINQ, or go old school data-access-layer and pass the entity that represents a SQL table directly to the view and display it on the page.

However, as views get more complicated, the data in the entities needs to be manipulated and changed before you can display it in a view. Often times, the views have fields that are not needed in an entity or vice-versa. You could end up with bloated entities that have a lot of properties that are not mapped to any field in a database. Back in the days of yore, we might resorted to creating stored procedures to manipulate the data to return properties we needed, but then the next guy to come along and work who wanted to make changes to that application learned quickly that this was a very bad idea.

My Approach

My MVC applications are really MVCS applications. That is, I typically add a service layer to my applications between the controller and the models, where I try and keep the lion share of my business (domain) logic. Also, instead of having entities display on the page, I create classes that represent a view models and then I map the entities to the view model which then get sent to the views. This mapping takes place in my new service layer that I just created.

Doing this gives keeps my entities clean and provides for a better separation of concerns between my database and my front end views. However, mapping entities to views and vice-versa can be a challenge especially as your application grows in both size and complexity. It can also be quite tedious, and makes for a laborious day of coding.

Enter AutoMapper

So to handle all my mappings, I use AutoMapper. Here is what AutoMapper is in their own words.

"AutoMapper is an object-object mapper. Object-object mapping works by transforming an input object of one type into an output object of a different type. What makes AutoMapper interesting is that it provides some interesting conventions to take the dirty work out of figuring out how to map type A to type B. As long as type B follows AutoMapper's established convention, almost zero configuration is needed to map two types."

Configuring AutoMapper.

Before mapping an entity object to a view model object, I need to tell AutoMapper the details about these mappings. Most times, the defaults were used, which by convention, is if object A has property with the same name as object B then those to fields will be automatically mapped. If object A has a property that object B doesn't have, then I can ignore it, or do something custom to account for that field. In an MVC web application, these configuration steps are done when the application starts.

In my Application_Start event in the global.asax.cs file, I have the following code.

Bootstrapper.RegisterMappings();

Inside this function, I register a profile for each of my mappings. These profile class is what tells AutoMapper the details of each of the mappings.

 public static void RegisterMappings()
        {
            Mapper.Initialize(x =>
                                  {
                                      x.AddProfile(new UserMapperProfile());
                                      x.AddProfile(new UserRoleMapperProfile());
                                      x.AddProfile(new BlogSiteMapperProfile());
                                      x.AddProfile(new PostMapperProfile());
                                      x.AddProfile(new SettingMappingProfile());
                                      x.AddProfile(new PingServiceMappingProfile());
                                  });;
        }

Here is an example of a simple configuration.

public class SettingMappingProfile : Profile
    {
         public const string ViewModel = "SettingProfile";
 
        public override string ProfileName
        {
            get { return ViewModel; }
        }
 
        protected override void Configure()
        {
            CreateMap<SettingSettingViewModel>();
            CreateMap<SettingViewModelSetting>();
        }
    }

In this case, I have a maping from the entity to the view and also from the view to the entity. Also, notice that my mapping class is inheriting from the Profile base class which is doing all of the magic and taking care of all of my mappings.

Where I have mappings that I need to ignore a field, I can use the Ignore option.

CreateMap<BlogSiteViewModelBlog>()
                .ForMember(dest => dest.Id, opt => opt.MapFrom(x => x.BlogId))
                ;

If I have a property that is itself an object and I want to map the Id property to its Is property and then instantiate the object, I could do the following.

CreateMap<HtmlFragmentViewModelHtmlFragment>()
                .ForMember(dest => dest.Blogs, opt => opt.Ignore())
                .ForMember(dest => dest.Location,
                           opt => opt.MapFrom(x => new HtmlFragmentLocation {Id = Convert.ToInt32( x.SelectedLocationId)}));
                ;

In some cases, I have property that I want to do special things with. In those cases, I can use what's called a value resolver. For example for my tags, I want to store them in the database as a collection; however, I want to display them on the page as comma delimited property. Here's how I can do this using AutoMapper.

First specify in the configuration to use a custom ValueResolver.

.ForMember(dest => dest.TagListCommaDelimited, opt => opt.ResolveUsing<TagListToDelimiterResolver>())

Then in the derived value resolver class, add the code to handle the list to delimiter functionality.

    public class TagListToDelimiterResolver : ValueResolver<Poststring>
    {
        protected override string ResolveCore(Post source)
        {
            if (source == nullreturn string.Empty;
            if (source.Tags == nullreturn string.Empty;
            string tagDelimited = source.Tags.Aggregate(string.Empty,
                                                        (current, tag) => current + string.Format("{0},", tag.TagName));
 
            return !string.IsNullOrEmpty(tagDelimited)
                       ? tagDelimited.Substring(0, tagDelimited.Length - 1)
                       : tagDelimited;
        }
    }

Testing

One of the things I like about AutoMapper is that it has an assert routine you can use to set up tests to test that your configuration works. When a test fails, the assert function does a good job of letting you know where the problem lies and which property is have an issue. Leaning on these mapping unit tests save me lots of time configuring my mappings, and I pretty much knew that if my tests were passing, then my web application would handle the mappings without any issues.

        [TestMethod]
        public void Should_be_able_to_configure_user_profile_to_view()
        {
            Bootstrapper.RegisterMappings();
            Mapper.AssertConfigurationIsValid();
        }

Mapping

Once the configurations are in place, the Mapper.Map function can be called and the views will be mapped to entities and entities map to views. Typically, I like to add some further abstraction and place these tasks in there own classes. This way, I can mock the results in a tests and add some additional customization if needed.  It also makes the mappings a little more maintainable.

public class PostMappingService : IPostMappingService
    {
        public Post MapToEntity(PostViewModel viewModel)
        {
            return Mapper.Map<PostViewModelPost>(viewModel);
        }
 
        public PostViewModel MapToView(Post entity)
        {
            return Mapper.Map<PostPostViewModel>(entity);
        }
    }

Conclusion

Hope this helps. As usual, you can see all of the code at GitHub.

 

 

 

comments powered by Disqus