Geta Mapping is a project for common mapping logic. It consists of two libraries: Geta.Mapping and Geta.AutoMapper.
Geta.Mapping is a library with abstractions for common mapping logic.
dotnet add package Geta.Mapping
For StructureMap
or Lamar
, configure interfaces to connect to implementations automatically:
Scan(x =>
{
x.TheCallingAssembly();
x.WithDefaultConventions();
x.ConnectImplementationsToTypesClosing(typeof(IMapper<,>));
x.ConnectImplementationsToTypesClosing(typeof(ICreateFrom<,>));
});
Then create a mapping class you want to map one object to another. Inherit from IMapper<TFrom, TTo>
.
public class MyPocoToMyDtoMapper : IMapper<MyPoco, MyDto>
{
public virtual void Map(MyPoco from, MyDto to)
{
to.Name = from.Name;
}
}
This mapping implementation will work for any classes, even for those that have a constructor with parameters.
If your destination class has a parameter-less constructor, then you can implement Mapper<TFrom, TTo>
.
public class MyPocoToMyDtoMapper : Mapper<MyPoco, MyDto>
{
public override void Map(MyPoco from, MyDto to)
{
to.Name = from.Name;
}
}
Now you can use this mapper by injecting it.
When you want to map one object to another, then use IMapper<TFrom, TTo>
interface.
public class MyController
{
private readonly IMapper<MyPoco, MyDto> _myMapper;
public MyController(IMapper<MyPoco, MyDto> myMapper)
{
_myMapper = myMapper;
}
public IActionResult Index()
{
var myPoco = // Get a source object from DB or somewhere else
var myDto = new MyDto(true); // Instantiating _myDto_ manually as there is no parameter-less contructor
_myMapper.Map(myPoco, myDto);
// ...
}
}
When you want to create one object from another, and a destination object's class has a parameter-less constructor, your mapper should implement Mapper<TFrom, TTo>
and you should inject ICreateFrom<TFrom, TTo>
.
public class MyController
{
private readonly ICreateFrom<MyPoco, MyDto> _myDtoCreator;
public MyController(ICreateFrom<MyPoco, MyDto> myDtoCreator)
{
_myDtoCreator = myDtoCreator;
}
public IActionResult Index()
{
var myPoco = // Get a source object from DB or somewhere else
var myDto = _myDtoCreator.Create(myPoco);
// ...
}
}
Geta.AutoMapper is a small addition for Automapper library to simplify mapping configuration. It scans for existing mappings and provides a standardized way to do a custom mappings with automapper using ICustomMapping
interface.
- Scans for existing mappings in the solution
- Use
AutoMap
attribute to cover simple mapping cases (default functionality in AutoMapper - https://docs.automapper.org/en/stable/Attribute-mapping.html) - Use
ICustomMapping
interface for a custom mapping scenarios
Install-Package Geta.AutoMapper
Make sure to call AutoMapperConfig.LoadMappings(...)
on application startup.
var configExpression = new MapperConfigurationExpression();
/* can add custom configurations if needed */
AutoMapperConfig.LoadMappings(configExpression, Assembly.GetExecutingAssembly());
var mapper = new MapperConfiguration(configExpression).CreateMapper();
You can use two ways how to configure the mapping between two types:
- Decorate destination type with
AutoMap
attribute (functionality already available in AutoMapper); - Destination type implementing
ICustomMapping
interface.
public class AttributeMappingTestModel
{
public int SomeProperty { get; set; }
public AttributeMappingTestChildModel Child { get; set; }
}
public class AttributeMappingTestChildModel
{
public string Property { get; set; }
}
[AutoMap(typeof(AttributeMappingTestModel))]
public class AttributeMappingTestViewModel
{
[SourceMember(nameof(AttributeMappingTestModel.SomeProperty))]
public int PropertyA { get; set; }
public string ChildProperty { get; set; }
}
You can then simply call AutoMapper's mapper.Map<AttributeMappingTestViewModel>(modelToMap);
to map from a AttributeTestModel
to an AttributeTestViewModel
.
Note: `SourceMember` attribute isn't working for child object property mapping configurations. If destination object propery isn't following naming pattern {PropertyName}{ChildPropertyName} you will have to use custom mapping approach.
Use ICustomMappings
for more advanced mapping scenarios.
public class CustomMappingTestModel
{
public int SomeProperty { get; set; }
public DateTime OtherProperty { get; set; }
public CustomMappingChildModel Child { get; set; }
}
public class CustomMappingChildModel
{
public string SomeProperty { get; set; }
}
public class CustomMappingTestViewModel : ICustomMapping
{
public int Property { get; set; }
public DateTime OtherProperty { get; set; }
public string ChildProperty { get; set; }
public void CreateMapping(IMapperConfigurationExpression configuration)
{
configuration.CreateMap<CustomMappingTestModel, CustomMappingTestViewModel>()
.ForMember(d => d.ChildProperty, o => o.MapFrom(s => s.Child.SomeProperty))
.ForMember(d => d.Property, o => o.MapFrom(s => s.SomeProperty));
}
}