Skip to content

Commit

Permalink
Thread safety issue #40 & Support for mapping ObservableCollection #39
Browse files Browse the repository at this point in the history
…and other collection types. Interim commit for Projection IQueryable
  • Loading branch information
anisimovyuriy committed Oct 1, 2015
1 parent 6c030f6 commit d2d6275
Show file tree
Hide file tree
Showing 17 changed files with 238 additions and 83 deletions.
2 changes: 1 addition & 1 deletion ExpressMapper NET40/DestinationMappingService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ public override void PrecompileCollection<T, TN>()
var lambda = Expression.Lambda<Func<T, TN, TN>>(block, sourceVariable, destVariable);
var compiledFunc = lambda.Compile();

CollectionMappers.Add(cacheKey, compiledFunc);
CollectionMappers[cacheKey] = compiledFunc;
}

#endregion
Expand Down
1 change: 1 addition & 0 deletions ExpressMapper NET40/ExpressMapper NET40.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
<Compile Include="IMappingService.cs" />
<Compile Include="MappingServiceBase.cs" />
<Compile Include="PreciseSubstituteParameterVisitor.cs" />
<Compile Include="ProjectionAccessMemberVisitor.cs" />
<Compile Include="SourceMappingService.cs" />
<Compile Include="SourceTypeMapper.cs" />
<Compile Include="StaticExpressions.cs" />
Expand Down
32 changes: 6 additions & 26 deletions ExpressMapper NET40/Mapper.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
using System;
using System.Linq;

namespace ExpressMapper
{
public static class Mapper
{
private static readonly object _lock = new object();
private static IMappingServiceProvider _instance;

// todo: via Internal DependencyResolver
Expand All @@ -16,18 +14,12 @@ public static IMappingServiceProvider Instance

public static void Compile()
{
lock (_lock)
{
Instance.Compile();
}
Instance.Compile();
}

public static void PrecompileCollection<T,TN>()
{
lock (_lock)
{
Instance.PrecompileCollection<T, TN>();
}
Instance.PrecompileCollection<T, TN>();
}

public static TN Map<T, TN>(T src)
Expand Down Expand Up @@ -62,35 +54,23 @@ public static object Map(object src, object dest, Type srcType, Type dstType)

public static IMemberConfiguration<T, TN> Register<T, TN>()
{
lock (_lock)
{
return Instance.Register<T, TN>();
}
return Instance.Register<T, TN>();
}

public static void RegisterCustom<T, TN, TMapper>()
where TMapper : ICustomTypeMapper<T, TN>
{
lock (_lock)
{
Instance.RegisterCustom<T, TN, TMapper>();
}
Instance.RegisterCustom<T, TN, TMapper>();
}

public static void RegisterCustom<T, TN>(Func<T, TN> mapFunc)
{
lock (_lock)
{
Instance.RegisterCustom<T, TN>(mapFunc);
}
Instance.RegisterCustom<T, TN>(mapFunc);
}

public static void Reset()
{
lock (_lock)
{
Instance.Reset();
}
Instance.Reset();
}
}
}
17 changes: 15 additions & 2 deletions ExpressMapper NET40/MappingServiceBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -403,9 +403,22 @@ internal static Expression ConvertCollection(Type destPropType,
{
return Expression.Call(typeof(Queryable), "AsQueryable", new[] { destType }, destColl);
}
var collectionType = typeof(Collection<>).MakeGenericType(destType);

return destPropType == collectionType ? Expression.New(collectionType.GetConstructor(new Type[] { destList }), destColl) : destColl;
if (destPropType.IsInterface && destColl.Type.IsSubclassOf(destPropType))
{
return destColl;
}

if (destPropType.IsClass)
{

}

return destPropType.IsClass ? Expression.New(destPropType.GetConstructor(new Type[] { destList }), destColl) : destColl;

//var collectionType = typeof(Collection<>).MakeGenericType(destType);

//return destPropType == collectionType ? Expression.New(collectionType.GetConstructor(new Type[] { destList }), destColl) : destColl;
}
}
}
122 changes: 75 additions & 47 deletions ExpressMapper NET40/MappingServiceProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ namespace ExpressMapper
{
public sealed class MappingServiceProvider : IMappingServiceProvider
{
private static readonly object _lock = new object();

public Dictionary<int, Func<ICustomTypeMapper>> CustomMappers { get; set; }
private readonly Dictionary<int, Func<object, object, object>> _customTypeMapperCache = new Dictionary<int, Func<object, object, object>>();
private readonly List<int> _nonGenericCollectionMappingCache = new List<int>();
Expand Down Expand Up @@ -54,89 +56,115 @@ public IQueryable<TN> Project<T, TN>(IQueryable<T> source)

public IMemberConfiguration<T, TN> Register<T, TN>()
{
var src = typeof(T);
var dest = typeof(TN);
var cacheKey = CalculateCacheKey(src, dest);
lock (_lock)
{
var src = typeof (T);
var dest = typeof (TN);
var cacheKey = CalculateCacheKey(src, dest);


if (SourceService.TypeMappers.ContainsKey(cacheKey) && DestinationService.TypeMappers.ContainsKey(cacheKey))
{
throw new InvalidOperationException(string.Format("Mapping from {0} to {1} is already registered", src.FullName, dest.FullName));
}
if (SourceService.TypeMappers.ContainsKey(cacheKey) &&
DestinationService.TypeMappers.ContainsKey(cacheKey))
{
throw new InvalidOperationException(string.Format("Mapping from {0} to {1} is already registered",
src.FullName, dest.FullName));
}


var sourceClassMapper = new SourceTypeMapper<T, TN>(SourceService);
var destinationClassMapper = new DestinationTypeMapper<T, TN>(DestinationService);
var sourceClassMapper = new SourceTypeMapper<T, TN>(SourceService);
var destinationClassMapper = new DestinationTypeMapper<T, TN>(DestinationService);

SourceService.TypeMappers[cacheKey] = sourceClassMapper;
DestinationService.TypeMappers[cacheKey] = destinationClassMapper;
return new MemberConfiguration<T, TN>(new ITypeMapper<T, TN>[]{sourceClassMapper, destinationClassMapper});
SourceService.TypeMappers[cacheKey] = sourceClassMapper;
DestinationService.TypeMappers[cacheKey] = destinationClassMapper;
return
new MemberConfiguration<T, TN>(new ITypeMapper<T, TN>[] {sourceClassMapper, destinationClassMapper});
}
}

public void Compile()
{
foreach (var mappingService in _mappingServices)
lock (_lock)
{
mappingService.Compile();
foreach (var mappingService in _mappingServices)
{
mappingService.Compile();
}
}
}

public void PrecompileCollection<T, TN>()
{
foreach (var mappingService in _mappingServices)
lock (_lock)
{
mappingService.PrecompileCollection<T, TN>();
foreach (var mappingService in _mappingServices)
{
mappingService.PrecompileCollection<T, TN>();
}
}
}

public void Reset()
{
foreach (var mappingService in _mappingServices)
lock (_lock)
{
mappingService.TypeMappers.Clear();
}
foreach (var mappingService in _mappingServices)
{
mappingService.TypeMappers.Clear();
}

CustomMappers.Clear();
CustomMappers.Clear();

foreach (var mappingService in _mappingServices)
{
mappingService.Reset();
foreach (var mappingService in _mappingServices)
{
mappingService.Reset();
}
}
}

public void RegisterCustom<T, TN>(Func<T, TN> mapFunc)
{
var src = typeof(T);
var dest = typeof(TN);
var cacheKey = CalculateCacheKey(src, dest);

if (CustomMappers.ContainsKey(cacheKey))
lock (_lock)
{
throw new InvalidOperationException(string.Format("Mapping from {0} to {1} is already registered", src.FullName, dest.FullName));
}
var src = typeof (T);
var dest = typeof (TN);
var cacheKey = CalculateCacheKey(src, dest);

var delegateMapperType = typeof(DelegateCustomTypeMapper<,>).MakeGenericType(src, dest);
var newExpression = Expression.New(delegateMapperType.GetConstructor(new Type[] { typeof(Func<,>).MakeGenericType(src, dest) }), Expression.Constant(mapFunc));
var newLambda = Expression.Lambda<Func<ICustomTypeMapper<T, TN>>>(newExpression);
var compile = newLambda.Compile();
CustomMappers.Add(cacheKey, compile);
if (CustomMappers.ContainsKey(cacheKey))
{
throw new InvalidOperationException(string.Format("Mapping from {0} to {1} is already registered",
src.FullName, dest.FullName));
}

var delegateMapperType = typeof (DelegateCustomTypeMapper<,>).MakeGenericType(src, dest);
var newExpression =
Expression.New(
delegateMapperType.GetConstructor(new Type[] {typeof (Func<,>).MakeGenericType(src, dest)}),
Expression.Constant(mapFunc));
var newLambda = Expression.Lambda<Func<ICustomTypeMapper<T, TN>>>(newExpression);
var compile = newLambda.Compile();
CustomMappers.Add(cacheKey, compile);
}
}

public void RegisterCustom<T, TN, TMapper>() where TMapper : ICustomTypeMapper<T, TN>
{
var src = typeof(T);
var dest = typeof(TN);
var cacheKey = CalculateCacheKey(src, dest);

if (CustomMappers.ContainsKey(cacheKey))
lock (_lock)
{
throw new InvalidOperationException(string.Format("Mapping from {0} to {1} is already registered", src.FullName, dest.FullName));
}
var src = typeof (T);
var dest = typeof (TN);
var cacheKey = CalculateCacheKey(src, dest);

var newExpression = Expression.New(typeof(TMapper));
var newLambda = Expression.Lambda<Func<ICustomTypeMapper<T, TN>>>(newExpression);
var compile = newLambda.Compile();
CustomMappers.Add(cacheKey, compile);
if (CustomMappers.ContainsKey(cacheKey))
{
throw new InvalidOperationException(string.Format("Mapping from {0} to {1} is already registered",
src.FullName, dest.FullName));
}

var newExpression = Expression.New(typeof (TMapper));
var newLambda = Expression.Lambda<Func<ICustomTypeMapper<T, TN>>>(newExpression);
var compile = newLambda.Compile();
CustomMappers[cacheKey] = compile;
}
}

public TN Map<T, TN>(T src, TN dest = default(TN))
Expand Down Expand Up @@ -314,7 +342,7 @@ private void CompileNonGenericCustomTypeMapper(Type srcType, Type dstType, ICust
srcAssigned, dstAssigned, assignExp, assignContextExp, sourceAssignedExp, destAssignedExp, /*destinationAssignedExp,*/ resultAssignExp, resultVarExp);

var lambda = Expression.Lambda<Func<object, object, object>>(blockExpression, sourceExpression, destinationExpression);
_customTypeMapperCache.Add(cacheKey, lambda.Compile());
_customTypeMapperCache[cacheKey] = lambda.Compile();
}

internal static Type GetCollectionElementType(Type type)
Expand Down
39 changes: 39 additions & 0 deletions ExpressMapper NET40/ProjectionAccessMemberVisitor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;

namespace ExpressMapper
{
public class ProjectionAccessMemberVisitor : ExpressionVisitor
{
private Expression _exp;
//private ParameterExpression _src;
private Type _type;
public ProjectionAccessMemberVisitor(Type type, Expression exp)
{
_exp = exp;
_type = type;
//_src = src;
}

//protected override Expression VisitParameter(ParameterExpression node)
//{
// if (node.Type == _type)
// {
// return _src;
// }
// return base.VisitParameter(node);
//}

protected override Expression VisitMember(MemberExpression node)
{
if (node.Expression.Type == _type)
{
return Expression.PropertyOrField(_exp, node.Member.Name);
}
return base.VisitMember(node);
}
}
}
2 changes: 1 addition & 1 deletion ExpressMapper NET40/SourceMappingService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public override void PrecompileCollection<T, TN>()
var blockExp = CompileCollectionInternal<T, TN>(sourceParameterExp);
var lambda = Expression.Lambda<Func<T, TN>>(blockExp, sourceParameterExp);
var compiledFunc = lambda.Compile();
CollectionMappers.Add(cacheKey, compiledFunc);
CollectionMappers[cacheKey] = compiledFunc;
}

public override bool DestinationSupport
Expand Down
31 changes: 27 additions & 4 deletions ExpressMapper NET40/SourceTypeMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace ExpressMapper
{
Expand Down Expand Up @@ -108,11 +109,33 @@ private void CreateQueryableProjection()
{
if (_bindingExpressions.ContainsKey(autoMember.Value.Name)) continue;

var source = autoMember.Key as PropertyInfo;
var destination = autoMember.Value as PropertyInfo;

var memberQueryableExpression =
MappingService.GetMemberQueryableExpression(autoMember.Key.DeclaringType,
autoMember.Value.DeclaringType);
var expression = memberQueryableExpression ??
Expression.PropertyOrField(SourceParameter, autoMember.Key.Name);
MappingService.GetMemberQueryableExpression(source.PropertyType,
destination.PropertyType);

var propertyOrField = Expression.PropertyOrField(SourceParameter, autoMember.Key.Name);

Expression expression;
if (memberQueryableExpression != null)
{
var lambdaExpression = memberQueryableExpression as LambdaExpression;
var projectionAccessMemberVisitor = new ProjectionAccessMemberVisitor(propertyOrField.Type, propertyOrField);
var clearanceExp = projectionAccessMemberVisitor.Visit(lambdaExpression.Body);
expression =
Expression.Condition(
Expression.Equal(propertyOrField, Expression.Constant(null, propertyOrField.Type)),
Expression.Constant(null, ((PropertyInfo) autoMember.Value).PropertyType), clearanceExp);
}
else
{
expression = propertyOrField;
}



_bindingExpressions.Add(autoMember.Value.Name,
Expression.Bind(autoMember.Value, expression));
}
Expand Down
Loading

0 comments on commit d2d6275

Please sign in to comment.