11#if ! NET35 && ! UAP10_0 && ! NETSTANDARD1_3
22using System . Collections ;
3+ using System . Collections . Concurrent ;
34using System . Collections . Generic ;
45using System . Dynamic ;
56using System . Linq . Expressions ;
@@ -15,7 +16,15 @@ internal class DynamicGetMemberBinder : GetMemberBinder
1516{
1617 private static readonly MethodInfo DynamicGetMemberMethod = typeof ( DynamicGetMemberBinder ) . GetMethod ( nameof ( GetDynamicMember ) ) ! ;
1718
18- public DynamicGetMemberBinder ( string name , ParsingConfig ? config ) : base ( name , config ? . IsCaseSensitive != true )
19+ // The _metaObjectCache uses a Tuple<Type, string, bool> as the key to cache DynamicMetaObject instances.
20+ // The key components are:
21+ // - Type: The LimitType of the target object, ensuring type-specific caching.
22+ // - string: The member name being accessed.
23+ // - bool: The IgnoreCase flag, indicating whether the member name comparison is case-insensitive.
24+ // This strategy ensures that the cache correctly handles different types, member names, and case-sensitivity settings.
25+ private readonly ConcurrentDictionary < Tuple < Type , string , bool > , DynamicMetaObject > _metaObjectCache = new ( ) ;
26+
27+ internal DynamicGetMemberBinder ( string name , ParsingConfig ? config ) : base ( name , config ? . IsCaseSensitive != true )
1928 {
2029 }
2130
@@ -28,8 +37,20 @@ public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, Dy
2837 Expression . Constant ( IgnoreCase ) ) ;
2938
3039 // Fix #907 and #912: "The result of the dynamic binding produced by the object with type '<>f__AnonymousType1`4' for the binder 'System.Linq.Dynamic.Core.DynamicGetMemberBinder' needs at least one restriction.".
31- var restrictions = BindingRestrictions . GetInstanceRestriction ( target . Expression , target . Value ) ;
32- return new DynamicMetaObject ( methodCallExpression , restrictions , target . Value ! ) ;
40+ // Fix #921: "Slow Performance"
41+ // Only add TypeRestriction if it's a Dynamic type and make sure to cache the DynamicMetaObject.
42+ if ( target . Value is IDynamicMetaObjectProvider )
43+ {
44+ var key = new Tuple < Type , string , bool > ( target . LimitType , Name , IgnoreCase ) ;
45+
46+ return _metaObjectCache . GetOrAdd ( key , _ =>
47+ {
48+ var restrictions = BindingRestrictions . GetTypeRestriction ( target . Expression , target . LimitType ) ;
49+ return new DynamicMetaObject ( methodCallExpression , restrictions , target . Value ) ;
50+ } ) ;
51+ }
52+
53+ return DynamicMetaObject . Create ( target . Value ! , methodCallExpression ) ;
3354 }
3455
3556 public static object ? GetDynamicMember ( object value , string name , bool ignoreCase )
0 commit comments