Search code examples
c#automapper

Automapper - Mapping a property to a method


I am currently working with classes that are defined as such:

public class User
{
    public string Name { get; private set; }
    public int Age { get; private set; }
    public bool Active { get; private set; }

    public void Name(string name)
    {
        Name = name;
    }

    public void Age(int age) 
    {
        Age = age;
    }

    public void Active(bool active) 
    {
        Active = active;
    }
}

public class UserDTO
{
    public string Name { get; set; }
    public int Age { get; set; }
    public bool Active { get; set; }
}

I have configured my mapper as such:

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

I have no control over the class User and will not be able to make any modifications to it.

var user = UserController.GetUser();
var userDTO = Mapper.Map<UserDTO>(user);

The above is how I usually do it from one direction, but now I want to be able to map UserDTO back to User and because setting of values are done through methods instead, I am not sure how to go about it.

What I would like to be able to do is:

userDTO.Name = "John Doe";
Mapper.Map(userDTO, user);

and behind the scene, it would actually do a user.Name("John Doe");


Solution

  • Because you have a special behaviour on creating a Destination instance out of a Source instance, you have to use the .ConvertUsing() method and manually create and fill your new (or existing) instance.

    An example would be:

    public class Source
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
    }
    
    public class Destination
    {
        private string _name;
        private string _id;
    
        public string Name { get => _name; }
        public string Id { get => _id; }
    
        public void UpdateName(string name)
        {
            _name = name;
        }
    
        public void UpdateId(string id)
        {
            _id = id;
        }
    }
    
    public class MappingProfile : Profile
    {
        public MappingProfile()
        {
            CreateMap<Source, Destination>()
                .ConvertUsing((source, existing, context) =>
                {
                    // If existing is not null, the user called .Map() with an existing
                    // destination instance, which should be filled.
                    var dest = existing ?? new Destination();
    
                    // Call special method to fill dest instance.
                    dest.UpdateName(source.Name);
    
                    // if mapper is needed for other (nested) mappings, it can be used
                    var id = context.Mapper.Map<string>(source.Id);
                    dest.UpdateId(id);
    
                    return dest;
                });
        }
    }
    
    public class Program
    {
        static void Main(string[] args)
        {
            var config = new MapperConfiguration(c => c.AddProfile<MappingProfile>());
            var mapper = config.CreateMapper();
    
            var source = new Source
            {
                Id = Guid.NewGuid(),
                Name = "Some name",
            };
            var dest = mapper.Map<Destination>(source);
    
            Console.WriteLine(JsonSerializer.Serialize(dest));
        }
    }