diff --git a/src/RulesEngine/CustomTypeProvider.cs b/src/RulesEngine/CustomTypeProvider.cs index 58bbcbe7..81ff9ce7 100644 --- a/src/RulesEngine/CustomTypeProvider.cs +++ b/src/RulesEngine/CustomTypeProvider.cs @@ -42,11 +42,19 @@ public CustomTypeProvider(Type[] types) : base(ParsingConfig.Default) _types.Add(typeof(IEnumerable)); } + private HashSet _mergedTypes; + public override HashSet GetCustomTypes() { - var all = new HashSet(base.GetCustomTypes()); - all.UnionWith(_types); - return all; + // base.GetCustomTypes() scans every assembly in the AppDomain for [DynamicLinqType]. + // The provider's type set is fixed after construction, so merge exactly once. + if (_mergedTypes == null) + { + var all = new HashSet(base.GetCustomTypes()); + all.UnionWith(_types); + _mergedTypes = all; + } + return _mergedTypes; } } } diff --git a/src/RulesEngine/ExpressionBuilders/RuleExpressionParser.cs b/src/RulesEngine/ExpressionBuilders/RuleExpressionParser.cs index f4138fc6..1c25c167 100644 --- a/src/RulesEngine/ExpressionBuilders/RuleExpressionParser.cs +++ b/src/RulesEngine/ExpressionBuilders/RuleExpressionParser.cs @@ -83,12 +83,33 @@ private void PopulateMethodInfo() _methodInfo.Add("dict_add", dict_add); } + private ParsingConfig _cachedParsingConfig; + private Type[] _cachedParsingConfigCustomTypes; + + private ParsingConfig GetParsingConfig() + { + // Building a CustomTypeProvider is expensive: System.Linq.Dynamic.Core's + // DefaultDynamicLinqCustomTypeProvider scans all AppDomain assemblies for + // [DynamicLinqType] and only caches per provider instance. Reuse one config + // until ReSettings.CustomTypes is swapped (AutoRegisterInputType does this + // on workflow registration). + var customTypes = _reSettings.CustomTypes; + var config = _cachedParsingConfig; + if (config == null || !ReferenceEquals(_cachedParsingConfigCustomTypes, customTypes)) + { + config = new ParsingConfig { + CustomTypeProvider = new CustomTypeProvider(customTypes), + IsCaseSensitive = _reSettings.IsExpressionCaseSensitive + }; + _cachedParsingConfigCustomTypes = customTypes; + _cachedParsingConfig = config; + } + return config; + } + public Expression Parse(string expression, ParameterExpression[] parameters, Type returnType) { - var config = new ParsingConfig { - CustomTypeProvider = new CustomTypeProvider(_reSettings.CustomTypes), - IsCaseSensitive = _reSettings.IsExpressionCaseSensitive - }; + var config = GetParsingConfig(); // Instead of immediately returning default values, allow for expression parsing to handle dynamic evaluation. try