By BoLOBOOLNE payday loans

AutoMapper: Ups, Downs, and Implementations – Part 3

Posted: January 28th, 2010 | Filed under: Programming
Author:
1 Comment →

I suspect I’m just terribly verbose since AutoMapper, while useful, probably doesn’t need the over 3000 words I’m giving it, but that’s ok. I enjoy getting my thoughts down and solid which makes me more effective. Anyway, this is part three in a series explaining why we needed AutoMapper and how it can be used. Part 1 talked mostly about the need for AutoMapper, the old ways of writing Object-Object mapping code and gave a quick look into the ideal situation for AutoMapper. Part 2 was about basic mapping exceptions and dealt a lot with understanding lambdas.

In this part I plan to deal with the more detailed exceptions in AutoMapper, and I’ll try to get my idea of when to and not to use it in your application. The guys who wrote it freely admit that sometimes it’s more work than it seems worthwhile to get AutoMapper working in your situation so I try to weigh the benefits of various options.

Quick Recap:

We were working with a Client presentation object that had some reference data that kept it from matching up exactly to the DTO which was causing some issue for AutoMapper. We had some simple solutions for handling mappings when it was just a different field name or ignoring when it made sense.

Here are those classes and the simple mappings for reference purposes.
ClientDto:

public class ClientDto
{
    public ClientDto() { }

    public int ClientId { get; set; }
    public int AddressId { get; set; }
    public int ClientTypeId { get; set; }
    public string Name { get; set; }
    public string Status { get; set; }
}

Client Object:

public class Client
{
    public Client() { }

    public int Id { get; set; }
    public string Name { get; set; }
    public string Status { get; set; }
    public Address Address { get; set; }
    public List<Contact> Contacts { get; set; }
    public Presentation.Enums.ClientType Type { get; set; }

    public string LongName {
        get { return Name + ", " + Address.Display; }
    }
}

Handling the Simple Mappings:

Mapper.Map<ClientDto, Client>()
    .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.ClientId)
    .ForMember(dest => dest.Type, opt => opt.MapFrom(src => (ClientType)ClientTypeId)
    .ForMember(dest => dest.LongName, opt => opt.Ignore());

The above map is still not complete. While we decided to ignore and correctly map a few of the mismatches, there are still two more complex types here that need to be mapped.

Custom Resolvers or Just Ignore() and Write Your Own?

When building AutoMapper they knew that some complex situations would come up and they built a way to handle almost everything I’ve come across. The issue is that sometimes it’s more code and work to solve the problem using AutoMapper. Custom Resolvers and Custom Type Converters are two of the more advanced things you can do. They both work well in my experiments. We don’t really need the CustomTypeConverters for my example but we do need at least one Custom Resolver.

I’ll go ahead and add a custom resolver for the Address and I’ll do the Contacts mapping by hand. You can see the difference and try to decide which you’d rather do. We’ll assume that we’ve already written a translation method for the Address. That means we just need to write fairly simple class that inherits from the ValueResolver class in the AutoMapper API. All we need to do is override a single method:

public class ClientAddressResolver: ValueResolver
{
	protected override Address ResolveCore(ClientDto source)
	{
		Address address = null;

		if (source.AddressId > 0)
                    address = new Address_DataAccess().Select(source.AddressId).Translate();

		return address;
	}
}

The AutoMapper ValueResolver class is a generic typed class. We need tell it our Source object type and return type. We override ResolveCode, which basically tells AutoMapper what the hand written mapping looks like. I said earlier we’d be doing the Contacts collection by hand so the new completed map that uses the custom resolver and ignores Contacts will look like this:

Mapper.Map<ClientDto, Client>()
    .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.ClientId)
    .ForMember(dest => dest.Type, opt => opt.MapFrom(src => (ClientType)ClientTypeId)
    .ForMember(dest => dest.LongName, opt => opt.Ignore())
    .ForMember(dest => dest.Contacts, opt => opt.Ignore())
    .ForMember(dest => dest.Address, opt => opt.ResolveUsing<ClientAddressResolver>());

Instead of the MapFrom or Ignore operations we use ResolveUsing telling it the class of our resolver. Now our translation method could look like this:

public static ClientTranslator
{
    public static Client Translate(this ClientDto dto)
    {
        Client client = Mapper.Map<ClientDto, Client>(dto);
        client.Contacts = new Contact_DataAccess().SelectByClient(dto.ClientId).Translate();

        return client;
    }
}

You can see here that I basically put the same line of code that would have gone into a custom ValueResponder class into the Translate method.

Or… just keep doing it myself because AutoMapper takes more code and more work

Just for comparison let’s look at what a non-AutoMapper translate would look like.

public static ClientTranslator
{
    public static Client Translate(this ClientDto dto)
    {
        Client pres = new Client()

        pres.Id = dto.ClientId;
        pres.Type = (ClientType)dto.ClientTypeId;
        pres.Name = dto.Name;
        pres.Status = dto.Status;

        pres.Address = new Address_DataAccess().Select(dto.AddressId).Translate();
        pres.Contacts = new Contact_DataAccess().SelectByClient(dto.ClientId).Translate();

        return pres;
    }
}

And that’s it. Rather than, building an AutoMapper bootstrapper, a Map, and at least one ValueResolver, I just wrote a few lines here it’s still centralized and it’s easy to maintain; maybe easier since I don’t have to dig through multiple files.

Now, granted, this is a fairly easy class. Sometimes our presentation classes can get quite a bit larger, but that also typically means quite a bit more relationships which as I’ve demonstrated are where AutoMapper is less effective.

One of the challenges in my opinion is that the ValueResolvers aren’t really reusable. For example, the address object: If I have 10 different dtos that have an AddressId on them and need to be mapped I would have to build a ValueResolver for each dto. One solution to this would be to make an IAddressable interface and a single ValueResolver. That would handle the situation but is extra abstraction when I could simply write my translators without AutoMapper to begin with.

Some more AutoMapper Ups

AutoMapper still offers a couple of other perks that I either brushed over or didn’t mention at all. One, is:

Mapper.AssertConfigurationIsValid();

This single line of code does a check on your mappings and verifies that you’re not leaving off any properties and that as long as your resolvers work you shouldn’t have any errors in your translation code. I like have this run from my bootstrapper, so if make a mistake here I’ll find out before I get too far into the code.

Another feature I brushed over are TypeConverters, AutoMapper supports the idea of always handling a particular type a certain way. For example, (I actually ran across this in some code before, much as, in my opinion, it’s a horrid practice) I was working in a database that instead of nulls for optional dates it would use ’1/1/1901 12:00:000.00′. I could easily have my dto classes just hang on to these as a non-nullable datetime (since in the database that’s what they are) then for my use in the presenter classes I could have them be nullable. I could set up a TypeConverter for AutoMapper that would take a DateTime that’s being mapped to a DateTime? and have it check for then db ‘null date value’ and just make it null. This, like a object map, is done once in the bootstrapper and would apply across the board to all maps of this type.

Another AutoMapper down

Another down that I ran into with AutoMapper is a challenge you’ll always have to some extent but I could handle it a little better in a hand written Mapping method. It’s from the circumstance I basically skipped over. In this case, mapping the Contacts.

What if the Contacts are a Many->Many relationship with Clients. I’ll need to construct a dto that’s going to contain both the contact and client primary keys, but AutoMapper just can’t really handle this mapping whether I start from the client or with the contact. Either way runs into data that it can’t access. The in ability to pass through an extra parameter for mapping puts a hold on this type of situation.

I realize this is often a very easy translation method to write by hand, I’m just pointing out a limitation.

In Conclusion: AutoMapper is good, but for the right situation

All in all, my hat is off the the AutoMapper guys. They’ve done a good job with something really annoying and often convoluted. It’s not perfect but it’s solid and very workable. From a performance stand point, none of my tests have shown any recognizable performance degradation, but my tests were not thorough from that stand point; it wasn’t my primary concern.

As I said in Part 2, I had great success using AutoMapper in my DataAccess layer, but as I seem to be moving more towards ORM solutions this may be an issue of the past.

I really like the general code structure I use when I’m using AutoMapper. My classes are small with a single responsibility. I like the ability to perform a quick test on my mappings as part of the bootstrapper.

In the end, I really want to find AutoMapper more useful than I do. All of the code is easy enough really, it’s just that there’s so much to write for what could often be easier and simpler. I keep the reference alive in my service project and just use it when it makes sense.

Hopefully all of this will be of use to someone. I had a hard time finding this information when I was giving AutoMapper a try in my first project. If you have anymore questions or would like more code just drop me a comment.

Tags: , , , , , ,

One Comment on “AutoMapper: Ups, Downs, and Implementations – Part 3”

  1. 1 Jimmy Bogard said at 11:49 am on January 28th, 2010:

    My take is that if you want to do reverse mapping, you’re better off doing an ActiveRecord-type solution like the MonoRail (and Rails) guys do. AutoMapper is optimized for one-way maps, as it doesn’t want to put a bunch of constraints on the shape of your source type. For example, we don’t expose public collection types, nor do we always expose public setters everywhere.


Leave a Reply