diff --git a/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AutoMapperObjectMapper.cs b/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AutoMapperObjectMapper.cs index 0c7986b24a..d943283514 100644 --- a/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AutoMapperObjectMapper.cs +++ b/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AutoMapperObjectMapper.cs @@ -1,25 +1,28 @@ -using AutoMapper; +using System; +using AutoMapper; using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.ObjectMapping; using Volo.DependencyInjection; namespace Volo.Abp.AutoMapper { [Dependency(ServiceLifetime.Transient, ReplaceServices = true)] - public class AutoMapperObjectMapper : Volo.Abp.ObjectMapping.IObjectMapper + public class AutoMapperObjectMapper : DefaultObjectMapper { private readonly IMapper _mapper; - public AutoMapperObjectMapper(IMapperAccessor mapper) + public AutoMapperObjectMapper(IMapperAccessor mapper, IServiceProvider serviceProvider) + : base(serviceProvider) { _mapper = mapper.Mapper; } - public TDestination Map(object source) + protected override TDestination AutoMap(object source) { return _mapper.Map(source); } - public TDestination Map(TSource source, TDestination destination) + protected override TDestination AutoMap(TSource source, TDestination destination) { return _mapper.Map(source, destination); } diff --git a/src/Volo.Abp/Volo/Abp/ObjectMapping/DefaultObjectMapper.cs b/src/Volo.Abp/Volo/Abp/ObjectMapping/DefaultObjectMapper.cs new file mode 100644 index 0000000000..04c9ee60a9 --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/ObjectMapping/DefaultObjectMapper.cs @@ -0,0 +1,68 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using Volo.DependencyInjection; + +namespace Volo.Abp.ObjectMapping +{ + //TODO: It can be slow to always check if service is available. Test it and optimize if necessary. + + public class DefaultObjectMapper : IObjectMapper, ISingletonDependency + { + private readonly IServiceProvider _serviceProvider; + + public DefaultObjectMapper(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } + + public virtual TDestination Map(TSource source) + { + if (source == null) + { + return default(TDestination); + } + + //Check if a specific mapper is registered + using (var scope = _serviceProvider.CreateScope()) + { + var specificMapper = scope.ServiceProvider.GetService>(); + if (specificMapper != null) + { + return specificMapper.Map(source); + } + } + + return AutoMap(source); + } + + public virtual TDestination Map(TSource source, TDestination destination) + { + if (source == null) + { + return default(TDestination); + } + + //Check if a specific mapper is registered + using (var scope = _serviceProvider.CreateScope()) + { + var specificMapper = scope.ServiceProvider.GetService>(); + if (specificMapper != null) + { + return specificMapper.Map(source, destination); + } + } + + return AutoMap(source, destination); + } + + protected virtual TDestination AutoMap(object source) + { + throw new NotImplementedException($"Can not map from given object ({source}) to {typeof(TDestination).AssemblyQualifiedName}."); + } + + protected virtual TDestination AutoMap(TSource source, TDestination destination) + { + throw new NotImplementedException($"Can no map from {typeof(TSource).AssemblyQualifiedName} to {typeof(TDestination).AssemblyQualifiedName}."); + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp/Volo/Abp/ObjectMapping/IObjectMapper.cs b/src/Volo.Abp/Volo/Abp/ObjectMapping/IObjectMapper.cs index 70212b1ff4..cc28d69ca0 100644 --- a/src/Volo.Abp/Volo/Abp/ObjectMapping/IObjectMapper.cs +++ b/src/Volo.Abp/Volo/Abp/ObjectMapping/IObjectMapper.cs @@ -9,8 +9,9 @@ /// Converts an object to another. Creates a new object of . /// /// Type of the destination object + /// Type of the source object /// Source object - TDestination Map(object source); + TDestination Map(TSource source); /// /// Execute a mapping from the source object to the existing destination object @@ -22,4 +23,11 @@ /// Returns the same object after mapping operation TDestination Map(TSource source, TDestination destination); } + + public interface IObjectMapper + { + TDestination Map(TSource source); + + TDestination Map(TSource source, TDestination destination); + } } diff --git a/src/Volo.Abp/Volo/Abp/ObjectMapping/NotImplementedObjectMapper.cs b/src/Volo.Abp/Volo/Abp/ObjectMapping/NotImplementedObjectMapper.cs deleted file mode 100644 index 064991e4d3..0000000000 --- a/src/Volo.Abp/Volo/Abp/ObjectMapping/NotImplementedObjectMapper.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using Volo.DependencyInjection; - -namespace Volo.Abp.ObjectMapping -{ - public sealed class NotImplementedObjectMapper : IObjectMapper, ISingletonDependency - { - /// - /// Singleton instance. - /// - public static NotImplementedObjectMapper Instance { get; } = new NotImplementedObjectMapper(); - - public TDestination Map(object source) - { - throw new NotImplementedException("Abp.ObjectMapping.IObjectMapper should be implemented in order to map objects."); - } - - public TDestination Map(TSource source, TDestination destination) - { - throw new NotImplementedException("Abp.ObjectMapping.IObjectMapper should be implemented in order to map objects."); - } - } -} \ No newline at end of file diff --git a/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/AbpAutoMapperModule_Basic_Tests.cs b/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/AbpAutoMapperModule_Basic_Tests.cs index 57215028b4..b899f3164d 100644 --- a/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/AbpAutoMapperModule_Basic_Tests.cs +++ b/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/AbpAutoMapperModule_Basic_Tests.cs @@ -10,30 +10,32 @@ namespace Volo.Abp.AutoMapper { public class AbpAutoMapperModule_Basic_Tests : AbpIntegratedTest { + private readonly IObjectMapper _objectMapper; + + public AbpAutoMapperModule_Basic_Tests() + { + _objectMapper = ServiceProvider.GetRequiredService(); + } + [Fact] public void Should_Replace_ObjectMapper() { - var objectMapper = ServiceProvider.GetRequiredService(); - Assert.True(objectMapper is AutoMapperObjectMapper); + Assert.True(_objectMapper is AutoMapperObjectMapper); } [Fact] public void Should_Map_Objects_With_AutoMap_Attributes() { - var objectMapper = ServiceProvider.GetRequiredService(); - - var dto = objectMapper.Map(new MyEntity {Number = 42}); + var dto = _objectMapper.Map(new MyEntity {Number = 42}); dto.Number.ShouldBe(42); } [Fact] public void Should_Not_Map_Objects_With_AutoMap_Attributes() { - var objectMapper = ServiceProvider.GetRequiredService(); - Assert.ThrowsAny(() => { - objectMapper.Map(new MyEntity {Number = 42}); + _objectMapper.Map(new MyEntity {Number = 42}); }); } } diff --git a/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/AbpAutoMapperModule_Specific_ObjectMapper_Tests.cs b/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/AbpAutoMapperModule_Specific_ObjectMapper_Tests.cs new file mode 100644 index 0000000000..96c3c05eb1 --- /dev/null +++ b/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/AbpAutoMapperModule_Specific_ObjectMapper_Tests.cs @@ -0,0 +1,26 @@ +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Volo.Abp.AutoMapper.SampleClasses; +using Volo.Abp.ObjectMapping; +using Volo.Abp.TestBase; +using Xunit; + +namespace Volo.Abp.AutoMapper +{ + public class AbpAutoMapperModule_Specific_ObjectMapper_Tests : AbpIntegratedTest + { + private readonly IObjectMapper _objectMapper; + + public AbpAutoMapperModule_Specific_ObjectMapper_Tests() + { + _objectMapper = ServiceProvider.GetRequiredService(); + } + + [Fact] + public void Should_Use_Specific_Object_Mapper_If_Registered() + { + var dto = _objectMapper.Map(new MyEntity { Number = 42 }); + dto.Number.ShouldBe(43); //MyEntityToMyEntityDto2Mapper adds 1 to number of the source. + } + } +} \ No newline at end of file diff --git a/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/AutofacTestModule.cs b/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/AutofacTestModule.cs index 052381dbd6..46d5e49e55 100644 --- a/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/AutofacTestModule.cs +++ b/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/AutofacTestModule.cs @@ -8,7 +8,7 @@ namespace Volo.Abp.AutoMapper { public override void ConfigureServices(IServiceCollection services) { - services.AddAssemblyOf(); + services.AddAssemblyOf(); } } } \ No newline at end of file diff --git a/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/SampleClasses/MyEntityDto2.cs b/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/SampleClasses/MyEntityDto2.cs new file mode 100644 index 0000000000..b95cffd0dd --- /dev/null +++ b/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/SampleClasses/MyEntityDto2.cs @@ -0,0 +1,9 @@ +using Volo.Abp.Application.Services.Dtos; + +namespace Volo.Abp.AutoMapper.SampleClasses +{ + public class MyEntityDto2 : EntityDto + { + public int Number { get; set; } + } +} \ No newline at end of file diff --git a/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/SampleClasses/MyEntityToMyEntityDto2Mapper.cs b/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/SampleClasses/MyEntityToMyEntityDto2Mapper.cs new file mode 100644 index 0000000000..4c8b3281bf --- /dev/null +++ b/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/SampleClasses/MyEntityToMyEntityDto2Mapper.cs @@ -0,0 +1,25 @@ +using Volo.Abp.ObjectMapping; +using Volo.DependencyInjection; + +namespace Volo.Abp.AutoMapper.SampleClasses +{ + [ExposeServices(typeof(IObjectMapper))] + public class MyEntityToMyEntityDto2Mapper : IObjectMapper, ITransientDependency + { + public MyEntityDto2 Map(MyEntity source) + { + return new MyEntityDto2 + { + Id = source.Id, + Number = source.Number + 1 + }; + } + + public MyEntityDto2 Map(MyEntity source, MyEntityDto2 destination) + { + destination.Id = source.Id; + destination.Number = source.Number + 1; + return destination; + } + } +} \ No newline at end of file