Removing the static API from AutoMapper

As I work towards the 4.2 release of AutoMapper, I got a little inspiration. Over the past year or so I’ve given some talks/podcasts about a long-lived open source codebase. One of my biggest regrets was that I made AutoMapper have a static API from literally the very beginning. The first tests/prototypes of AutoMapper all started out with “Mapper.CreateMap” and “Mapper.Map”. I showed my boss, Jeffrey Palermo, at the time and asked what he thought, and he said “looks great Jimmy, maybe you shouldn’t make it static” and I said “PFFFFFFFFFFFT NO WAY”.

Well, I’ve seen the problems with static and I’ve regretted it ever since. With the upcoming release, I’ve had a chance to prototype what it might look like without static – it worked great – and I’m ready to mark the entire static API as obsolete.

This is a huge shift in AutoMapper, not so much the API, but forcing everything to be static. Here’s the before:

Mapper.CreateMap<Source, Dest>();

var source = new Source();
var dest = Mapper.Map<Source, Dest>(source);

Or if you’re doing things the Right Way™ you used Initialize:

Mapper.Initialize(cfg => {
  cfg.CreateMap<Source, Dest>();
});

var source = new Source();
var dest = Mapper.Map<Source, Dest>(source);

There are a number of issues with this approach, most of which just result from having a static API. The static API was just a wrapper around instances, but the main API was all static.

With 4.2, you’ll be able to, and encouraged, to do this:

var config = new MapperConfiguration(cfg => {
  cfg.CreateMap<Source, Dest>();
});

IMapper mapper = config.CreateMapper();
var source = new Source();
var dest = mapper.Map<Source, Dest>(source);

The IMapper interface is a lot lighter, and the underlying type is now just concerned with executing maps, removing a lot of the threading problems I was having before.

I’m keeping the existing functionality and API there, just all obsoleted. The one difficult piece will be the LINQ extensions, since those as extensions methods are inherently static. You’d have to do something like this:

var config = new MapperConfiguration(cfg => {
  cfg.CreateMap<User, UserDto>();
});

var builder = config.CreateExpressionBuilder();
var users = dbContext.Users.ProjectTo<UserDto>(builder);

It’s not exactly ideal, so I’m playing with doing something like this:

var config = new MapperConfiguration(cfg => {
  cfg.CreateMap<User, UserDto>();
});
QueryableExtensions.Factories.Configuration = () => config;
// The above two can be done once at startup

var users = dbContext.Users.ProjectTo<UserDto>();

Expressions can’t depend on any runtime values, and can be more or less explicitly tied to the configuration.

The pre-release versions of AutoMapper on MyGet now have this API, so it’s not release yet as 4.2. Before I pushed 4.2 out the door, I wanted to get some feedback.

So do you like this idea? Let me know! I’ve got a GitHub issue to track the progress: https://github.com/AutoMapper/AutoMapper/issues/1031

About Jimmy Bogard

I'm a technical architect with Headspring in Austin, TX. I focus on DDD, distributed systems, and any other acronym-centric design/architecture/methodology. I created AutoMapper and am a co-author of the ASP.NET MVC in Action books.
This entry was posted in AutoMapper. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • gilligan_MH

    thumbs up

  • The static API has always struck me as “wrong”, so I’m glad you’re getting rid of it! Actually, I had suggested something similar a while back ;)

  • I had long thought about this, but now I’m not so sure. For once, you have to make maps available everywhere you want to do the transformations… Thoughts, guys?

    • You can easily inject the IMapper anywhere you need it

    • jbogard

      You can always just set a static property or field yourself too, at app startup time. I just won’t force it through the API.

      • Yeah, yeah, I know all that. It’s just that it won’t be a drop in replacement. @jbogard:disqus: have you considered bumping the major version, like, to 5.0? It is a substantial change.

        • jbogard

          The existing API will be there for 4.2 still. In 5.0 I’ll remove the static stuff but wanted to give a dot release to have both APIs to allow people to transition.

    • Betty

      it would also allow you to do mappings different ways in different parts of the code, even if you’re mapping the same types. I love this change.

  • arbenowskee

    Love it! DI 4 teh win =)

  • Bruno Brant

    I always wrapped AutoMapper so that it could be injected, so yeah, you’re saving a lot of redundant code in my projects.

  • Owen Morgan-Jones

    I think this is great. It would have helped me out on a previous project where I called Mapper.Initialize() in my code and overwrote the config being used under the covers in a dependency, causing it to fail.

    It looks like this approach would eliminate that completely, and let me scope configs/IMappers only as much as I needed to, without worrying what my dependencies may or may not be doing.

  • Dan Keller

    I’ve just been doing enough F# to annoy my friends talking about it, but
    some of problems you are having with the code, just don’t happen in the
    functional programming style.

    I think the reason static methods took off as a style was because of Linq
    and extension methods that came with it. The only problem was that people (I know I did) wrote extension methods (a very functional style) as if they were instance methods (very OO style), and I guess would get caught in some traps that I just don’t see in F#, such as threading issues.

    Best practices I’ve tried to follow. Taken from guru’s like Scott Wlaschin, Tomas Petricek, Jon Skeet
    F# Best Practice 1) Try to make most things immutable, goal being everything is immutable.

    F# Best Practice 2) If it is mutable make it explicit, big warning flags (in F# this would usually be .NET Properties)

    F# Best Practice 3) Treat your functions as input and output, no side effects, do only one thing. In the end you could almost substitute it with a table of values or dictionary of precalculated/cached values.

    Gist with some thoughts protecting or adding fluent to a function: https://gist.github.com/kellerd/a7f612d2b271d9f85366

    String does this, Linq does this. You call .ToUpper or .Where, you get a new thing, you don’t modify the original. At first it was weird, then overtime we came to enjoy the features of this and but may not have known why.

    I think there are other ways around this, like what you’re proof of concept is doing, closures or functions that create functions to create your thing.

    • I’m a C#’er for life, but very interested in F#. Love seeing posts like these! Once the tooling (ReSharper, for starters) catches up there’s going to be a disturbance in the .NET Force. :)

      • Dan Keller

        Fsharpforfunandprofit.com :) Even tips for using it at work

  • Janek Kowalski

    Thanks for fixing this static nightmare. No more need to write dump wrapping AM? Szczesc Boze panu za to dobre oprogramowanie!

  • Yeah… with xUnit I too have learned the ways (or rather “unways”) of static resources. Thank you for posting this! Glad this showed up in the search engines while trying to deal with my new obsolete warnings. :)

    Speaking of which, a random thought I had just now, but maybe a new best practice for all obsolete/deprecated messages should include a URL if possible? That would be pretty rad/convenient.

  • Diechort

    How about something like:
    var query = context.Users.Where( … etc
    mapper.ProjectTo(query);

    The syntax is a little nastier than the extension methods, but this would allow using different mapping profiles in different places of the application. (Not sure why you’d need that though)

    • jbogard

      You can apply different profiles through different mapping configurations still with the 4.2/5.0 API.

      But I often chain after the Select too, so that syntax would be awkward.

      • Diechort

        Maybe there’s something i’m missing… Suppose you go with the second approach you mention in the post:


        var config = new MapperConfiguration(cfg => {

        cfg.CreateMap();

        });

        QueryableExtensions.Factories.Configuration = () => config;

        // The above two can be done once at startup

        var users = dbContext.Users.ProjectTo();

        And supose you have a multi tenant application, that for some reason needs different mapping profiles for different tenants.

        Since the configuration is done at startup, how would you tell the extension method which mapping profile or configuration it would need to use?

        PS: I’ve started using automapper a few moths ago, and i fell in love with it immediately, so thanks for building this awesome framework :)

        EDIT: Now that i think about it, i guess using the first approach, one could register expression builders for each tenant, and inject the expression builder on your service/query/command/whatever

  • Sameh Saeed

    thx you save my time :)

  • Thanks! Greate article! :)

  • Gabriel

    Hi thanks for the update! I use AutoMapper in every project. How do you recommend to deal with null values? I would like for all members to ignore if value is null. This is the example (lets keep it simple):
    Category class with Properties Id and Name
    CategoryModel class with Id and Name

    If I want to go from CategoryModel to Category and CategoryModel has no value in Property “Id”, it should keep the Id of the original Category.

    Category category = new Category() { Id = 2, Name = “a” };
    CategoryModel model = new CategoryModel() { Name = “b”};
    category = _mapper.Map(model, category );

    category should be Id = 2 and Name “b”. It ignores the “Id” of model because it came null, so It should keep the original value.

    Thanks!

  • cbates

    Just when I convince myself that using the static mapper is
    okay and can be overlooked when my code OCD kicks in, you go and change it…such
    is life. Thanks for the great tool btw!

  • Matthew T. Baker

    I can’t seem to get this working with Profiles, do you have an example to show? Thank you!

    • Brian Bauer

      I have the same issue, were you able to resolve it?

  • jeffreypalermo

    Good stuff, Jimmy. It’s been an incredibly successful project, and using static forces the usage of a global variable at some point. And we all know globals are “bad”. :)

  • James Grace

    Lord love a duck, Jimmy. Is there any clear examples of how to set this up for use in an MVC5 project?

    I just found out about AutoMapper but cannot figure out in my MVC5 application where to place configuration code (startup.cs? global.asax?) and how to properly use AutoMapper to map the results of an EF LINQ query results to a viewmodel which gets send to a view in the Return View statement… I desperately wanna get out of matching field for field ..

    Any chance you can help out a newbie to this? I need to know where in Donald Trump (sorry for the swearing) I’m supposed to place my code…

    • jbogard

      The static API is back, with the exception that CreateMap is gone. If you’re using Mapper.Initialize, you’re good to do.

      • James Grace

        Thanks for the quick reply, Jimmy … but I’m struggling to figure out where I place my initializing code in MVC5 project (static or not).

        Can you point me to a good code sample that maps a model to a Viewmodel and how it is initialized at application start and used within a controller? There are many partial examples that “assume” we know where stuff goes… I’d prefer doing it the “new” way… so I don’t have to change much when going to V5…

        • jbogard

          My ContosoUniversity project is a good start.

  • bibek gautam

    thanks for the update and saving my time :)

  • Vitaliy M

    Jimmy, could you please clarify new approach.
    Before in Web application I called from Global.asax.cs Application_Start() method where all mappers got initialized.
    Somewhat like this:
    Mapper.Initialize(map=>
    {
    map.AddProfile();
    ..}
    Mapper.CreateMap()
    ..
    )

    And I used Mapper.Map(source) where i need it.
    So mapping happened only once per application start and all mapping was kept in one place.

    Now how do I do mapping once per application? Do I need to create map before I do mapping every time? What is project structure for new approach?

    • jbogard

      The static API is back. Continue on.

      • Vitaliy M

        what do you mean it is back? I updated to v.5 from v.4.2.1.0 and project can not be built any more. Errors about non existing methods..

        • jbogard

          Mapper.Initialize and Mapper.Map are back.

          Not Mapper.CreateMap, that’s dead as the dodo.

          • Vitaliy M

            looks like documentation for “Custom value resolvers” should be updated.. new interface has different signature with 3 param, not 2 as used to be:
            public interface IValueResolver

  • dasjestyr

    So can I no longer pre-configure all of the mappings? This seems like I have to always run the config before using the mapper. Or do I just call Initialize() for each mapping?

    • jbogard

      Eh? Just use Mapper.Initialize.

      • dasjestyr

        For every mapping? I don’t mean to sound obtuse, but usually “Initialize” only gets run once on something.

        • jbogard

          Yes, you call Mapper.Initialize once on startup (just like you would your ORM or DI container). Mapper.CreateMap is dead, I threw it in a dumpster, poured gasoline and lit it on fire.

          • dasjestyr

            What I mean is, before, I could have a class that would setup all of the mapping combinations, basically “teaching” the mapper how to map which objects to which, and that is what I would run at startup as initialization. But, the new initialize method only takes a single expression. How do I create multiple maps at startup?

          • jbogard

            Maybe open a GitHub issue? You can’t really format code in a Disqus comment, so I’m curious to see what you were doing before. I don’t know what “teaching the mapper how to map objects” means exactly.

          • dasjestyr

            I didn’t want to open an issue until I knew it was an issue.

            Basically with the old static interface, one would simply call Mapper.CreateMap (with optional mapping config) *for each pair of objects (source/destination)* that you’d want to map, essentially registering the mapper internally (the “teaching” part to which I referred). Then, you’d just call Mapper.Map(source) and so long as you created the map beforehand, you’d be good. But since Mapper.CreateMap() has been removed, I’m not sure how to register all of the mappers, as the Initialize() method only seems to take a single expression — which is why I’m asking how would one CreateMap for multiple objects.

            So for example, in the meanwhile, I’ve recreated the effect by making a factory of sorts where during startup, I can register the object pairs with the mapping configuration to be recalled later when I need it.

            Something like this: https://gist.github.com/draepetan/c700fab6e8e2a1284fb5897d99899bd8

            I feel like this would probably be in Automapper somewhere and I just haven’t found it.

          • jbogard

            The Initialize method takes a delegate, so you can use any sort of delegate in C#, a lambda expression, lambda statement, method group to a static method, almost anything really.

            Sprinkling around Mapper.CreateMap inside runtime code though is pretty dangerous – you can’t use Mapper.AssertConfigurationIsValid! Which, imo, is crazy if you don’t do that somewhere.

          • dasjestyr

            Right. I don’t want to sprinkle that around which is why I’m trying to do it on startup, but it wasn’t obvious (at least to me and a few others here) on how to register multiple maps. Ok, so the delegate you speak of; are you just saying call .CreateMap on its argument as many times as I need? Something like:

            Mapper.Initialize(cfg =>
            {
            cfg.CreateMap();
            cfg.CreateMap();
            });

            I think I just came to that realization. Feels a little awkward because it’s referred to as an expression rather than a delegate (I guess I took it literally and was thinking of it like a MemberExpression for whatever reason); I was comparing it to the old interface, but i’ll buy it. Can I recommend showing that as an example in the Getting Started doc?

          • jbogard

            Ohhhhhh yeah I see now. Yeah, I was copying another library’s names for those classes when I was building that stuff years ago. Proooooobably not the best of ideas.

          • dasjestyr

            I just realized that I’m asking pretty much the same question that Vitaliy M is asking below, which doesn’t seem to be actually answered.

  • Colin M

    So you say you’re keeping it available but Obsolete, yet on the current GitHub branch (at the time of writing this comment), the ObsoleteAttribute no longer exists on the static API. What’s the decision behind reverting this?

    • jbogard

      This is all old, check out the wiki for static vs. instance. The wiki is always up to date, blog posts are not.