1

I have some Automapper profiles, and I create two different mapper instances at runtime according to the situations. I need to ignore some members mapped inside these profiles at runtime for one of the mapper instances. Considering the example below how can I achieve this?

public class Foo
{
    public object SomeField { get; set; }
}

public class FooDto
{
    public object SomeField { get; set; }
}

public class FooProfile : Profile
{
    public FooProfile()
    {
        CreateMap<Foo, FooDto>();
    }
}

Create multiple mapper instances using the profile, and with different mapping expressions, at runtime:

public class MapperFactory
{
    public IMapper Create(bool isSomeFieldIgnored)
    {
        MapperConfiguration configuration;
        if (isSomeFieldIgnored)
        {
            configuration = new MapperConfiguration(expression =>
            {
                expression.AddProfile<FooProfile>();
                // ignore the SomeField here!
            });
        }
        else
        {
            configuration = new MapperConfiguration(expression =>
            {
                expression.AddProfile<FooProfile>();
            });
        }

        return configuration.CreateMapper();
    }
}
Mahdi
  • 119
  • 2
  • 11
  • Are you asking how to ignore the field, or how to use different profiles? – stuartd Nov 27 '20 at 12:31
  • when you map ignore the fields `CreateMap().ForMember(x => x.SomeField , opt => opt.Ignore());` – coder_b Nov 27 '20 at 12:35
  • @stuartd It is not a simple member ignoring. I'm going to ignore a member inside a mapping which has been already added to the configuration using a profile. In the example, the mapping from Foo to FooDto has been already defined in the profile, and I want to ignore the "SomeField" member at runtime after adding the profile. – Mahdi Nov 27 '20 at 12:55
  • @coder_b It is not a simple member ignoring. I'm going to ignore a member inside a mapping which has been already added to the configuration using a profile. In the example, the mapping from Foo to FooDto has been already defined in the profile, and I want to ignore the "SomeField" member at runtime after adding the profile. – Mahdi Nov 27 '20 at 12:58
  • Why not have two profiles, one which ignores the field and one that doesn't, and choose which to use at runtime? – stuartd Nov 27 '20 at 16:39
  • @stuartd having two profiles and repeating the majority of their codes is exactly what I want to avoid. – Mahdi Nov 27 '20 at 16:47
  • 1
    You don't need to do that, all you need is to have a virtual method in Profile1 which Profile2 (which subclasses Profile1) overrides to make the changes required. – stuartd Nov 27 '20 at 16:57
  • @stuartd the Foo class in this example corresponds to Entity classes in my real code for which defining two different profiles is very bothering and ugly to me. In my real code, the changes I want to apply for the Mapper1 is applied for all entities dynamically at runtime, except entities that have specific mapping defined by profiles. – Mahdi Nov 27 '20 at 17:16
  • Been a while since I used AutoMapper but if you can’t change the mappings once they've been loaded (which I assume is the case, or you would have just done that) then I don’t see many other choices than having two profiles? Maybe update your question with code that shows your actual problem? – stuartd Nov 28 '20 at 00:58
  • why do you have to do this conditional mapping anyway? – stuartd Nov 28 '20 at 01:03
  • @stuartd the point is to change the mapping expression outside the profile and avoid having two profiles for one entity which their majority of codes are the same. – Mahdi Nov 28 '20 at 15:48
  • @stuartd my real code is complicated and causes confusion for which I tried to show the problem in a simple example. The reason for having conditional mapping is ignoring the children collection navigations to avoid the cyclic reference and improve the performance of Automapper. For most of my entities that have default mapping, the ignoring children collection navigations are applied dynamically at runtime, but some of the entities have custom profiles and I have to ignore these navigations inside them. – Mahdi Nov 28 '20 at 15:49
  • @stuartd after I read your comment about inheritance between profile 1 and profile 2 and using the virtual method to override the difference, I thought still a single profile can be used if the condition is passed to the constructor of the profile and the expression is changed based on this condition. Then the profile is instantiated according to the condition and Automapper adds the profile instance instead of profile type. I posted the solution here. – Mahdi Nov 28 '20 at 16:12

2 Answers2

2

I couldn't find a way to change the mapping expression outside the profile and nobody answered it. So I had to solve the problem in another way. The example below shows the solution in which the runtime condition is applied to the expression using a parameter in the constructor of the profile.

public class Foo
{
    public object SomeField { get; set; }
}

public class FooDto
{
    public object SomeField { get; set; }
}

public class FooProfile : Profile
{
    public FooProfile()
        : this(false)
    {
        // the profile must have a parameterless constructor.
    }

    public FooProfile(bool isSomeFieldIgnored)
    {
        var expression =
            CreateMap<Foo, FooDto>();

        // ignore the SomeField is necessary.
        if (isSomeFieldIgnored)
            expression.ForMember(fooDto => fooDto.SomeField,
                options => options.Ignore());
    }
}
    

The profile is instantiated at runtime and the condition is passed to its constructor. Therefore, the mapper factory can be used to get two different mapper instances:

public class MapperFactory
{
    public IMapper Create(bool isSomeFieldIgnored)
    {
        var configuration = new MapperConfiguration(expression =>
        {
            // instantiate the profile at runtime and pass the isSomeFieldIgnore as 
            // its constructor argument.
            var fooProfile =
                (Profile)Activator.CreateInstance(typeof(FooProfile),
                    new object[] { isSomeFieldIgnored });

            expression.AddProfile(fooProfile);
        });

        return configuration.CreateMapper();
    }
}
Mahdi
  • 119
  • 2
  • 11
0

Maybe instead of opt.Ignore() you can use opt.Condition()? If you can base your trigger to ignore a member on a static condition, that is.

https://docs.automapper.org/en/stable/Conditional-mapping.html

"Conditionally map this member against the source, destination, source and destination members."

  • The condition comes from the outside when the mapper is created in the mapper factory (in my example, the "isSomeFieldIgnored" parameter of the Create method of the MapperFactory), and not from the values of the source and destination members. – Mahdi Nov 27 '20 at 13:24