Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion global.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"sdk": {
"version": "1.0.0-preview1-002702"
}
},
"projects": []
}
16 changes: 11 additions & 5 deletions src/AutoMapper.Collection-Signed/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
}
}
}
}
3 changes: 1 addition & 2 deletions src/AutoMapper.Collection.EntityFramework/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,10 @@ public static class Extensions
/// <typeparam name="TSource">Source table type to be updated</typeparam>
/// <param name="source">DbSet to be updated</param>
/// <returns>Persistance object to Update or Remove data</returns>
[Obsolete("Use version that passes instance of IMapper")]
public static IPersistance Persist<TSource>(this DbSet<TSource> source)
where TSource : class
{
return new Persistance<TSource>(source, null);
return new Persistance<TSource>(source, Mapper.Instance);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,16 @@ public class GenerateEntityFrameworkPrimaryKeyEquivilentExpressions<TDatabaseCon
/// </summary>
/// <param name="mapper">IMapper used to find TypeMap between classes</param>
public GenerateEntityFrameworkPrimaryKeyEquivilentExpressions(IMapper mapper)
: base(new GenerateEntityFrameworkPrimaryKeyPropertyMaps<TDatabaseContext>(mapper))
: base(new GenerateEntityFrameworkPrimaryKeyPropertyMaps<TDatabaseContext>(mapper.ConfigurationProvider))
{
}

/// <summary>
/// Generate EquivilencyExpressions based on EnityFramework's primary key
/// Uses static API's Mapper for finding TypeMap between classes
/// </summary>
[Obsolete("Use version that passes instance of IMapper")]
public GenerateEntityFrameworkPrimaryKeyEquivilentExpressions()
: base(new GenerateEntityFrameworkPrimaryKeyPropertyMaps<TDatabaseContext>(null))
: base(new GenerateEntityFrameworkPrimaryKeyPropertyMaps<TDatabaseContext>(Mapper.Configuration))
{
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,19 @@ namespace AutoMapper.EntityFramework
public class GenerateEntityFrameworkPrimaryKeyPropertyMaps<TDatabaseContext> : 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<PropertyMap> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,16 @@ public class GetLinqToSQLPrimaryKeyExpression : GenerateEquivilentExpressionsBas
/// </summary>
/// <param name="mapper">IMapper used to find TypeMap between classes</param>
public GetLinqToSQLPrimaryKeyExpression(IMapper mapper)
: base(new GetLinqToSQLPrimaryKeyProperties(mapper))
: base(new GetLinqToSQLPrimaryKeyProperties(mapper.ConfigurationProvider))
{
}

/// <summary>
/// Generate EquivilencyExpressions based on LinqToSQL's primary key
/// Uses static API's Mapper for finding TypeMap between classes
/// </summary>
[Obsolete("Use version that passes instance of IMapper")]
public GetLinqToSQLPrimaryKeyExpression()
: base(new GetLinqToSQLPrimaryKeyProperties(null))
: base(new GetLinqToSQLPrimaryKeyProperties(Mapper.Configuration))
{
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<PropertyMap> 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();
Expand Down
3 changes: 1 addition & 2 deletions src/AutoMapper.Collection.LinqToSQL/PersistanceExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,10 @@ public static class PersistanceExtensions
/// <typeparam name="TSource">Source table type to be updated</typeparam>
/// <param name="source">Table to be updated</param>
/// <returns>Persistance object to Update or Remove data</returns>
[Obsolete("Use version that passes instance of IMapper")]
public static IPersistance Persist<TSource>(this Table<TSource> source)
where TSource : class
{
return new Persistance<TSource>(source, null);
return new Persistance<TSource>(source, Mapper.Instance);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using AutoMapper.Execution;

namespace AutoMapper.EquivilencyExpression
{
Expand All @@ -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<IMemberGetter>();
var memberGetters = matchPM.SourceMembers;

var memberExpression = Expression.Property(Visit(node.Expression), memberGetters.First().MemberInfo as PropertyInfo);

foreach (var memberGetter in memberGetters.Skip(1))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Linq.Expressions;
using System.Reflection;
using AutoMapper.Execution;

namespace AutoMapper.EquivilencyExpression
{
Expand All @@ -12,61 +13,33 @@ static EquivilentExpression()
{
BadValue = new EquivilentExpression();
}

public bool IsEquivlent(object source, object destination)
{
throw new NotImplementedException();
}
}

internal class EquivilentExpression<TSource,TDestination> : IEquivilentExpression , IToSingleSourceEquivalentExpression
internal class EquivilentExpression<TSource,TDestination> : IEquivilentExpression<TSource, TDestination>
where TSource : class
where TDestination : class
{
private readonly Expression<Func<TSource, TDestination, bool>> _equivilentExpression;
private readonly Func<TSource, TDestination, bool> _equivilentFunc;

public EquivilentExpression(Expression<Func<TSource,TDestination,bool>> 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<Func<TDestination, bool>> 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<TSource>(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<Func<TDestination, bool>>(expression.Body, _equivilentExpression.Parameters[1]);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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<TSource, TDestination> : IEquivilentExpression
{
Expression ToSingleSourceExpression(object destination);
bool IsEquivlent(TSource source, TDestination destination);
Expression<Func<TDestination, bool>> ToSingleSourceExpression(TSource destination);
}
}
Original file line number Diff line number Diff line change
@@ -1,82 +1,63 @@
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<Type, MethodCacheItem> _methodCache = new ConcurrentDictionary<Type, MethodCacheItem>();
private readonly CollectionMapper _collectionMapper = new CollectionMapper();

public object Map(ResolutionContext context)
private readonly CollectionMapper CollectionMapper = new CollectionMapper();
public static TDestination Map<TSource, TSourceItem, TDestination, TDestinationItem>(TSource source, TDestination destination, ResolutionContext context, Func<TDestination> ifNullFunc)
where TSource : IEnumerable<TSourceItem>
where TDestination : class, ICollection<TDestinationItem>
{
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<TSourceItem,TDestinationItem>;
var compareSourceToDestination = source.ToDictionary(s => s, s => destination.FirstOrDefault(d => equivilencyExpression.IsEquivlent(s, d)));

var destItems = destEnumerable.Cast<object>().ToList();
var sourceItems = sourceEnumerable.Cast<object>().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<TDestinationItem>(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()
&& typePair.DestinationType.IsCollectionType()
&& 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<IEnumerable, object> Add { get; set; }
public Action<IEnumerable, object> Remove { get; set; }
return EquivilentExpressions.GetEquivilentExpression(TypeHelper.GetElementType(typePair.SourceType), TypeHelper.GetElementType(typePair.DestinationType));
}
}
}
Loading