Mapping an object to self

Viewed 63

We are currently using v8 of AutoMapper in our code and is planning to upgrade to 9.0+. For that we need to convert all static mappers to instance based.

We have a lot of code in our code base like this below.

public class ConfigModel 
{
    public int MyProperty1 { get; set; }
    ...
    
    public ConfigModel Load(ConfigEntity config)
    {
        Mapper.Map(config, this);
        return this;
    }
}

I am looking for something similar that we can do with IMapper. We just need to map an Entity to the properties in the model.

3 Answers

I assume you're using dependency injection. You need somewhere to inject the mapper instance. I recommend a factory.

interface IConfigModelFactory
{
    ConfigModel CreateInstance(ConfigEntity config);
}

class ConfigModelFactory : IConfigModelFactory
{
    protected readonly IMapper _mapper;

    public ConfigModelFactory(IMapper mapper)
    {
        _mapper = mapper;
    }

    public ConfigModel CreateInstance(ConfigEntity entity)
    {
        return new ConfigModel(_mapper, entity);
    }
}

public class ConfigModel
{
    public ConfigModel(IMapper mapper, ConfigEntity entity)
    {
        mapper.Map(entity, this);
    }
}

Now wherever you need to create instances of the ConfigModel class, inject your factory, and the mapper will come along with it (assuming you've registered it). Use the factory instead of using new directly.

Turns out, AutoMapper has a method with a definition like this. I could not find it in the documentation page but this is part of the package.

//
// Summary:
//     Execute a mapping from the source object to the existing destination object.
//
// Parameters:
//   source:
//     Source object to map from
//
//   destination:
//     Destination object to map into
//
// Type parameters:
//   TSource:
//     Source type to use
//
//   TDestination:
//     Destination type
//
// Returns:
//     The mapped destination object, same instance as the destination object
TDestination Map<TSource, TDestination>(TSource source, TDestination destination);

So we can do this and in a model, we can map an entity to self.

this.mapper.Map(entity, this);

This can be unit tested using Moq like this.

var mapper = autoMocker.GetMock<IMapper>();
mapper.Setup(a => a.Map<Entity, Model>(It.IsAny<Entity>()))
    .Returns(new Model
    {
        Prop1 = "val 1",
        Prop2 = "val 2",
        ...
    });

I'm guessing that you have a class for ConfigModel and inside is this Load method, something like that:

public class ConfigModel{
   int MyProperty1 { get; private set; }

   public ConfigModel Load(ConfigEntity config)
   {
      Mapper.Map(config, this);
      return this;
   }

   public ConfigModel(int myProperty1) // <-constructor
   {
      MyProperty1 = myProperty1;
   }
}

So to map object to itself you don't even need a mapper, have a method that maps incoming object to this object:

public class ConfigModel{
   int MyProperty1 { get; private set; }

   public ConfigModel Load(ConfigEntity config)
   {
      Map(config);
      return this;
   }

   public void Map(ConfigModel configModel) // <- mapping method without using AutoMapper, using something like that does not collide with other mappings using AutoMapper
   {
      this.MyProperty1 = configModel.MyProperty1
      //... you can map as many properties as you like
   }

   public ConfigModel(int myProperty1) // <-constructor
   {
      MyProperty1 = myProperty1;
   }
}
Related