diff --git a/global.json b/global.json index cb6f51a..0abff92 100644 --- a/global.json +++ b/global.json @@ -1,5 +1,6 @@ { "sdk": { "version": "1.0.0-preview1-002702" - } + }, + "projects": [] } \ No newline at end of file diff --git a/src/AutoMapper.Collection-Signed/project.json b/src/AutoMapper.Collection-Signed/project.json index 67c07b3..81748c3 100644 --- a/src/AutoMapper.Collection-Signed/project.json +++ b/src/AutoMapper.Collection-Signed/project.json @@ -18,21 +18,27 @@ }, "dependencies": { - "AutoMapper": "4.2.1" + "AutoMapper": "5.0.0" }, "frameworks": { "net45": { + "dependencies": { + } }, "netstandard1.1": { "dependencies": { - "NETStandard.Library": "1.5.0-rc2-24027", - "System.Linq.Expressions": "4.0.11-rc2-24027" + "System.Threading": "4.0.11" }, "imports": [ "portable-net45+win8+dnxcore50", "portable-net45+win8" ] + }, + "netstandard1.3": { + "dependencies": { + "System.Threading": "4.0.11" + } + } } - } -} \ No newline at end of file + } \ No newline at end of file diff --git a/src/AutoMapper.Collection.EntityFramework/Extensions.cs b/src/AutoMapper.Collection.EntityFramework/Extensions.cs index 831ca49..b7ffaef 100644 --- a/src/AutoMapper.Collection.EntityFramework/Extensions.cs +++ b/src/AutoMapper.Collection.EntityFramework/Extensions.cs @@ -15,11 +15,10 @@ public static class Extensions /// Source table type to be updated /// DbSet to be updated /// Persistance object to Update or Remove data - [Obsolete("Use version that passes instance of IMapper")] public static IPersistance Persist(this DbSet source) where TSource : class { - return new Persistance(source, null); + return new Persistance(source, Mapper.Instance); } /// diff --git a/src/AutoMapper.Collection.EntityFramework/GenerateEntityFrameworkPrimaryKeyEquivilentExpressions.cs b/src/AutoMapper.Collection.EntityFramework/GenerateEntityFrameworkPrimaryKeyEquivilentExpressions.cs index 37f5087..6e91011 100644 --- a/src/AutoMapper.Collection.EntityFramework/GenerateEntityFrameworkPrimaryKeyEquivilentExpressions.cs +++ b/src/AutoMapper.Collection.EntityFramework/GenerateEntityFrameworkPrimaryKeyEquivilentExpressions.cs @@ -16,7 +16,7 @@ public class GenerateEntityFrameworkPrimaryKeyEquivilentExpressions /// IMapper used to find TypeMap between classes public GenerateEntityFrameworkPrimaryKeyEquivilentExpressions(IMapper mapper) - : base(new GenerateEntityFrameworkPrimaryKeyPropertyMaps(mapper)) + : base(new GenerateEntityFrameworkPrimaryKeyPropertyMaps(mapper.ConfigurationProvider)) { } @@ -24,9 +24,8 @@ public GenerateEntityFrameworkPrimaryKeyEquivilentExpressions(IMapper mapper) /// Generate EquivilencyExpressions based on EnityFramework's primary key /// Uses static API's Mapper for finding TypeMap between classes /// - [Obsolete("Use version that passes instance of IMapper")] public GenerateEntityFrameworkPrimaryKeyEquivilentExpressions() - : base(new GenerateEntityFrameworkPrimaryKeyPropertyMaps(null)) + : base(new GenerateEntityFrameworkPrimaryKeyPropertyMaps(Mapper.Configuration)) { } } diff --git a/src/AutoMapper.Collection.EntityFramework/GenerateEntityFrameworkPrimaryKeyPropertyMatches.cs b/src/AutoMapper.Collection.EntityFramework/GenerateEntityFrameworkPrimaryKeyPropertyMatches.cs index ff73304..7da754b 100644 --- a/src/AutoMapper.Collection.EntityFramework/GenerateEntityFrameworkPrimaryKeyPropertyMatches.cs +++ b/src/AutoMapper.Collection.EntityFramework/GenerateEntityFrameworkPrimaryKeyPropertyMatches.cs @@ -12,21 +12,19 @@ namespace AutoMapper.EntityFramework public class GenerateEntityFrameworkPrimaryKeyPropertyMaps : IGeneratePropertyMaps where TDatabaseContext : IObjectContextAdapter, new() { - private readonly IMapper _mapper; + private readonly IConfigurationProvider _configurationProvider; private readonly TDatabaseContext _context = new TDatabaseContext(); private readonly MethodInfo _createObjectSetMethodInfo = typeof(ObjectContext).GetMethod("CreateObjectSet", Type.EmptyTypes); - public GenerateEntityFrameworkPrimaryKeyPropertyMaps(IMapper mapper) + public GenerateEntityFrameworkPrimaryKeyPropertyMaps(IConfigurationProvider configurationProvider) { - _mapper = mapper; + _configurationProvider = configurationProvider; } public IEnumerable GeneratePropertyMaps(Type srcType, Type destType) { - var typeMap = _mapper == null - ? (Mapper.Configuration as IConfigurationProvider).ResolveTypeMap(srcType, destType) - : _mapper.ConfigurationProvider.ResolveTypeMap(srcType, destType); + var typeMap = _configurationProvider.ResolveTypeMap(srcType, destType); var propertyMaps = typeMap.GetPropertyMaps(); var createObjectSetMethod = _createObjectSetMethodInfo.MakeGenericMethod(destType); dynamic objectSet = createObjectSetMethod.Invoke(_context.ObjectContext, null); diff --git a/src/AutoMapper.Collection.LinqToSQL/GetLinqToSQLPrimaryKeyExpression.cs b/src/AutoMapper.Collection.LinqToSQL/GetLinqToSQLPrimaryKeyExpression.cs index 4052130..1a1bc95 100644 --- a/src/AutoMapper.Collection.LinqToSQL/GetLinqToSQLPrimaryKeyExpression.cs +++ b/src/AutoMapper.Collection.LinqToSQL/GetLinqToSQLPrimaryKeyExpression.cs @@ -10,7 +10,7 @@ public class GetLinqToSQLPrimaryKeyExpression : GenerateEquivilentExpressionsBas /// /// IMapper used to find TypeMap between classes public GetLinqToSQLPrimaryKeyExpression(IMapper mapper) - : base(new GetLinqToSQLPrimaryKeyProperties(mapper)) + : base(new GetLinqToSQLPrimaryKeyProperties(mapper.ConfigurationProvider)) { } @@ -18,9 +18,8 @@ public GetLinqToSQLPrimaryKeyExpression(IMapper mapper) /// Generate EquivilencyExpressions based on LinqToSQL's primary key /// Uses static API's Mapper for finding TypeMap between classes /// - [Obsolete("Use version that passes instance of IMapper")] public GetLinqToSQLPrimaryKeyExpression() - : base(new GetLinqToSQLPrimaryKeyProperties(null)) + : base(new GetLinqToSQLPrimaryKeyProperties(Mapper.Configuration)) { } } diff --git a/src/AutoMapper.Collection.LinqToSQL/GetLinqToSQLPrimaryKeyProperties.cs b/src/AutoMapper.Collection.LinqToSQL/GetLinqToSQLPrimaryKeyProperties.cs index eeacc31..d5c294f 100644 --- a/src/AutoMapper.Collection.LinqToSQL/GetLinqToSQLPrimaryKeyProperties.cs +++ b/src/AutoMapper.Collection.LinqToSQL/GetLinqToSQLPrimaryKeyProperties.cs @@ -9,18 +9,18 @@ namespace AutoMapper.Collection.LinqToSQL { public class GetLinqToSQLPrimaryKeyProperties : IGeneratePropertyMaps { - private readonly IMapper _mapper; - - public GetLinqToSQLPrimaryKeyProperties(IMapper mapper) + private readonly IConfigurationProvider _configurationProvider; + + public GetLinqToSQLPrimaryKeyProperties(IConfigurationProvider configurationProvider) { - _mapper = mapper; + if (configurationProvider == null) + throw new ArgumentNullException(nameof(configurationProvider)); + _configurationProvider = configurationProvider; } public IEnumerable GeneratePropertyMaps(Type srcType, Type destType) { - var typeMap = _mapper == null - ? (Mapper.Configuration as IConfigurationProvider).ResolveTypeMap(srcType, destType) - : _mapper.ConfigurationProvider.ResolveTypeMap(srcType, destType); + var typeMap = _configurationProvider.ResolveTypeMap(srcType, destType); var propertyMaps = typeMap.GetPropertyMaps(); var primaryKeyPropertyMatches = destType.GetProperties().Where(IsPrimaryKey).Select(m => propertyMaps.FirstOrDefault(p => p.DestinationProperty.Name == m.Name)).ToList(); diff --git a/src/AutoMapper.Collection.LinqToSQL/PersistanceExtensions.cs b/src/AutoMapper.Collection.LinqToSQL/PersistanceExtensions.cs index 265c01b..e06b166 100644 --- a/src/AutoMapper.Collection.LinqToSQL/PersistanceExtensions.cs +++ b/src/AutoMapper.Collection.LinqToSQL/PersistanceExtensions.cs @@ -15,11 +15,10 @@ public static class PersistanceExtensions /// Source table type to be updated /// Table to be updated /// Persistance object to Update or Remove data - [Obsolete("Use version that passes instance of IMapper")] public static IPersistance Persist(this Table source) where TSource : class { - return new Persistance(source, null); + return new Persistance(source, Mapper.Instance); } /// diff --git a/src/AutoMapper.Collection/Equivilency Expression/CustomExpressionVisitor.cs b/src/AutoMapper.Collection/Equivilency Expression/CustomExpressionVisitor.cs index 3315ab6..00b6db1 100644 --- a/src/AutoMapper.Collection/Equivilency Expression/CustomExpressionVisitor.cs +++ b/src/AutoMapper.Collection/Equivilency Expression/CustomExpressionVisitor.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Linq.Expressions; using System.Reflection; +using AutoMapper.Execution; namespace AutoMapper.EquivilencyExpression { @@ -29,11 +30,8 @@ protected override Expression VisitMember(MemberExpression node) var matchPM = _propertyMaps.FirstOrDefault(pm => pm.DestinationProperty.MemberInfo == node.Member); if (matchPM == null) throw new Exception("No matching PropertyMap"); - var sourceValueResolvers = matchPM.GetSourceValueResolvers(); - if (!sourceValueResolvers.All(r => r is IMemberGetter)) - throw new Exception("Not all member getters"); - - var memberGetters = sourceValueResolvers.OfType(); + var memberGetters = matchPM.SourceMembers; + var memberExpression = Expression.Property(Visit(node.Expression), memberGetters.First().MemberInfo as PropertyInfo); foreach (var memberGetter in memberGetters.Skip(1)) diff --git a/src/AutoMapper.Collection/Equivilency Expression/EquivilentExpression.cs b/src/AutoMapper.Collection/Equivilency Expression/EquivilentExpression.cs index 88b83a4..d02e375 100644 --- a/src/AutoMapper.Collection/Equivilency Expression/EquivilentExpression.cs +++ b/src/AutoMapper.Collection/Equivilency Expression/EquivilentExpression.cs @@ -1,6 +1,7 @@ using System; using System.Linq.Expressions; using System.Reflection; +using AutoMapper.Execution; namespace AutoMapper.EquivilencyExpression { @@ -12,61 +13,33 @@ static EquivilentExpression() { BadValue = new EquivilentExpression(); } - - public bool IsEquivlent(object source, object destination) - { - throw new NotImplementedException(); - } } - internal class EquivilentExpression : IEquivilentExpression , IToSingleSourceEquivalentExpression + internal class EquivilentExpression : IEquivilentExpression where TSource : class where TDestination : class { private readonly Expression> _equivilentExpression; + private readonly Func _equivilentFunc; public EquivilentExpression(Expression> equivilentExpression) { _equivilentExpression = equivilentExpression; + _equivilentFunc = _equivilentExpression.Compile(); } - public bool IsEquivlent(object source, object destination) + public bool IsEquivlent(TSource source, TDestination destination) { - if(!(source is TSource)) - throw new EquivilentExpressionNotOfTypeException(source.GetType(), typeof(TSource)); - if (!(destination is TDestination)) - throw new EquivilentExpressionNotOfTypeException(destination.GetType(), typeof(TDestination)); - return _equivilentExpression.Compile()(source as TSource, destination as TDestination); + return _equivilentFunc(source, destination); } - public Expression ToSingleSourceExpression(object source) + public Expression> ToSingleSourceExpression(TSource source) { if (source == null) throw new Exception("Invalid somehow"); - return GetSourceOnlyExpresison(source as TSource); - } - internal Expression GetSourceOnlyExpresison(TSource source) - { var expression = new ParametersToConstantVisitor(source).Visit(_equivilentExpression) as LambdaExpression; - return Expression.Lambda(expression.Body, _equivilentExpression.Parameters[1]); - } - - private class EquivilentExpressionNotOfTypeException : Exception - { - private readonly Type _objectType; - private readonly Type _expectedType; - - public EquivilentExpressionNotOfTypeException(Type objectType, Type expectedType) - { - _objectType = objectType; - _expectedType = expectedType; - } - - public override string Message - { - get { return string.Format("{0} does not equal or inherit from {1}", _objectType.Name, _expectedType.Name); } - } + return Expression.Lambda>(expression.Body, _equivilentExpression.Parameters[1]); } } diff --git a/src/AutoMapper.Collection/Equivilency Expression/IEquivilentExpression.cs b/src/AutoMapper.Collection/Equivilency Expression/IEquivilentExpression.cs index 0ee9fa6..8772687 100644 --- a/src/AutoMapper.Collection/Equivilency Expression/IEquivilentExpression.cs +++ b/src/AutoMapper.Collection/Equivilency Expression/IEquivilentExpression.cs @@ -1,14 +1,15 @@ +using System; using System.Linq.Expressions; namespace AutoMapper.EquivilencyExpression { public interface IEquivilentExpression { - bool IsEquivlent(object source, object destination); + } - - public interface IToSingleSourceEquivalentExpression + public interface IEquivilentExpression : IEquivilentExpression { - Expression ToSingleSourceExpression(object destination); + bool IsEquivlent(TSource source, TDestination destination); + Expression> ToSingleSourceExpression(TSource destination); } } \ No newline at end of file diff --git a/src/AutoMapper.Collection/Mappers/EquivlentExpressionAddRemoveCollectionMapper.cs b/src/AutoMapper.Collection/Mappers/EquivlentExpressionAddRemoveCollectionMapper.cs index d7eb29b..0f68fea 100644 --- a/src/AutoMapper.Collection/Mappers/EquivlentExpressionAddRemoveCollectionMapper.cs +++ b/src/AutoMapper.Collection/Mappers/EquivlentExpressionAddRemoveCollectionMapper.cs @@ -1,66 +1,43 @@ using System; -using System.Collections; -using System.Collections.Concurrent; +using System.Collections.Generic; using System.Linq; -using AutoMapper.EquivilencyExpression; -using AutoMapper.Internal; +using System.Linq.Expressions; using System.Reflection; +using AutoMapper.Configuration; +using AutoMapper.EquivilencyExpression; namespace AutoMapper.Mappers { public class EquivlentExpressionAddRemoveCollectionMapper : IObjectMapper { - private readonly ConcurrentDictionary _methodCache = new ConcurrentDictionary(); - private readonly CollectionMapper _collectionMapper = new CollectionMapper(); - - public object Map(ResolutionContext context) + private readonly CollectionMapper CollectionMapper = new CollectionMapper(); + public static TDestination Map(TSource source, TDestination destination, ResolutionContext context, Func ifNullFunc) + where TSource : IEnumerable + where TDestination : class, ICollection { - if (context.IsSourceValueNull || context.DestinationValue == null) - return _collectionMapper.Map(context); - - var sourceElementType = TypeHelper.GetElementType(context.SourceType); - var destinationElementType = TypeHelper.GetElementType(context.DestinationType); - var equivilencyExpression = GetEquivilentExpression(new TypePair(context.SourceType, context.DestinationType)); + if (source == null || destination == null) + return ifNullFunc(); - var destEnumerable = context.DestinationValue as IEnumerable; - var sourceEnumerable = context.SourceValue as IEnumerable; + var equivilencyExpression = GetEquivilentExpression(new TypePair(typeof(TSource), typeof(TDestination))) as IEquivilentExpression; + var compareSourceToDestination = source.ToDictionary(s => s, s => destination.FirstOrDefault(d => equivilencyExpression.IsEquivlent(s, d))); - var destItems = destEnumerable.Cast().ToList(); - var sourceItems = sourceEnumerable.Cast().ToList(); - var compareSourceToDestination = sourceItems.ToDictionary(s => s, s => destItems.FirstOrDefault(d => equivilencyExpression.IsEquivlent(s, d))); - - var actualDestType = destEnumerable.GetType(); - var methodItem = _methodCache.GetOrAdd(actualDestType, t => - { - var addMethod = actualDestType.GetTypeInfo().GetDeclaredMethod("Add"); - var removeMethod = actualDestType.GetTypeInfo().GetDeclaredMethod("Remove"); - return new MethodCacheItem - { - Add = (e, o) => addMethod.Invoke(e, new[] {o}), - Remove = (e, o) => removeMethod.Invoke(e, new[] {o}) - }; - }); + foreach (var removedItem in destination.Except(compareSourceToDestination.Values).ToList()) + destination.Remove(removedItem); foreach (var keypair in compareSourceToDestination) { if (keypair.Value == null) - { - methodItem.Add(destEnumerable, context.Engine.Mapper.Map(keypair.Key, sourceElementType, destinationElementType)); - } + destination.Add(context.Mapper.Map(keypair.Key)); else - { - context.Engine.Mapper.Map(keypair.Key, keypair.Value, sourceElementType, destinationElementType); - } - } - - foreach (var removedItem in destItems.Except(compareSourceToDestination.Values)) - { - methodItem.Remove(destEnumerable, removedItem); + context.Mapper.Map(keypair.Key, keypair.Value); } - return destEnumerable; + return destination; } + private static readonly MethodInfo MapMethodInfo = typeof(EquivlentExpressionAddRemoveCollectionMapper).GetRuntimeMethods().First(_ => _.IsStatic); + + public bool IsMatch(TypePair typePair) { return typePair.SourceType.IsEnumerableType() @@ -68,15 +45,19 @@ public bool IsMatch(TypePair typePair) && GetEquivilentExpression(typePair) != null; } - private static IEquivilentExpression GetEquivilentExpression(TypePair typePair) + public Expression MapExpression(TypeMapRegistry typeMapRegistry, IConfigurationProvider configurationProvider, + PropertyMap propertyMap, Expression sourceExpression, Expression destExpression, Expression contextExpression) { - return EquivilentExpressions.GetEquivilentExpression(TypeHelper.GetElementType(typePair.SourceType), TypeHelper.GetElementType(typePair.DestinationType)); + var collectionExpression = CollectionMapper.MapExpression(typeMapRegistry, configurationProvider, propertyMap, sourceExpression, destExpression, contextExpression); + var collectionFunc = Expression.Lambda(collectionExpression).Compile(); + return Expression.Call(null, + MapMethodInfo.MakeGenericMethod(sourceExpression.Type, TypeHelper.GetElementType(sourceExpression.Type), destExpression.Type, TypeHelper.GetElementType(destExpression.Type)), + sourceExpression, destExpression, contextExpression, Expression.Constant(collectionFunc)); } - private class MethodCacheItem + private static IEquivilentExpression GetEquivilentExpression(TypePair typePair) { - public Action Add { get; set; } - public Action Remove { get; set; } + return EquivilentExpressions.GetEquivilentExpression(TypeHelper.GetElementType(typePair.SourceType), TypeHelper.GetElementType(typePair.DestinationType)); } } } diff --git a/src/AutoMapper.Collection/Mappers/ObjectToEquivalencyExpressionByEquivalencyExistingMapper.cs b/src/AutoMapper.Collection/Mappers/ObjectToEquivalencyExpressionByEquivalencyExistingMapper.cs index 8cdf3a8..75f5991 100644 --- a/src/AutoMapper.Collection/Mappers/ObjectToEquivalencyExpressionByEquivalencyExistingMapper.cs +++ b/src/AutoMapper.Collection/Mappers/ObjectToEquivalencyExpressionByEquivalencyExistingMapper.cs @@ -1,23 +1,35 @@ +using System; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; using AutoMapper.EquivilencyExpression; namespace AutoMapper.Mappers { public class ObjectToEquivalencyExpressionByEquivalencyExistingMapper : IObjectMapper { - public object Map(ResolutionContext context) + public static Expression> Map(TSource source) { - var destExpressArgType = context.DestinationType.GetSinglePredicateExpressionArgumentType(); - var toSourceExpression = EquivilentExpressions.GetEquivilentExpression(context.SourceType, destExpressArgType) as IToSingleSourceEquivalentExpression; - return toSourceExpression.ToSingleSourceExpression(context.SourceValue); + var toSourceExpression = EquivilentExpressions.GetEquivilentExpression(typeof(TSource),typeof(TDestination)) as IEquivilentExpression; + return toSourceExpression.ToSingleSourceExpression(source); } + private static readonly MethodInfo MapMethodInfo = typeof(ObjectToEquivalencyExpressionByEquivalencyExistingMapper).GetRuntimeMethods().First(_ => _.IsStatic); + public bool IsMatch(TypePair typePair) { var destExpressArgType = typePair.DestinationType.GetSinglePredicateExpressionArgumentType(); if (destExpressArgType == null) return false; var expression = EquivilentExpressions.GetEquivilentExpression(typePair.SourceType, destExpressArgType); - return expression is IToSingleSourceEquivalentExpression; + return expression != null; + } + + public Expression MapExpression(TypeMapRegistry typeMapRegistry, IConfigurationProvider configurationProvider, + PropertyMap propertyMap, Expression sourceExpression, Expression destExpression, Expression contextExpression) + { + var destExpressArgType = destExpression.Type.GetSinglePredicateExpressionArgumentType(); + return Expression.Call(null, MapMethodInfo.MakeGenericMethod(sourceExpression.Type, destExpressArgType), sourceExpression); } } } \ No newline at end of file diff --git a/src/AutoMapper.Collection/ReflectionHelper.cs b/src/AutoMapper.Collection/ReflectionHelper.cs deleted file mode 100644 index a3be5b4..0000000 --- a/src/AutoMapper.Collection/ReflectionHelper.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Reflection; -using AutoMapper.Internal; - -namespace AutoMapper.EquivilencyExpression -{ - internal static class ReflectionHelper - { - public static IMemberGetter ToMemberGetter(this MemberInfo accessorCandidate) - { - if (accessorCandidate == null) - return null; - - if (accessorCandidate is PropertyInfo) - return new PropertyGetter((PropertyInfo)accessorCandidate); - - if (accessorCandidate is FieldInfo) - return new FieldGetter((FieldInfo)accessorCandidate); - - if (accessorCandidate is MethodInfo) - return new MethodGetter((MethodInfo)accessorCandidate); - - return null; - } - } -} \ No newline at end of file diff --git a/src/AutoMapper.Collection/project.json b/src/AutoMapper.Collection/project.json index 0bb7e3f..d89e009 100644 --- a/src/AutoMapper.Collection/project.json +++ b/src/AutoMapper.Collection/project.json @@ -10,21 +10,27 @@ }, "dependencies": { - "AutoMapper" : "4.2.1" + "AutoMapper": "5.0.0" }, "frameworks": { "net45": { + "dependencies": { + } }, "netstandard1.1": { "dependencies": { - "NETStandard.Library": "1.5.0-rc2-24027", - "System.Linq.Expressions": "4.0.11-rc2-24027" + "System.Threading": "4.0.11" }, "imports": [ "portable-net45+win8+dnxcore50", "portable-net45+win8" ] + }, + "netstandard1.3": { + "dependencies": { + "System.Threading": "4.0.11" + } } } } \ No newline at end of file