ditto : with regard to AutoMapper

A comparison of ditto to AutoMapper was suggested so I will try to be more specific for some problems ditto has solved for me. Hopefully this help you decide if it is the right tool for your project.

hat tip

Before I 'compare' AutoMapper, let me clearly state that I am not criticizing anyone's work. I suspect AutoMapper has been a big helper to many projects and the community surrounding it seems pretty helpful. It just didn't fit my needs. I tried to use AutoMapper correctly so if I state something in error here please let me know.

I'd also like to highlight that ditto's best friend is Fasterflect. Alot of the reflection magic is handled by this excellent library.

ditto doesn't

First, let me point out a few limitations that currently exist in ditto when compared to AutoMapper.

  • Dictionaries are not supported ( I believe they are in AutoMapper ). I don't really need this, but it would just be an implementation of IBinder registered on your container.
  • It is slower than AutoMapper. The benchmarking project I have in my project shows about a -2 sec difference in mapping 10,000 messages. To be honest I haven't spent a ton of time optimizing ditto. It's fast enough for me right now though.
  • UPDATE : I just pushed a small change that makes my benchmarks 1 second faster than AutoMapper now...My benchmarking tests are simple, so objections are welcome!
  • Flattening / Unflattening assumptions. This could probably be easily extended into ditto but I actually haven't a need for it since the objects I am focused on are not behavioral.
  • Conditional mapping . Actually, this is supported but not in an explicit method call.
  • I am sure there are others. Please comment and I will update this limitations list.

ditto gots-ta-have

  • The primary attraction of AutoMapper for me was the little call to .AssertConfigurationIsValid();. I knew if I was going to create a similar tool it had to provide this kind of validation.
  • Provide a fluenty interface but don't tie my hands with generic parameters when a simple Type will do.
  • No requirement to specify the superfluous generic parameter for the source object when calling .Map().

ditto intent

My focus for ditto is lightweight objects; that is, Data Transfer Objects that are commonly used at application boundaries. That's not to say things like nested objects or collections are not supported (they are).

ditto does

Okay, with that out of the way let's see what ditto DOES do. The hassle in any tool that meets this problem will be found in the configuration bits. Like you'd expect, ditto assumes property names are the same between objects so gives you some help with setting up the mappings based on property name. That's the easy stuff. ditto is greedy so just does this automatically.

The real work comes from exceptions to the rule. Rather than treating the association between two objects as the subject for mapping, I decided to simply treat the destination types as subject and provide a facility for binding together all mappings together to create a validatable, executable whole. This is what I mean on the project when I state the initial goal of ditto was to aggregate data from multiple sources. This gives me the coding/typing freedom from having to specify every property for every source, but also gives me the freakin' cool ability to confirm that all my destination object properties have some mapping configured.

Let's illustrate a lightweight example for what I mean by that. I have a service that receives messages (events) that each contribute bits to a single view model. Each view model has a Denormalizer class that implements an IDenormalize:

public class ProjectViewModelDenormalizer : Denormalize<ProjectViewModel>, 
    IDenormalize<ProjectCreatedEvent>,
    IDenormalize<ProjectSamplesAddedEvent>,
    IDenormalize<ProjectMixCodeDefinedEvent>

    /* implementations of these interfaces */
}

public class ProjectViewModel {
    public Guid ProjectId {get;set;}
    public string Name {get;set;}       
    public List<Guid> SampleIds {get;set;}      
    public string MixCode {get;set;}
    /* about 20 more properties, each coming from 10 different events */
}

public class ProjectCreatedEvent {
    public Guid Id {get;set;}
    public string Name {get;set;}
}

public class ProjectSamplesAddedEvent {
    public List<Guid> SampleIds {get;set;} 
}

public class ProjectMixCodeDefinedEvent {
    public string MixCode {get;set;}
}

/* 10 more events contributing to this view model*/

In order to achieve this in the current drop of AutoMapper and get validation checking I would have to map each property on Project with an .Ignore() call to the properties that each event does not cover. This is due to the AutoMapper signature that requires a <TSource,TDestination> mapping. In order to avoid validation assertion failures, I'd have to zip up this configuration into a custom converter just for the ProjectViewModel but then I lose validation checking which was the strongest feature in AutoMapper for me. Now, this is still workable, but since I have hundreds of these ViewModels it just became too much .Ignore() ing for me. Without a Custom Converter, mapping AutoMapper would look something like this for just these :

public class ProjectViewModelProfile : Profile
{
    protected override void Configure() {
        CreateMap<ProjectCreatedEvent,ProjectViewModel>()
            .ForMember(m => m.SampleIds, opt => opt.Ignore() )
            .ForMember(m => m.ProjectId, opt => opt.MapFrom(its => its.Id) )
            .ForMember(m => m.MixCode, opt => opt.Ignore() );

        CreateMap<ProjectSamplesAddedEvent, ProjectViewModel>()
            .ForMember(m => m.Name, opt => opt.Ignore() )
            .ForMember(m => m.ProjectId, opt => opt.Ignore() )
            .ForMember(m => m.MixCode, opt => opt.Ignore() );

        CreateMap<ProjectMixCodeDefinedEvent, ProjectViewModel>()
            .ForMember(m => m.ProjectId, opt => opt.Ignore() )
            .ForMember(m => m.Name, opt => opt.Ignore() )
            .ForMember(m => m.SampleIds, opt => opt.Ignore() );

        /* 10 more blocks like these repeating .Ignore() */
    }
}

View Models are always changing out so adding properties or tweaking them slightly suddenly becomes very cumbersome due to the repetition of their definitions for each event type.

ditto takes a different approach and executes validation on its mappings after everything has been configured and bound up. It treats the 'Destination' object as the one you are interested in and hollers if the sum of all your sources don't affect one of its properties in some way. So the above model config would be written like this in ditto:

public class ProjectViewModelConfig : AbstractMappingConfiguration {
    public override void Configure() [
        //Cfg is injected by your container...see the docs
        Cfg.Map<ProjectViewModel>().From(typeof(ProjectCreatedEvent),typeof(ProjectSamplesAddedEvent),typeof(ProjectMixCodeDefinedEvent),/*more events?*/)
            .Redirecting<ProjectCreatedEvent>(from => from.Id, to => to.ProjectId );
    }       
}

I typically simplify this with a wee extension method for discovering the source types.

Concepts

ditto basically applies three concepts to execute mapping:

  • IPropertyCriterion : This is the selector for the destination property to match on a object; ie Naming Convention matching
  • IResolveValue : This is the behavior of the resolution, given the source and destination (similar to what you see in AutoMapper).
  • IConvertValue : This is the convention for certain types; ie, DateTime UTC standards

Manually configured mappings take precedence of global conventions. So this means if you apply a convention to ignore all properties with names starting with "System":

Conventions.AddResolver(new PrefixPropertyCriterion("System"),new IgnoreResolver());

But you find later you have a model you do want to participate in mapping execution. You can do this:

Cfg.Map<MyViewModel>().From<MyEventWithSystemDetails>().Redirecting<MyEventWithSystemDetails>( from => from.SystemOS, to => to.OperatingSystem );

This will use your special mapping in this one instance.

More info on setting up global conventions is at the project site README.

Container

Since ditto relies heavily on IoC container to do its thing, it is dead simple to provide your own resolver implementations,organize your mapping files, or override its behavior it a pretty granular level. This means it'd be easy to provide custom ways for creating destination objects, etc.

Execution API

There isn't a need to specify the source object in generic parameters to map your object. Just tell ditto what destination type to map to and your done. Specifying the source type when I am already passing in the source object was more a pet peeve and made my code noisier than I prefer. Probably this could be relieved with a utility extension method AutoMapper though.

var viewModel = ditto.Map<MyViewModel>(message);

This is getting pretty long now. I hope this sheds some light on ditto's purpose for being. If you have more questions either on the API or whether it'd be a good fit for you feel free to comment below or email me.


Posted 04-10-2011 4:08 PM by Michael Nichols
Filed under: , ,

[Advertisement]

Comments

David Thibault wrote re: ditto : with regard to AutoMapper
on 04-12-2011 12:08 PM

This looks very interesting. Especially the validation system.

I'll have to see if I can use this in our CQRS app. Thanks!

Michael Nichols wrote re: ditto : with regard to AutoMapper
on 04-12-2011 1:10 PM

@David

CQRS was the main reason for writing this. Let me know if you need help with ditto.

Cheers

About The CodeBetter.Com Blog Network
CodeBetter.Com FAQ

Our Mission

Advertisers should contact Brendan

Subscribe
Google Reader or Homepage

del.icio.us CodeBetter.com Latest Items
Add to My Yahoo!
Subscribe with Bloglines
Subscribe in NewsGator Online
Subscribe with myFeedster
Add to My AOL
Furl CodeBetter.com Latest Items
Subscribe in Rojo

Member Projects
DimeCasts.Net - Derik Whittaker

Friends of Devlicio.us
Red-Gate Tools For SQL and .NET

NDepend

SlickEdit
 
SmartInspect .NET Logging
NGEDIT: ViEmu and Codekana
LiteAccounting.Com
DevExpress
Fixx
NHibernate Profiler
Unfuddle
Balsamiq Mockups
Scrumy
JetBrains - ReSharper
Umbraco
NServiceBus
RavenDb
Web Sequence Diagrams
Ducksboard<-- NEW Friend!

 



Site Copyright © 2007 CodeBetter.Com
Content Copyright Individual Bloggers

 

Community Server (Commercial Edition)