48

I am using AutoMapper in my ASP.NET MVC website to map my database objects to ViewModel objects and I am trying to use several profiles to map the same types, but using another logic. I had the idea of doing so by reading Matt's blog post where he says:

The really key part is the AutoMapper configuration profile. You can group configurations with profiles. Maybe in one profile you format dates in one way, in another profile you format dates in another way. I’m just using one profile here.

So I created a profile for one case:

public class MyProfile : Profile
{
    protected override string ProfileName
    {
        get
        {
            return "MyProfile";
        }
    }

    protected override void Configure()
    {
        CreateMap<DateTime, String>().ConvertUsing<StringFromDateTimeTypeConverter>();
    }
}

public class StringFromDateTimeTypeConverter : ITypeConverter<DateTime, String>
{
    public string Convert(DateTime source)
    {
        return source.ToString("dd/mm/yyyy", CultureInfo.InvariantCulture);
    }
}

And another one for another case:

public class MyProfile2 : Profile
{
    protected override string ProfileName
    {
        get
        {
            return "MyProfile2";
        }
    }

    protected override void Configure()
    {
        CreateMap<DateTime, String>().ConvertUsing<AnotherStringFromDateTimeTypeConverter>();
    }
}

public class AnotherStringFromDateTimeTypeConverter : ITypeConverter<DateTime, String>
{
    public string Convert(DateTime source)
    {
        return source.ToString("mm - yyyy", CultureInfo.InvariantCulture);
    }
}

However, I cannot find any overload of the Mapper.Map<>() method to specify a profile. I also had a look at the Configuration object with no luck.
The last registered profile always takes precedence.

Is there a way to use profiles for this purpose?

wonea
  • 4,783
  • 17
  • 86
  • 139
sebd
  • 572
  • 1
  • 4
  • 8

2 Answers2

49

Profiles are for segregating common configuration applied across several type maps, like formatting. However, type maps are still global. You're better off creating separate Configuration objects, and creating a separate MappingEngine for each. The Mapper class is merely a static facade over each of those, with some lifecycle management.

Jimmy Bogard
  • 26,045
  • 5
  • 74
  • 69
  • Will that be thread safe though without the locking? For example 'MapperRegistry.AllMappers()' is static and 'TypeMapFactory' has a static dictionary. – Ian Warburton Jun 06 '11 at 17:23
  • Typically configuration is only called once per AppDomain at startup. You just need to find the right seam in whatever hosting model you have for where that goes (different for WCF, ASP.NET, WinForms etc.) – Jimmy Bogard Jun 06 '11 at 20:16
  • @sebd: were you able to figure out, how can we keeping multiple mappings for same types? It would be helpful, for example in case of combination with the Ignore configuration. – Baig Oct 11 '11 at 12:45
  • @JimmyBogard: Isn't there any performance hit for creating multiple instances of Configuration and MappingEngine for different configurations? – Baig Oct 11 '11 at 14:03
  • No, just a performance hit for startup on configuration. But even when things are shared, it shouldn't be that big of a problem. – Jimmy Bogard Oct 20 '11 at 22:07
  • 36
    A sample of how to implement an IConfigurationProvider or a sample using a new MappingEngine with a supplied configuration would be great. The sparse documentation for AutoMapper makes it a little tricky for these more complex scenarios. Can someone perhaps improve this answer with a little code? – Jim Oct 31 '11 at 12:36
  • 5
    it took me 20 minutes to find the answer of my question. The answer was in your sentence: "Profiles are for segregating common configuration" . Thank you. – Ashi Feb 28 '16 at 06:28
  • I don't understand what "for segregating common configuration applied across several type maps, like formatting." means, but would like to. What did "common configuration" mean, and "formatting" of what? – xr280xr Feb 05 '20 at 00:10
4

As of AutoMapper v10. You need to instantiate an IMapper that uses the the specific Profile that you wanted. Something like this:

MapperConfiguration config = new MapperConfiguration(cfg => {
    cfg.AddProfile(new MyProfile());
});
IMapper mapper = new Mapper(config);
var myHuman = mapper.map<Human>(myPerson);

Example that can map a Person to a Human with either a Name that is lowercase or uppercase.

class Program
{
    static void Main(string[] args)
    {
        Person myPerson = new Person
        {
            Name = "HAzEL NUtt"
        };
        Console.WriteLine(myPerson.Name);

        MapperConfiguration configUpper = new MapperConfiguration(cfg => {
            cfg.AddProfile(new UpperCaseProfile());
        });
        IMapper mapperUpper = new Mapper(configUpper);
        Console.WriteLine(mapperUpper.Map<Human>(myPerson).Name);

        MapperConfiguration configLower = new MapperConfiguration(cfg => {
            cfg.AddProfile(new LowerCaseProfile());
        });
        IMapper mapperLower = new Mapper(configLower);
        Console.WriteLine(mapperLower.Map<Human>(myPerson).Name);
    }
}

public class LowerCaseProfile : Profile
{
    public LowerCaseProfile()
    {
        CreateMap<Person, Human>()
                .ForMember(d => d.Name, opt => opt.MapFrom(src => src.Name.ToLower()));
    }
}

public class UpperCaseProfile : Profile
{
    public UpperCaseProfile()
    {
        CreateMap<Person, Human>()
            .ForMember(d => d.Name, opt => opt.MapFrom(src => src.Name.ToUpper()));
    }
}

Here is the output:

HAzEL NUtt
HAZEL NUTT
hazel nutt
Batibot323
  • 83
  • 4