Last active
January 25, 2024 17:13
-
-
Save ilyapalkin/8822638 to your computer and use it in GitHub Desktop.
An abstraction over AutoMapper to map several sources into single destination.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <summary> | |
/// Type mapping api | |
/// </summary> | |
public interface IMapper | |
{ | |
/// <summary> | |
/// Maps the specified source type instance to destination type instance. | |
/// </summary> | |
/// <typeparam name="TSource">Source type.</typeparam> | |
/// <typeparam name="TDestination">Destination type.</typeparam> | |
/// <param name="source">The source.</param> | |
/// <returns> | |
/// Instance of destination type. | |
/// </returns> | |
TDestination Map<TSource, TDestination>(TSource source); | |
/// <summary> | |
/// Maps the specified source type instance to destination type instance. | |
/// </summary> | |
/// <typeparam name="TDestination">The type of the destination instance.</typeparam> | |
/// <param name="source">The source instance.</param> | |
/// <returns> | |
/// Instance of destination type. | |
/// </returns> | |
TDestination MapTo<TDestination>(object source); | |
/// <summary> | |
/// Maps the specified source type instance to destination type instance. | |
/// </summary> | |
/// <param name="source">The source instance.</param> | |
/// <returns>Fluent interface for mapping.</returns> | |
IMapBuilder Map(object source); | |
} | |
/// <summary> | |
/// Fluent interface for mapping. | |
/// </summary> | |
public interface IMapBuilder | |
{ | |
/// <summary> | |
/// Maps the specified source type instance to destination type instance. | |
/// </summary> | |
/// <param name="source">The source instance.</param> | |
/// <returns>Fluent interface for mapping.</returns> | |
IMapBuilder Map(object source); | |
/// <summary> | |
/// Maps the specified earlier source type instances to destination type instance. | |
/// </summary> | |
/// <typeparam name="TDestination">The type of the destination.</typeparam> | |
/// <param name="destination">The destination object.</param> | |
/// <returns> | |
/// Instance of destination type. | |
/// </returns> | |
TDestination To<TDestination>(TDestination destination); | |
/// <summary> | |
/// Maps the specified earlier source type instances to destination type instance. | |
/// </summary> | |
/// <typeparam name="TDestination">The type of the destination.</typeparam> | |
/// <returns> | |
/// Instance of destination type. | |
/// </returns> | |
TDestination To<TDestination>(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <summary> | |
/// Type mapping api | |
/// </summary> | |
public class Mapper : IMapper | |
{ | |
/// <summary> | |
/// Maps the specified source type instance to destination type instance. | |
/// </summary> | |
/// <typeparam name="TSource">Source type.</typeparam> | |
/// <typeparam name="TDestination">Destination type.</typeparam> | |
/// <param name="source">The source.</param> | |
/// <returns> | |
/// Instance of destination type. | |
/// </returns> | |
public TDestination Map<TSource, TDestination>(TSource source) | |
{ | |
return MapTo<TDestination>(source); | |
} | |
/// <summary> | |
/// Maps the specified source type instance to destination type instance. | |
/// </summary> | |
/// <typeparam name="TDestination">The type of the destination instance.</typeparam> | |
/// <param name="source">The source instance.</param> | |
/// <returns> | |
/// Instance of destination type. | |
/// </returns> | |
public TDestination MapTo<TDestination>(object source) | |
{ | |
return Map(source) | |
.To<TDestination>(); | |
} | |
/// <summary> | |
/// Maps the specified source type instance to destination type instance. | |
/// </summary> | |
/// <param name="source">The source instance.</param> | |
/// <returns>Fluent interface for mapping.</returns> | |
public IMapBuilder Map(object source) | |
{ | |
return new MapBuilder(source); | |
} | |
#region Internal types | |
/// <summary> | |
/// Fluent interface for mapping. | |
/// </summary> | |
internal class MapBuilder : IMapBuilder | |
{ | |
private readonly List<object> sources = new List<object>(); | |
/// <summary> | |
/// Initialises a new instance of the <see cref="MapBuilder"/> class. | |
/// </summary> | |
/// <param name="source">The source instance.</param> | |
public MapBuilder(object source) | |
{ | |
sources.Add(source); | |
} | |
/// <summary> | |
/// Maps the specified source type instance to destination type instance. | |
/// </summary> | |
/// <param name="source">The source instance.</param> | |
/// <returns>Fluent interface for mapping.</returns> | |
public IMapBuilder Map(object source) | |
{ | |
sources.Add(source); | |
return this; | |
} | |
/// <summary> | |
/// Maps the specified earlier source type instances to destination type instance. | |
/// </summary> | |
/// <typeparam name="TDestination">The type of the destination.</typeparam> | |
/// <param name="destination">The destination object.</param> | |
/// <returns> | |
/// Instance of destination type. | |
/// </returns> | |
public TDestination To<TDestination>(TDestination destination) | |
{ | |
sources.ForEach(source => Map(source, destination)); | |
return destination; | |
} | |
/// <summary> | |
/// Maps the specified earlier source type instances to destination type instance. | |
/// </summary> | |
/// <typeparam name="TDestination">The type of the destination.</typeparam> | |
/// <returns> | |
/// Instance of destination type. | |
/// </returns> | |
public TDestination To<TDestination>() | |
{ | |
return sources.Aggregate(default(TDestination), (destination, source) => Map(source, destination)); | |
} | |
/// <summary> | |
/// Maps specified earlier source type instances to destination type instance. | |
/// </summary> | |
/// <param name="destinationType">The type of the destination.</param> | |
/// <returns> | |
/// Instance of destination type. | |
/// </returns> | |
public object ToType(Type destinationType) | |
{ | |
return sources.Aggregate<object, object>(null, (destination, source) => Map(source, destination, destinationType)); | |
} | |
private TDestination Map<TDestination>(object source, TDestination destination) | |
{ | |
return destination != null | |
? (TDestination)AutoMapper.Mapper.Map(source, destination, source.GetType(), typeof(TDestination)) | |
: AutoMapper.Mapper.Map<TDestination>(source); | |
} | |
private object Map(object source, object destination, Type destinationType) | |
{ | |
return destination != null | |
? AutoMapper.Mapper.Map(source, destination, source.GetType(), destinationType) | |
: AutoMapper.Mapper.Map(source, source.GetType(), destinationType); | |
} | |
} | |
#endregion | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class MapperTest | |
{ | |
private readonly IMapper target; | |
public MapperTest() | |
{ | |
AutoMapper.Mapper.CreateMap<SourceA, Destination>(); | |
AutoMapper.Mapper.CreateMap<SourceB, Destination>(); | |
target = new Mapper(); | |
} | |
[Fact] | |
public void Map_MapsSourceToDestinationObject_viaMapBuilder() | |
{ | |
// Arrange. | |
var source = new SourceA { A = "a value" }; | |
Destination destination = new DestinationProxy(); | |
// Act. | |
var result = target.Map(source).To(destination); | |
// Assert. | |
result.Should().BeSameAs(destination); | |
Assert.Equal(source.A, result.A); | |
result.A.Should().Be(source.A); | |
result.B.Should().BeNull(); | |
} | |
[Fact] | |
public void Map_MapsSourceToDestinationType_viaMapBuilder() | |
{ | |
// Arrange. | |
var source = new SourceA { A = "a value" }; | |
// Act. | |
var result = target.Map(source).To<Destination>(); | |
// Assert. | |
result.A.Should().Be(source.A); | |
result.B.Should().BeNull(); | |
} | |
[Fact] | |
public void Map_MapsManySourcesToDestinationObject_viaMapBuilder() | |
{ | |
// Arrange. | |
var sourceA = new SourceA { A = "a value" }; | |
var sourceB = new SourceB { B = "b value" }; | |
Destination destination = new DestinationProxy(); | |
// Act. | |
var result = target.Map(sourceA).Map(sourceB).To(destination); | |
// Assert. | |
result.Should().BeSameAs(destination); | |
result.A.Should().Be(sourceA.A); | |
result.B.Should().Be(sourceB.B); | |
} | |
[Fact] | |
public void Map_MapsManySourcesToDestinationType_viaMapBuilder() | |
{ | |
// Arrange. | |
var sourceA = new SourceA { A = "a value" }; | |
var sourceB = new SourceB { B = "b value" }; | |
// Act. | |
var result = target.Map(sourceA).Map(sourceB).To<Destination>(); | |
// Assert. | |
result.A.Should().Be(sourceA.A); | |
result.B.Should().Be(sourceB.B); | |
} | |
[Fact] | |
public void Map_MapsManySourcesToTypeOfDestination_viaMapBuilder() | |
{ | |
// Arrange. | |
var sourceA = new SourceA { A = "a value" }; | |
var sourceB = new SourceB { B = "b value" }; | |
// Act. | |
var result = target.Map(sourceA).Map(sourceB).ToType(typeof(Destination)); | |
// Assert. | |
result.Should().BeOfType<Destination>(); | |
var destination = (Destination)result; | |
destination.A.Should().Be(sourceA.A); | |
destination.B.Should().Be(sourceB.B); | |
} | |
[Fact] | |
public void Map_MapsSourceToDestinationType() | |
{ | |
// Arrange. | |
var source = new SourceA { A = "a value" }; | |
// Act. | |
var result = target.Map<SourceA, Destination>(source); | |
// Assert. | |
result.A.Should().Be(source.A); | |
result.B.Should().BeNull(); | |
} | |
[Fact] | |
public void MapTo_MapsSourceToDestinationType() | |
{ | |
// Arrange. | |
var source = new SourceA { A = "a value" }; | |
// Act. | |
var result = target.MapTo<Destination>(source); | |
// Assert. | |
result.A.Should().Be(source.A); | |
result.B.Should().BeNull(); | |
} | |
#region Internals | |
private class SourceA | |
{ | |
public string A { get; set; } | |
} | |
private class SourceB | |
{ | |
public string B { get; set; } | |
} | |
private class Destination | |
{ | |
public string A { get; private set; } | |
public string B { get; private set; } | |
} | |
/// <summary> | |
/// It is used to make sure that mapper works with proxies. | |
/// </summary> | |
private class DestinationProxy : Destination | |
{ | |
} | |
#endregion | |
} |
Getting following error after using classes as specified:
An object reference is required for the non-static field, method, or property 'Mapper.Map(object, object, Type, Type)'
I am looking a generic way to map single source from multiple destination.
Thanks.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Needs to come with AutoMapper as standard in my opinion, good work!