Browse Source

Support more collection types.

pull/17376/head
maliming 3 years ago
parent
commit
9d9fa7075f
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 88
      framework/src/Volo.Abp.ObjectMapping/Volo/Abp/ObjectMapping/DefaultObjectMapper.cs
  2. 40
      framework/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/AbpAutoMapperModule_Specific_ObjectMapper_Tests.cs

88
framework/src/Volo.Abp.ObjectMapping/Volo/Abp/ObjectMapping/DefaultObjectMapper.cs

@ -124,16 +124,12 @@ public class DefaultObjectMapper : IObjectMapper, ITransientDependency
protected virtual TDestination? TryToMapCollection<TSource, TDestination>(IServiceScope serviceScope, TSource source, TDestination? destination) protected virtual TDestination? TryToMapCollection<TSource, TDestination>(IServiceScope serviceScope, TSource source, TDestination? destination)
{ {
if (!typeof(TSource).IsGenericType || typeof(TSource).GetGenericTypeDefinition() != typeof(ICollection<>) || if (!IsCollectionGenericType<TSource, TDestination>(out var sourceArgumentType, out var destinationArgumentType, out var definitionGenericType))
!typeof(TDestination).IsGenericType || typeof(TDestination).GetGenericTypeDefinition() != typeof(ICollection<>))
{ {
//skip, not a collection
return default; return default;
} }
var sourceGenericTypeDefinition = typeof(TSource).GenericTypeArguments[0]; var mapperType = typeof(IObjectMapper<,>).MakeGenericType(sourceArgumentType, destinationArgumentType);
var destinationGenericTypeDefinition = typeof(TDestination).GenericTypeArguments[0];
var mapperType = typeof(IObjectMapper<,>).MakeGenericType(sourceGenericTypeDefinition, destinationGenericTypeDefinition);
var specificMapper = serviceScope.ServiceProvider.GetService(mapperType); var specificMapper = serviceScope.ServiceProvider.GetService(mapperType);
if (specificMapper == null) if (specificMapper == null)
{ {
@ -148,17 +144,87 @@ public class DefaultObjectMapper : IObjectMapper, ITransientDependency
x.GetParameters().Length == (destination == null ? 1 : 2)); x.GetParameters().Length == (destination == null ? 1 : 2));
}); });
var result = Activator.CreateInstance(typeof(Collection<>).MakeGenericType(destinationGenericTypeDefinition))!.As<IList>(); var sourceList = source!.As<IList>();
foreach (var sourceItem in (IEnumerable)source!) var result = definitionGenericType.IsGenericType
? Activator.CreateInstance(definitionGenericType.MakeGenericType(destinationArgumentType))!.As<IList>()
: Array.CreateInstance(destinationArgumentType, sourceList.Count);
for (var i = 0; i < sourceList.Count; i++)
{ {
result.Add(destination == null var invokeResult = destination == null
? method.Invoke(specificMapper, new [] { sourceItem })! ? method.Invoke(specificMapper, new [] { sourceList[i] })!
: method.Invoke(specificMapper, new [] { sourceItem, Activator.CreateInstance(destinationGenericTypeDefinition)! })!); : method.Invoke(specificMapper, new [] { sourceList[i], Activator.CreateInstance(destinationArgumentType)! })!;
if (definitionGenericType.IsGenericType)
{
result.Add(invokeResult);
}
else
{
result[i] = invokeResult;
}
} }
return (TDestination)result!; return (TDestination)result!;
} }
protected virtual bool IsCollectionGenericType<TSource, TDestination>(out Type sourceArgumentType, out Type destinationArgumentType, out Type definitionGenericType)
{
sourceArgumentType = default!;
destinationArgumentType = default!;
definitionGenericType = default!;
if ((!typeof(TSource).IsGenericType && !typeof(TSource).IsArray) ||
(!typeof(TDestination).IsGenericType && !typeof(TDestination).IsArray))
{
return false;
}
var supportedCollectionTypes = new[]
{
typeof(IEnumerable<>),
typeof(ICollection<>),
typeof(Collection<>),
typeof(IList<>),
typeof(List<>)
};
if (typeof(TSource).IsGenericType && supportedCollectionTypes.Any(x => x == typeof(TSource).GetGenericTypeDefinition()))
{
sourceArgumentType = typeof(TSource).GenericTypeArguments[0];
}
if (typeof(TSource).IsArray)
{
sourceArgumentType = typeof(TSource).GetElementType()!;
}
if (sourceArgumentType == default!)
{
return false;
}
definitionGenericType = typeof(List<>);
if (typeof(TDestination).IsGenericType && supportedCollectionTypes.Any(x => x == typeof(TDestination).GetGenericTypeDefinition()))
{
destinationArgumentType = typeof(TDestination).GenericTypeArguments[0];
if (typeof(TDestination).GetGenericTypeDefinition() == typeof(ICollection<>) ||
typeof(TDestination).GetGenericTypeDefinition() == typeof(Collection<>))
{
definitionGenericType = typeof(Collection<>);
}
}
if (typeof(TDestination).IsArray)
{
destinationArgumentType = typeof(TDestination).GetElementType()!;
definitionGenericType = typeof(Array);
}
return destinationArgumentType != default!;
}
protected virtual TDestination AutoMap<TSource, TDestination>(object source) protected virtual TDestination AutoMap<TSource, TDestination>(object source)
{ {
return AutoObjectMappingProvider.Map<TSource, TDestination>(source); return AutoObjectMappingProvider.Map<TSource, TDestination>(source);

40
framework/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/AbpAutoMapperModule_Specific_ObjectMapper_Tests.cs

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Shouldly; using Shouldly;
@ -29,20 +30,47 @@ public class AbpAutoMapperModule_Specific_ObjectMapper_Tests : AbpIntegratedTest
[Fact] [Fact]
public void Specific_Object_Mapper_Should_Be_Used_For_Collections_If_Registered() public void Specific_Object_Mapper_Should_Be_Used_For_Collections_If_Registered()
{ {
var dtos = _objectMapper.Map<ICollection<MyEntity>, ICollection<MyEntityDto2>>(new List<MyEntity>() _objectMapper.Map<IEnumerable<MyEntity>, IEnumerable<MyEntityDto2>>(new List<MyEntity>()
{ {
new MyEntity { Number = 42 } new MyEntity { Number = 42 }
}); }).First().Number.ShouldBe(43); //MyEntityToMyEntityDto2Mapper adds 1 to number of the source.
dtos.First().Number.ShouldBe(43); //MyEntityToMyEntityDto2Mapper adds 1 to number of the source.
dtos = _objectMapper.Map<ICollection<MyEntity>, ICollection<MyEntityDto2>>(new List<MyEntity>() _objectMapper.Map<ICollection<MyEntity>, ICollection<MyEntityDto2>>(new List<MyEntity>()
{
new MyEntity { Number = 42 }
}).First().Number.ShouldBe(43); //MyEntityToMyEntityDto2Mapper adds 1 to number of the source.
_objectMapper.Map<Collection<MyEntity>, Collection<MyEntityDto2>>(new Collection<MyEntity>()
{
new MyEntity { Number = 42 }
}, new Collection<MyEntityDto2>() //When mapping to an existing collection, the destination collection is cleared first
{
new MyEntityDto2 { Number = 44 }
}).First().Number.ShouldBe(43); //MyEntityToMyEntityDto2Mapper adds 1 to number of the source.
_objectMapper.Map<IList<MyEntity>, IList<MyEntityDto2>>(new List<MyEntity>()
{
new MyEntity { Number = 42 }
}, new Collection<MyEntityDto2>() //When mapping to an existing collection, the destination collection is cleared first
{
new MyEntityDto2 { Number = 44 }
}).First().Number.ShouldBe(43); //MyEntityToMyEntityDto2Mapper adds 1 to number of the source.
_objectMapper.Map<List<MyEntity>, List<MyEntityDto2>>(new List<MyEntity>()
{ {
new MyEntity { Number = 42 } new MyEntity { Number = 42 }
}, new List<MyEntityDto2>() //When mapping to an existing collection, the destination collection is cleared first }, new List<MyEntityDto2>() //When mapping to an existing collection, the destination collection is cleared first
{ {
new MyEntityDto2 { Number = 44 } new MyEntityDto2 { Number = 44 }
}); }).First().Number.ShouldBe(43); //MyEntityToMyEntityDto2Mapper adds 1 to number of the source.
dtos.First().Number.ShouldBe(43); //MyEntityToMyEntityDto2Mapper adds 1 to number of the source.
_objectMapper.Map<MyEntity[], MyEntityDto2[]>(new MyEntity[]
{
new MyEntity { Number = 42 }
}, new MyEntityDto2[] //When mapping to an existing collection, the destination collection is cleared first
{
new MyEntityDto2 { Number = 44 }
}).First().Number.ShouldBe(43); //MyEntityToMyEntityDto2Mapper adds 1 to number of the source.
} }
[Fact] [Fact]

Loading…
Cancel
Save