Click here to Skip to main content
Click here to Skip to main content

Using AutoMapper's Custom Formatters

, 4 Jan 2010
Rate this:
Please Sign up or sign in to vote.
A simple how-to on using the Custom Formatter capability within the AutoMapper library.

Introduction

AutoMapper is an Open Source library that provides a convention-based object-object mapper. It is a very useful tool that can help speed up your development and cut down the amount of code you need to write, particularly... object mapping code.

Overview

Object mapping is used frequently to transform input object of one type (say a Data Access Object) into an output object of a different type (say a Data Transfer Object). AutoMapper makes this transformation very simple by providing a convention-based way to transform data between these objects. So, mapping code that once looked like this:

public class ProductDAOtoProductDTOMapper
{
    public ProductDTO Map(ProductDAO product)
    {
        return new ProductDTO
            {
                Name = product.Name,
                Description = product.Description,
                Price = product.Price,
                Quantity = product.Quantity
            };
    }
}

...can be replaced with this:

var productDto = Mapper.Map<productdao, >(productDao);

That's it!

Getting Started with AutoMapper

All the configuration that's required to setup AutoMapper occurs in the Global.asax.cs file, within the "Application_Start" method:

protected void Application_Start()
{
    Mapper.CreateMap<ProductDAO, ProductDTO>();
}

This will setup the mapping between these objects using the following conventions:

  • Matching property names
  • Nested property names (Product.Name maps to ProductName, by assuming a Pascal case naming convention)
  • Methods starting with the word Get, so GetTotal() maps to Total
  • Any existing type map already configured

Custom Formatters

AutoMapper also provides another extremely powerful capability called "Custom Formatters". Custom Formatters can step in during the mapping process and format the data being mapped in any way that you want. For a practical example, the code below is a mapping between a Model object and a ViewModel object that will be used on a "View" in an ASP.NET MVC application. The "Message" property contains a comment that was entered by a website visitor, and therefore it needs to be HTML encoded. To set this up for the Message property only, we would write this code in the Application_Start method:

Mapper.CreateMap<Comment, CommentViewModel>()
    .ForMember(c => c.Message, m => 
      m.AddFormatter<htmlencodeformatter>());
    
public class HtmlEncodeFormatter : IValueFormatter
{
    public string FormatValue(ResolutionContext context)
    {
        return HttpContext.Current.Server.HtmlEncode(
                      context.SourceValue.ToString());
    }
}

In the example above, you'll notice that when we create the map between the Comment and CommentViewModel objects, that we singled out one of the properties (the Message property, in this case) to be formatted using the HtmlEncodeFormatter.

You can also setup global formatters that will be applied during every mapping operation. One common scenario that can be addressed by this feature is the case where you want to display all dates in the exact same format. This is extremely easy to setup as well:

//setup the global formatter
Mapper.AddFormatter<DateStringFormatter>();

//define the global formatter
public class DateStringFormatter : BaseFormatter<DateTime?>
{
	protected override string FormatValueCore(DateTime value)
	{
		return value.ToString("dddd, MMM dd, yyyy");
	}
}
public abstract class BaseFormatter<T> : IValueFormatter
{
	public string FormatValue(ResolutionContext context)
	{
		if (context.SourceValue == null)
			return null;

		if (!(context.SourceValue is T))
			return context.SourceValue ==
                                 null ? String.Empty : context.SourceValue.ToString();

		return FormatValueCore((T)context.SourceValue);
	}

	protected abstract string FormatValueCore(T value);
}

In the example above, the first line is the one that actually registers the global formatter. The DateStringFormatter and BaseFormatter classes are what actually do the work.

First, and explanation of the BaseFormatter class. The class implements the IValueFormatter interface which an AutoMapper interface... this is what makes this class recognizable as a formatter in AutoMapper's eyes. I've made this class abstract so that we can handle all of the common checks that we need to handle: null checks, is it the right type, etc. The last line of the FormatValue method calls the FormatValueCore method that is defined as an abstract method on this class. This is what provides the "hook" for our implementation class: DateStringFormatter. All the DateStringFormatter class nees to do is inherit the BaseFormatter class, specify the actual type being formatted (DateTime in this case), and override the FormatValueCore method. Once in this method, you can see that all we need to do is format the date in the way we want.

**One very important rule to remember: Formatters are only applied when the destination member type is of type "string".

Conclusion

Using custom formatters within AutoMapper is a very simple that you can use AutoMapper to its full potential. There are a number of other great features in AutoMapper (Custom Value Resolvers, Custom Type Converters, Custom Actions before/after mapping, etc.) - but those are outside of the scope of this article. See the project website on CodePlex for more information: http://www.codeplex.com/AutoMapper.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Leftend
Software Developer (Senior) Leftend
United States United States
No Biography provided

Comments and Discussions

 
GeneralNice work PinmemberLouis Gale13-Jan-11 20:23 
GeneralMy vote of 5 Pinmembernodar13-Nov-10 14:01 
QuestionCustom formatter global configuration PinmemberJames Kolpack31-Dec-09 4:24 
AnswerRe: Custom formatter global configuration PinmemberLeftend31-Dec-09 6:08 
Ah! Right you are, sorry about that. I just corrected it, and have submitted the updated article to CodeProject - it should be updated shortly. I also noticed that a few of the other examples had some formatting issues - and in some cases actually omitted some code - so those have been fixed as well.
 
In the meantime, here's the missing code for setting up a global formatter:
 
//setup the global formatter
Mapper.AddFormatter<DateStringFormatter>();
 
//define the global formatter
public class DateStringFormatter : BaseFormatter<DateTime>
{
protected override string FormatValueCore(DateTime value)
{
return value.ToString("dddd, MMM dd, yyyy");
}
}
public abstract class BaseFormatter<T> : IValueFormatter
{
public string FormatValue(ResolutionContext context)
{
if (context.SourceValue == null)
return null;
 
if (!(context.SourceValue is T))
return context.SourceValue == null ? String.Empty : context.SourceValue.ToString();
 
return FormatValueCore((T)context.SourceValue);
}
 
protected abstract string FormatValueCore(T value);
}
GeneralRe: Custom formatter global configuration PinmemberJames Kolpack31-Dec-09 7:50 
GeneralRe: Custom formatter global configuration Pinmemberjimmy.bogard31-Dec-09 10:41 
GeneralRe: Custom formatter global configuration PinmemberJames Kolpack31-Dec-09 10:51 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140721.1 | Last Updated 4 Jan 2010
Article Copyright 2009 by Leftend
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid