using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; // ReSharper disable StaticMemberInGenericType namespace VCommon.VAutoMapper.Internals { internal static class MiddleMapper { private static readonly Dictionary MiddleClasses = new Dictionary(); private static readonly Dictionary> MiddleProjection = new Dictionary>(); private static readonly Type DtoType = typeof(TDto); public static TDto[] ProjectionToArray(IQueryable queryable) { Func mp; lock (MiddleClasses) { lock (MiddleProjection) { var entityType = typeof(TEntity); if (false == MiddleClasses.TryGetValue(entityType, out var mt)) mt = MiddleClasses[entityType] = MiddleClass.Create(entityType, DtoType); if (false == MiddleProjection.TryGetValue(entityType, out mp)) mp = MiddleProjection[entityType] = CreateMiddleProjection(entityType, mt); } } var arr = mp(queryable); var result = arr.MapTo(); return result; } private static Func CreateMiddleProjection(Type entityType, Type mt) { // (object q) => ( (Queryable<[EntityType]>)q ).Projection<[mt]>().ToArray(); var projectionArray = (typeof(VAutoMapper).GetMethod(nameof(VAutoMapper.ProjectionToArray), BindingFlags.Static | BindingFlags.Public) ?? throw new VAutoMapperException($"Missing method [{typeof(VAutoMapper).FullName}.{nameof(VAutoMapper.ProjectionToArray)}<>]?")) .MakeGenericMethod(entityType, mt); var p = Expression.Parameter(typeof(object)); var unbox = Expression.Convert(p, typeof(IQueryable<>).MakeGenericType(entityType)); var invoke = Expression.Call(null, projectionArray, unbox); var exp = Expression.Lambda>(invoke, p); return exp.Compile(); } } }