Created
August 1, 2014 06:39
-
-
Save forcewake/280eb30a9de2be3e5b79 to your computer and use it in GitHub Desktop.
Light mapper instead of AutoMapper
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static class QueryableExtensions | |
{ | |
public static ProjectionExpression<TSource> Project<TSource>(this IQueryable<TSource> source) | |
{ | |
return new ProjectionExpression<TSource>(source); | |
} | |
} | |
public class ProjectionExpression<TSource> | |
{ | |
private static readonly Dictionary<string, Expression> ExpressionCache = new Dictionary<string, Expression>(); | |
private readonly IQueryable<TSource> _source; | |
public ProjectionExpression(IQueryable<TSource> source) | |
{ | |
_source = source; | |
} | |
public IQueryable<TDest> To<TDest>() | |
{ | |
var queryExpression = GetCachedExpression<TDest>() ?? BuildExpression<TDest>(); | |
return _source.Select(queryExpression); | |
} | |
private static Expression<Func<TSource, TDest>> GetCachedExpression<TDest>() | |
{ | |
var key = GetCacheKey<TDest>(); | |
return ExpressionCache.ContainsKey(key) ? ExpressionCache[key] as Expression<Func<TSource, TDest>> : null; | |
} | |
private static Expression<Func<TSource, TDest>> BuildExpression<TDest>() | |
{ | |
var sourceProperties = typeof(TSource).GetProperties(); | |
var destinationProperties = typeof(TDest).GetProperties().Where(dest => dest.CanWrite); | |
var parameterExpression = Expression.Parameter(typeof(TSource), "src"); | |
var bindings = destinationProperties | |
.Select(destinationProperty => BuildBinding(parameterExpression, destinationProperty, sourceProperties)) | |
.Where(binding => binding != null); | |
var expression = Expression.Lambda<Func<TSource, TDest>>(Expression.MemberInit(Expression.New(typeof(TDest)), bindings), parameterExpression); | |
var key = GetCacheKey<TDest>(); | |
ExpressionCache.Add(key, expression); | |
return expression; | |
} | |
private static MemberAssignment BuildBinding(Expression parameterExpression, MemberInfo destinationProperty, IEnumerable<PropertyInfo> sourceProperties) | |
{ | |
var sourceProperty = sourceProperties.FirstOrDefault(src => src.Name == destinationProperty.Name); | |
if (sourceProperty != null) | |
{ | |
return Expression.Bind(destinationProperty, Expression.Property(parameterExpression, sourceProperty)); | |
} | |
var propertyNames = SplitCamelCase(destinationProperty.Name); | |
if (propertyNames.Length == 2) | |
{ | |
sourceProperty = sourceProperties.FirstOrDefault(src => src.Name == propertyNames[0]); | |
if (sourceProperty != null) | |
{ | |
var sourceChildProperty = sourceProperty.PropertyType.GetProperties().FirstOrDefault(src => src.Name == propertyNames[1]); | |
if (sourceChildProperty != null) | |
{ | |
return Expression.Bind(destinationProperty, Expression.Property(Expression.Property(parameterExpression, sourceProperty), sourceChildProperty)); | |
} | |
} | |
} | |
return null; | |
} | |
private static string GetCacheKey<TDest>() | |
{ | |
return string.Concat(typeof(TSource).FullName, typeof(TDest).FullName); | |
} | |
private static string[] SplitCamelCase(string input) | |
{ | |
return Regex.Replace(input, "([A-Z])", " $1", RegexOptions.Compiled).Trim().Split(' '); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment