diff --git a/System.Linq.Dynamic.Core.sln b/System.Linq.Dynamic.Core.sln index 60d593a1..4506baf8 100644 --- a/System.Linq.Dynamic.Core.sln +++ b/System.Linq.Dynamic.Core.sln @@ -74,7 +74,12 @@ Project("{7CF6DF6D-3B04-46F8-A40B-537D21BCA0B4}") = "System.Linq.Dynamic.Core", {D3804228-91F4-4502-9595-39584E510002} = {D3804228-91F4-4502-9595-39584E510002} EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleAppPerformanceTest236", "src-console\ConsoleAppPerformanceTest236\ConsoleAppPerformanceTest236.csproj", "{E9C52E5E-28DC-4D45-B9AB-1B2CF2924A84}" +EndProject Global + GlobalSection(Performance) = preSolution + HasPerformanceSessions = true + EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Debug|ARM = Debug|ARM @@ -388,6 +393,22 @@ Global {591F9224-A8D6-49CF-8AF8-F9B383C1F9FF}.Release|x64.Build.0 = Release|Any CPU {591F9224-A8D6-49CF-8AF8-F9B383C1F9FF}.Release|x86.ActiveCfg = Release|Any CPU {591F9224-A8D6-49CF-8AF8-F9B383C1F9FF}.Release|x86.Build.0 = Release|Any CPU + {E9C52E5E-28DC-4D45-B9AB-1B2CF2924A84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E9C52E5E-28DC-4D45-B9AB-1B2CF2924A84}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E9C52E5E-28DC-4D45-B9AB-1B2CF2924A84}.Debug|ARM.ActiveCfg = Debug|Any CPU + {E9C52E5E-28DC-4D45-B9AB-1B2CF2924A84}.Debug|ARM.Build.0 = Debug|Any CPU + {E9C52E5E-28DC-4D45-B9AB-1B2CF2924A84}.Debug|x64.ActiveCfg = Debug|Any CPU + {E9C52E5E-28DC-4D45-B9AB-1B2CF2924A84}.Debug|x64.Build.0 = Debug|Any CPU + {E9C52E5E-28DC-4D45-B9AB-1B2CF2924A84}.Debug|x86.ActiveCfg = Debug|Any CPU + {E9C52E5E-28DC-4D45-B9AB-1B2CF2924A84}.Debug|x86.Build.0 = Debug|Any CPU + {E9C52E5E-28DC-4D45-B9AB-1B2CF2924A84}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E9C52E5E-28DC-4D45-B9AB-1B2CF2924A84}.Release|Any CPU.Build.0 = Release|Any CPU + {E9C52E5E-28DC-4D45-B9AB-1B2CF2924A84}.Release|ARM.ActiveCfg = Release|Any CPU + {E9C52E5E-28DC-4D45-B9AB-1B2CF2924A84}.Release|ARM.Build.0 = Release|Any CPU + {E9C52E5E-28DC-4D45-B9AB-1B2CF2924A84}.Release|x64.ActiveCfg = Release|Any CPU + {E9C52E5E-28DC-4D45-B9AB-1B2CF2924A84}.Release|x64.Build.0 = Release|Any CPU + {E9C52E5E-28DC-4D45-B9AB-1B2CF2924A84}.Release|x86.ActiveCfg = Release|Any CPU + {E9C52E5E-28DC-4D45-B9AB-1B2CF2924A84}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -412,6 +433,7 @@ Global {F1880F07-238F-4A3A-9E58-141350665E1F} = {7971CAEB-B9F2-416B-966D-2D697C4C1E62} {926D446C-8358-465A-AFAC-2F9078C22262} = {ECA5702B-5D32-4888-A34E-9461FC533F23} {591F9224-A8D6-49CF-8AF8-F9B383C1F9FF} = {1384C18E-DCF3-4A5B-9560-2BF5DD8C51CE} + {E9C52E5E-28DC-4D45-B9AB-1B2CF2924A84} = {7971CAEB-B9F2-416B-966D-2D697C4C1E62} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {94C56722-194E-4B8B-BC23-B3F754E89A20} diff --git a/src-console/ConsoleAppPerformanceTest236/ConsoleAppPerformanceTest236.csproj b/src-console/ConsoleAppPerformanceTest236/ConsoleAppPerformanceTest236.csproj new file mode 100644 index 00000000..d10b7fc2 --- /dev/null +++ b/src-console/ConsoleAppPerformanceTest236/ConsoleAppPerformanceTest236.csproj @@ -0,0 +1,16 @@ + + + + Exe + net472 + + + + + + + + + + + diff --git a/src-console/ConsoleAppPerformanceTest236/Program.cs b/src-console/ConsoleAppPerformanceTest236/Program.cs new file mode 100644 index 00000000..dfec60ed --- /dev/null +++ b/src-console/ConsoleAppPerformanceTest236/Program.cs @@ -0,0 +1,11 @@ +namespace ConsoleAppPerformanceTest236 +{ + class Program + { + static void Main(string[] args) + { + Test.DoIt(); + int x = 0; + } + } +} diff --git a/src-console/ConsoleAppPerformanceTest236/Report-net472-after.diagsession b/src-console/ConsoleAppPerformanceTest236/Report-net472-after.diagsession new file mode 100644 index 00000000..6b285cbb Binary files /dev/null and b/src-console/ConsoleAppPerformanceTest236/Report-net472-after.diagsession differ diff --git a/src-console/ConsoleAppPerformanceTest236/Test.cs b/src-console/ConsoleAppPerformanceTest236/Test.cs new file mode 100644 index 00000000..331a7159 --- /dev/null +++ b/src-console/ConsoleAppPerformanceTest236/Test.cs @@ -0,0 +1,24 @@ +using System; +using System.Linq; +using System.Linq.Dynamic.Core; + +namespace ConsoleAppPerformanceTest236 +{ + public static class Test + { + public static void DoIt() + { + var q = User.GenerateSampleModels(10000).AsQueryable(); + + int count = 100; + for (int i = 0; i <= count; i++) + { + if (i % 10 == 0) + { + Console.WriteLine(i); + } + var result = q.OrderBy("FullName ASC, City DESC").FirstOrDefault(); + } + } + } +} diff --git a/src-console/ConsoleAppPerformanceTest236/User.cs b/src-console/ConsoleAppPerformanceTest236/User.cs new file mode 100644 index 00000000..4123f3c3 --- /dev/null +++ b/src-console/ConsoleAppPerformanceTest236/User.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using RandomDataGenerator.FieldOptions; +using RandomDataGenerator.Randomizers; + +namespace ConsoleAppPerformanceTest236 +{ + public class User + { + public Guid Id { get; set; } + + public string FullName { get; set; } + + public string City { get; set; } + + public int? NullableInt { get; set; } + + public int Income { get; set; } + + public static IList GenerateSampleModels(int total, bool allowNullableProfiles = false) + { + var list = new List(); + + var randomizerFullName = RandomizerFactory.GetRandomizer(new FieldOptionsFullName()); + var randomizerCity= RandomizerFactory.GetRandomizer(new FieldOptionsCity()); + + for (int i = 0; i < total; i++) + { + var user = new User + { + Id = Guid.NewGuid(), + FullName = randomizerFullName.Generate(), + City = randomizerCity.Generate(), + Income = 1 + i % 15 * 100 + }; + + list.Add(user); + } + + return list.ToArray(); + } + } +} diff --git a/src/System.Linq.Dynamic.Core/CustomTypeProviders/AbstractDynamicLinqCustomTypeProvider.cs b/src/System.Linq.Dynamic.Core/CustomTypeProviders/AbstractDynamicLinqCustomTypeProvider.cs index afeb3d3b..40e25650 100644 --- a/src/System.Linq.Dynamic.Core/CustomTypeProviders/AbstractDynamicLinqCustomTypeProvider.cs +++ b/src/System.Linq.Dynamic.Core/CustomTypeProviders/AbstractDynamicLinqCustomTypeProvider.cs @@ -1,33 +1,27 @@ using JetBrains.Annotations; using System.Collections.Generic; -using System.Reflection; using System.Linq.Dynamic.Core.Validation; +using System.Reflection; namespace System.Linq.Dynamic.Core.CustomTypeProviders { /// - /// The abstract DynamicLinqCustomTypeProvider which is used by the and can be used by a custom TypeProvider like in .NET Core. + /// The abstract DynamicLinqCustomTypeProvider which is used by the DefaultDynamicLinqCustomTypeProvider and can be used by a custom TypeProvider like in .NET Core. /// public abstract class AbstractDynamicLinqCustomTypeProvider { /// - /// Finds the types marked with DynamicLinqTypeAttribute. + /// Finds the unique types marked with DynamicLinqTypeAttribute. /// /// The assemblies to process. - /// IEnumerable{Type} + /// protected IEnumerable FindTypesMarkedWithDynamicLinqTypeAttribute([NotNull] IEnumerable assemblies) { Check.NotNull(assemblies, nameof(assemblies)); #if !NET35 - assemblies = assemblies.Where(x => !x.IsDynamic); -#endif - var definedTypes = GetAssemblyTypes(assemblies); - -#if (WINDOWS_APP || DOTNET5_1 || UAP10_0 || NETSTANDARD) - return definedTypes.Where(x => x.CustomAttributes.Any(y => y.AttributeType == typeof(DynamicLinqTypeAttribute))).Select(x => x.AsType()); -#else - return definedTypes.Where(x => x.GetCustomAttributes(typeof(DynamicLinqTypeAttribute), false).Any()); + assemblies = assemblies.Where(a => !a.IsDynamic); #endif + return GetAssemblyTypesWithDynamicLinqTypeAttribute(assemblies).Distinct().ToArray(); } /// @@ -41,7 +35,7 @@ protected Type ResolveType([NotNull] IEnumerable assemblies, [NotNull] Check.NotNull(assemblies, nameof(assemblies)); Check.NotEmpty(typeName, nameof(typeName)); - foreach (Assembly assembly in assemblies) + foreach (var assembly in assemblies) { Type resolvedType = assembly.GetType(typeName, false, true); if (resolvedType != null) @@ -55,28 +49,32 @@ protected Type ResolveType([NotNull] IEnumerable assemblies, [NotNull] #if (WINDOWS_APP || DOTNET5_1 || UAP10_0 || NETSTANDARD) /// - /// Gets the assembly types in an Exception friendly way. + /// Gets the assembly types annotated with in an Exception friendly way. /// /// The assemblies to process. - /// IEnumerable{Type} - protected IEnumerable GetAssemblyTypes([NotNull] IEnumerable assemblies) + /// + protected IEnumerable GetAssemblyTypesWithDynamicLinqTypeAttribute([NotNull] IEnumerable assemblies) { Check.NotNull(assemblies, nameof(assemblies)); foreach (var assembly in assemblies) { - IEnumerable definedTypes = null; + Type[] definedTypes = null; try { - definedTypes = assembly.DefinedTypes; + definedTypes = assembly.ExportedTypes.Where(t => t.GetTypeInfo().IsDefined(typeof(DynamicLinqTypeAttribute), false)).ToArray(); + } + catch (ReflectionTypeLoadException reflectionTypeLoadException) + { + definedTypes = reflectionTypeLoadException.Types; } catch { - // Ignore error + // Ignore all other exceptions } - if (definedTypes != null) + if (definedTypes != null && definedTypes.Length > 0) { foreach (var definedType in definedTypes) { @@ -87,28 +85,33 @@ protected IEnumerable GetAssemblyTypes([NotNull] IEnumerable } #else /// - /// Gets the assembly types in an Exception friendly way. + /// Gets the assembly types annotated with in an Exception friendly way. /// /// The assemblies to process. - /// IEnumerable{Type} - protected IEnumerable GetAssemblyTypes([NotNull] IEnumerable assemblies) + /// + protected IEnumerable GetAssemblyTypesWithDynamicLinqTypeAttribute([NotNull] IEnumerable assemblies) { Check.NotNull(assemblies, nameof(assemblies)); - foreach (var assembly in assemblies) + foreach (var assembly in assemblies.Where(a => !a.GlobalAssemblyCache)) // Skip System DLL's { - IEnumerable definedTypes = null; + Type[] definedTypes = null; try { - definedTypes = assembly.GetTypes(); + definedTypes = assembly.GetExportedTypes() + .Where(t => t.IsDefined(typeof(DynamicLinqTypeAttribute), false)).ToArray(); + } + catch (ReflectionTypeLoadException reflectionTypeLoadException) + { + definedTypes = reflectionTypeLoadException.Types; } catch { - // Ignore error + // Ignore all other exceptions } - if (definedTypes != null) + if (definedTypes != null && definedTypes.Length > 0) { foreach (var definedType in definedTypes) { diff --git a/src/System.Linq.Dynamic.Core/CustomTypeProviders/DefaultDynamicLinqCustomTypeProvider.cs b/src/System.Linq.Dynamic.Core/CustomTypeProviders/DefaultDynamicLinqCustomTypeProvider.cs index d51c7f6f..ad91dc39 100644 --- a/src/System.Linq.Dynamic.Core/CustomTypeProviders/DefaultDynamicLinqCustomTypeProvider.cs +++ b/src/System.Linq.Dynamic.Core/CustomTypeProviders/DefaultDynamicLinqCustomTypeProvider.cs @@ -1,12 +1,12 @@ -#if !(WINDOWS_APP || UAP10_0) -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq.Dynamic.Core.Validation; using System.Reflection; namespace System.Linq.Dynamic.Core.CustomTypeProviders { /// - /// The default . + /// The default implementation for . + /// /// Scans the current AppDomain for all types marked with , and adds them as custom Dynamic Link types. /// /// Also provides functionality to resolve a Type in the current Application Domain. @@ -16,12 +16,33 @@ namespace System.Linq.Dynamic.Core.CustomTypeProviders public class DefaultDynamicLinqCustomTypeProvider : AbstractDynamicLinqCustomTypeProvider, IDynamicLinkCustomTypeProvider { private readonly IAssemblyHelper _assemblyHelper = new DefaultAssemblyHelper(); + private readonly bool _cacheCustomTypes; + + private HashSet _cachedCustomTypes; + + /// + /// Initializes a new instance of the class. + /// + /// Defines whether to cache the CustomTypes which are found in the Application Domain. Default set to 'true'. + public DefaultDynamicLinqCustomTypeProvider(bool cacheCustomTypes = true) + { + _cacheCustomTypes = cacheCustomTypes; + } /// public virtual HashSet GetCustomTypes() { - IEnumerable assemblies = _assemblyHelper.GetAssemblies(); - return new HashSet(FindTypesMarkedWithDynamicLinqTypeAttribute(assemblies)); + if (_cacheCustomTypes) + { + if (_cachedCustomTypes == null) + { + _cachedCustomTypes = GetCustomTypesInternal(); + } + + return _cachedCustomTypes; + } + + return GetCustomTypesInternal(); } /// @@ -32,6 +53,11 @@ public Type ResolveType(string typeName) IEnumerable assemblies = _assemblyHelper.GetAssemblies(); return ResolveType(assemblies, typeName); } + + private HashSet GetCustomTypesInternal() + { + IEnumerable assemblies = _assemblyHelper.GetAssemblies(); + return new HashSet(FindTypesMarkedWithDynamicLinqTypeAttribute(assemblies)); + } } } -#endif diff --git a/src/System.Linq.Dynamic.Core/CustomTypeProviders/IDynamicLinkCustomTypeProvider.cs b/src/System.Linq.Dynamic.Core/CustomTypeProviders/IDynamicLinkCustomTypeProvider.cs index 49e355be..a56a9a93 100644 --- a/src/System.Linq.Dynamic.Core/CustomTypeProviders/IDynamicLinkCustomTypeProvider.cs +++ b/src/System.Linq.Dynamic.Core/CustomTypeProviders/IDynamicLinkCustomTypeProvider.cs @@ -1,5 +1,5 @@ -using System.Collections.Generic; -using JetBrains.Annotations; +using JetBrains.Annotations; +using System.Collections.Generic; namespace System.Linq.Dynamic.Core.CustomTypeProviders { @@ -11,7 +11,7 @@ public interface IDynamicLinkCustomTypeProvider /// /// Returns a list of custom types that System.Linq.Dynamic.Core will understand. /// - /// A list of custom types. + /// A list of custom types. HashSet GetCustomTypes(); /// diff --git a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs index f884bda1..66b0280b 100644 --- a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs +++ b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs @@ -1564,7 +1564,7 @@ Type FindType(string name) { _keywordsHelper.TryGetValue(name, out object type); - var result = type as Type; + Type result = type as Type; if (result != null) { return result; @@ -1584,6 +1584,7 @@ Type FindType(string name) { return _root.Type; } + if (_it != null && _it.Type.Namespace + "." + _it.Type.Name == name) { return _it.Type; diff --git a/src/System.Linq.Dynamic.Core/ParsingConfig.cs b/src/System.Linq.Dynamic.Core/ParsingConfig.cs index 84741f21..a86562a9 100644 --- a/src/System.Linq.Dynamic.Core/ParsingConfig.cs +++ b/src/System.Linq.Dynamic.Core/ParsingConfig.cs @@ -35,7 +35,7 @@ public IDynamicLinkCustomTypeProvider CustomTypeProvider get { #if !(DOTNET5_1 || WINDOWS_APP || UAP10_0 || NETSTANDARD) - // only use DefaultDynamicLinqCustomTypeProvider if not WINDOWS_APP || UAP10_0 || NETSTANDARD + // only use DefaultDynamicLinqCustomTypeProvider for full .NET Framework return _customTypeProvider ?? (_customTypeProvider = new DefaultDynamicLinqCustomTypeProvider()); #else return _customTypeProvider; diff --git a/test/EntityFramework.DynamicLinq.Tests.net452/EntityFramework.DynamicLinq.Tests.net452.csproj b/test/EntityFramework.DynamicLinq.Tests.net452/EntityFramework.DynamicLinq.Tests.net452.csproj index effb692b..4b471b46 100644 --- a/test/EntityFramework.DynamicLinq.Tests.net452/EntityFramework.DynamicLinq.Tests.net452.csproj +++ b/test/EntityFramework.DynamicLinq.Tests.net452/EntityFramework.DynamicLinq.Tests.net452.csproj @@ -64,8 +64,8 @@ ..\..\packages\Moq.4.10.0\lib\net45\Moq.dll - - ..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll + + ..\..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll ..\..\packages\NFluent.2.1.1\lib\net45\NFluent.dll diff --git a/test/EntityFramework.DynamicLinq.Tests.net452/packages.config b/test/EntityFramework.DynamicLinq.Tests.net452/packages.config index cd072130..e5dd8b28 100644 --- a/test/EntityFramework.DynamicLinq.Tests.net452/packages.config +++ b/test/EntityFramework.DynamicLinq.Tests.net452/packages.config @@ -9,7 +9,7 @@ - + diff --git a/test/EntityFramework.DynamicLinq.Tests/EntityFramework.DynamicLinq.Tests.csproj b/test/EntityFramework.DynamicLinq.Tests/EntityFramework.DynamicLinq.Tests.csproj index f390dffb..5285cf81 100644 --- a/test/EntityFramework.DynamicLinq.Tests/EntityFramework.DynamicLinq.Tests.csproj +++ b/test/EntityFramework.DynamicLinq.Tests/EntityFramework.DynamicLinq.Tests.csproj @@ -34,7 +34,7 @@ - + diff --git a/test/System.Linq.Dynamic.Core.Tests/System.Linq.Dynamic.Core.Tests.csproj b/test/System.Linq.Dynamic.Core.Tests/System.Linq.Dynamic.Core.Tests.csproj index 22101e05..8790911c 100644 --- a/test/System.Linq.Dynamic.Core.Tests/System.Linq.Dynamic.Core.Tests.csproj +++ b/test/System.Linq.Dynamic.Core.Tests/System.Linq.Dynamic.Core.Tests.csproj @@ -30,7 +30,7 @@ all runtime; build; native; contentfiles; analyzers - +