Support more collection types.

pull/17376/head
maliming 2 years ago
parent e05d105247
commit 9d9fa7075f
No known key found for this signature in database
GPG Key ID: A646B9CB645ECEA4

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

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

Loading…
Cancel
Save