MiddleMapper.cs 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556
  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Linq.Expressions;
  6. using System.Reflection;
  7. // ReSharper disable StaticMemberInGenericType
  8. namespace VCommon.VAutoMapper.Internals
  9. {
  10. internal static class MiddleMapper<TDto>
  11. {
  12. private static readonly Dictionary<Type, Type> MiddleClasses = new Dictionary<Type, Type>();
  13. private static readonly Dictionary<Type, Func<object, object>> MiddleProjection = new Dictionary<Type, Func<object, object>>();
  14. private static readonly Type DtoType = typeof(TDto);
  15. public static TDto[] ProjectionToArray<TEntity>(IQueryable<TEntity> queryable)
  16. {
  17. Func<object, object> mp;
  18. lock (MiddleClasses)
  19. {
  20. lock (MiddleProjection)
  21. {
  22. var entityType = typeof(TEntity);
  23. if (false == MiddleClasses.TryGetValue(entityType, out var mt))
  24. mt = MiddleClasses[entityType] = MiddleClass.Create(entityType, DtoType);
  25. if (false == MiddleProjection.TryGetValue(entityType, out mp))
  26. mp = MiddleProjection[entityType] = CreateMiddleProjection(entityType, mt);
  27. }
  28. }
  29. var arr = mp(queryable);
  30. var result = arr.MapTo<TDto[]>();
  31. return result;
  32. }
  33. private static Func<object, object> CreateMiddleProjection(Type entityType, Type mt)
  34. {
  35. // (object q) => ( (Queryable<[EntityType]>)q ).Projection<[mt]>().ToArray();
  36. var projectionArray = (typeof(VAutoMapper).GetMethod(nameof(VAutoMapper.ProjectionToArray), BindingFlags.Static | BindingFlags.Public) ?? throw new VAutoMapperException($"Missing method [{typeof(VAutoMapper).FullName}.{nameof(VAutoMapper.ProjectionToArray)}<>]?"))
  37. .MakeGenericMethod(entityType, mt);
  38. var p = Expression.Parameter(typeof(object));
  39. var unbox = Expression.Convert(p, typeof(IQueryable<>).MakeGenericType(entityType));
  40. var invoke = Expression.Call(null, projectionArray, unbox);
  41. var exp = Expression.Lambda<Func<object, object>>(invoke, p);
  42. return exp.Compile();
  43. }
  44. }
  45. }