ExpressionCache.cs 3.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  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. using System.Text;
  8. using System.Threading.Tasks;
  9. namespace EasyDevCore.Common.Caches
  10. {
  11. /// <summary>
  12. ///
  13. /// </summary>
  14. public static class ExpressionCache
  15. {
  16. /// <summary>
  17. ///
  18. /// </summary>
  19. /// <param name="dictionary">The dictionary.</param>
  20. /// <param name="o">The o.</param>
  21. public delegate void AppendToDictionary(IDictionary<string, object> dictionary, object o);
  22. /// <summary>
  23. /// The s type cache
  24. /// </summary>
  25. static readonly ConcurrentDictionary<Type, AppendToDictionary> s_typeCache = new ConcurrentDictionary<Type, AppendToDictionary>();
  26. /// <summary>
  27. /// The dictionary indexer property
  28. /// </summary>
  29. private static readonly PropertyInfo _dictionaryIndexerProperty = GetDictionaryIndexer();
  30. /// <summary>
  31. /// Gets the or create append to dictionary method.
  32. /// </summary>
  33. /// <param name="type">The type.</param>
  34. /// <returns></returns>
  35. public static AppendToDictionary GetOrCreateAppendToDictionaryMethod(Type type) => s_typeCache.GetOrAdd(type, t => CreateAppendToDictionaryMethod(t));
  36. /// <summary>
  37. /// Creates the append to dictionary method.
  38. /// </summary>
  39. /// <param name="type">The type.</param>
  40. /// <returns></returns>
  41. private static AppendToDictionary CreateAppendToDictionaryMethod(Type type)
  42. {
  43. var dictionaryParameter = Expression.Parameter(typeof(IDictionary<string, object>), "dictionary");
  44. var objectParameter = Expression.Parameter(typeof(object), "o");
  45. var castedParameter = Expression.Convert(objectParameter, type); // cast o to the actual type
  46. // Create setter for each properties
  47. // dictionary["PropertyName"] = o.PropertyName;
  48. var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy);
  49. var setters =
  50. from prop in properties
  51. where prop.CanRead
  52. let indexerExpression = Expression.Property(dictionaryParameter, _dictionaryIndexerProperty, Expression.Constant(prop.Name))
  53. let getExpression = Expression.Property(castedParameter, prop.GetMethod)
  54. select Expression.Assign(indexerExpression, getExpression);
  55. var body = new List<Expression>(properties.Length + 1);
  56. body.Add(castedParameter);
  57. body.AddRange(setters);
  58. var lambdaExpression = Expression.Lambda<AppendToDictionary>(Expression.Block(body), dictionaryParameter, objectParameter);
  59. return lambdaExpression.Compile();
  60. }
  61. // Get the PropertyInfo for IDictionary<string, object>.this[string key]
  62. /// <summary>
  63. /// Gets the dictionary indexer.
  64. /// </summary>
  65. /// <returns></returns>
  66. private static PropertyInfo GetDictionaryIndexer()
  67. {
  68. var indexers = from prop in typeof(IDictionary<string, object>).GetProperties(BindingFlags.Instance | BindingFlags.Public)
  69. let indexParameters = prop.GetIndexParameters()
  70. where indexParameters.Length == 1 && typeof(string).IsAssignableFrom(indexParameters[0].ParameterType)
  71. select prop;
  72. return indexers.Single();
  73. }
  74. }
  75. }