A C# Automapper trick

AD, please don't block.

Writing blog posts is infinitely valuable in helping me memorize new tricks; even when it does not work, I can still more easily google for myself. So, today’s blog post is meant to continue this glorious tradition.

In C# development it’s pretty common to have to map fields between objects of different class. Automapper simplifies a lot the work.

For instance, in the simplest scenario given the two classes

public class ProductDto {
  public string name { get; set; }
  public string code { get; set; }
  public decimal price { get; set; }
  public string currency { get; set; }
}

public class Product {
  public string Name { get; set; }
  public string Code { get; set; }
  public decimal Price { get; set; }
  public string Currency { get; set; }
}

in order to copy values from ProductDto fields into a new Product instance, we’ve to execute Automapper’s map method

// ProductDto response.product
Product product = Mapper.Map<Product>(response.product);

and assure that the mapping has been configured sometime before:

Mapper.CreateMap<ProductDto, Product>()
  .ForMember(dest => dest.Code, x => x.MapFrom(src => src.code.ToUpper()));

Easy peasy. That has the nice bonus effect of working even with list of objects.

List<Product> products = Mapper.Map<List<Product>>(response.products);

Let’s say now that one of Product’s field is not defined in the source object, but it’s known at run time in the context of the method that invokes the mapping. An easy way to get out this situation is

List<Product> products = Mapper.Map<List<Product>>(response.products);

foreach(var product in products) {
  product.Foo = ...
}

… but I cannot do this without feel guilty; even more after I’ve found that Automapper supports this use case almost as easily as the others.

Let’s start changing how we configure the mapping.

Mapper.CreateMap<ProductDto, Product>()
  .ForMember(dest => dest.Foo, x => x.ResolveUsing(res => res.Context.Options.Items["Foo"]));

then how we map at runtime

List<Product> products = Mapper.Map<List<ProductDto>, List<Product>>(response.products, opt => opt.Items["Foo"] = "Bar");

and now the mapping should work just fine.