diff --git a/src-console/ConsoleAppEF2.0.2_InMemory/Program.cs b/src-console/ConsoleAppEF2.0.2_InMemory/Program.cs index 5dbd40ea..295eb8d8 100644 --- a/src-console/ConsoleAppEF2.0.2_InMemory/Program.cs +++ b/src-console/ConsoleAppEF2.0.2_InMemory/Program.cs @@ -41,6 +41,12 @@ public Type ResolveType(string typeName) return ResolveType(assemblies, typeName); } + + public Type ResolveTypeBySimpleName(string typeName) + { + var assemblies = AppDomain.CurrentDomain.GetAssemblies(); + return ResolveTypeBySimpleName(assemblies, typeName); + } } private static IQueryable GetQueryable() diff --git a/src-console/ConsoleAppEF2.0/Program.cs b/src-console/ConsoleAppEF2.0/Program.cs index ecce6aac..a0ed25a0 100644 --- a/src-console/ConsoleAppEF2.0/Program.cs +++ b/src-console/ConsoleAppEF2.0/Program.cs @@ -30,6 +30,12 @@ public Type ResolveType(string typeName) var assemblies = AppDomain.CurrentDomain.GetAssemblies(); return ResolveType(assemblies, typeName); } + + public Type ResolveTypeBySimpleName(string typeName) + { + var assemblies = AppDomain.CurrentDomain.GetAssemblies(); + return ResolveTypeBySimpleName(assemblies, typeName); + } } private static IQueryable GetQueryable() diff --git a/src-console/ConsoleAppEF2.1.1/Program.cs b/src-console/ConsoleAppEF2.1.1/Program.cs index 70b8a1b2..645a545b 100644 --- a/src-console/ConsoleAppEF2.1.1/Program.cs +++ b/src-console/ConsoleAppEF2.1.1/Program.cs @@ -31,6 +31,12 @@ public Type ResolveType(string typeName) return ResolveType(assemblies, typeName); } + + public Type ResolveTypeBySimpleName(string typeName) + { + var assemblies = AppDomain.CurrentDomain.GetAssemblies(); + return ResolveTypeBySimpleName(assemblies, typeName); + } } private static IQueryable GetQueryable() diff --git a/src-console/ConsoleAppEF2.1.1_InMemory/Program.cs b/src-console/ConsoleAppEF2.1.1_InMemory/Program.cs index 2ff28a83..989c2390 100644 --- a/src-console/ConsoleAppEF2.1.1_InMemory/Program.cs +++ b/src-console/ConsoleAppEF2.1.1_InMemory/Program.cs @@ -189,6 +189,12 @@ private static void OfTypeAndCastTests(TestContext context, ParsingConfig config var oftypeDynamicWithType = context.BaseDtos.OfType(typeof(TestDto)).ToDynamicArray(); var oftypeDynamicWithString = context.BaseDtos.OfType(config, "ConsoleAppEF2.Database.TestDto").ToDynamicArray(); + var configX = new ParsingConfig + { + ResolveTypesBySimpleName = true + }; + var oftypeDynamicWithSimpleNameString = context.BaseDtos.OfType(configX, "TestDto").ToDynamicArray(); + int isOfType = context.BaseDtos.Count(b => b is TestDto); int isOfTypeDynamicTestDto = context.BaseDtos.Count(config, "OfType(\"ConsoleAppEF2.Database.TestDto\")"); int isOfTypeDynamicOtherTestDto = context.BaseDtos.Count(config, "OfType(\"ConsoleAppEF2.Database.OtherTestDto\")"); diff --git a/src/System.Linq.Dynamic.Core/CustomTypeProviders/AbstractDynamicLinqCustomTypeProvider.cs b/src/System.Linq.Dynamic.Core/CustomTypeProviders/AbstractDynamicLinqCustomTypeProvider.cs index 40e25650..0b9ab268 100644 --- a/src/System.Linq.Dynamic.Core/CustomTypeProviders/AbstractDynamicLinqCustomTypeProvider.cs +++ b/src/System.Linq.Dynamic.Core/CustomTypeProviders/AbstractDynamicLinqCustomTypeProvider.cs @@ -47,6 +47,35 @@ protected Type ResolveType([NotNull] IEnumerable assemblies, [NotNull] return null; } + /// + /// Resolve a type by the simple name which is registered in the current application domain. + /// + /// The assemblies to inspect. + /// The simple typename to resolve. + /// A resolved or null when not found. + protected Type ResolveTypeBySimpleName([NotNull] IEnumerable assemblies, [NotNull] string simpleTypeName) + { + Check.NotNull(assemblies, nameof(assemblies)); + Check.NotEmpty(simpleTypeName, nameof(simpleTypeName)); + + foreach (var assembly in assemblies) + { + var fullnames = assembly.GetTypes().Select(t => t.FullName).Distinct(); + var firstMatchingFullname = fullnames.FirstOrDefault(fn => fn.EndsWith($".{simpleTypeName}")); + + if (firstMatchingFullname != null) + { + Type resolvedType = assembly.GetType(firstMatchingFullname, false, true); + if (resolvedType != null) + { + return resolvedType; + } + } + } + + return null; + } + #if (WINDOWS_APP || DOTNET5_1 || UAP10_0 || NETSTANDARD) /// /// Gets the assembly types annotated with in an Exception friendly way. diff --git a/src/System.Linq.Dynamic.Core/CustomTypeProviders/DefaultDynamicLinqCustomTypeProvider.cs b/src/System.Linq.Dynamic.Core/CustomTypeProviders/DefaultDynamicLinqCustomTypeProvider.cs index ad91dc39..757352e9 100644 --- a/src/System.Linq.Dynamic.Core/CustomTypeProviders/DefaultDynamicLinqCustomTypeProvider.cs +++ b/src/System.Linq.Dynamic.Core/CustomTypeProviders/DefaultDynamicLinqCustomTypeProvider.cs @@ -54,6 +54,15 @@ public Type ResolveType(string typeName) return ResolveType(assemblies, typeName); } + /// + public Type ResolveTypeBySimpleName(string simpleTypeName) + { + Check.NotEmpty(simpleTypeName, nameof(simpleTypeName)); + + IEnumerable assemblies = _assemblyHelper.GetAssemblies(); + return ResolveTypeBySimpleName(assemblies, simpleTypeName); + } + private HashSet GetCustomTypesInternal() { IEnumerable assemblies = _assemblyHelper.GetAssemblies(); diff --git a/src/System.Linq.Dynamic.Core/CustomTypeProviders/IDynamicLinkCustomTypeProvider.cs b/src/System.Linq.Dynamic.Core/CustomTypeProviders/IDynamicLinkCustomTypeProvider.cs index a56a9a93..3a8c0ed0 100644 --- a/src/System.Linq.Dynamic.Core/CustomTypeProviders/IDynamicLinkCustomTypeProvider.cs +++ b/src/System.Linq.Dynamic.Core/CustomTypeProviders/IDynamicLinkCustomTypeProvider.cs @@ -15,10 +15,17 @@ public interface IDynamicLinkCustomTypeProvider HashSet GetCustomTypes(); /// - /// Resolve any type which is registered in the current application domain. + /// Resolve any type by fullname which is registered in the current application domain. /// /// The typename to resolve. /// A resolved or null when not found. Type ResolveType([NotNull] string typeName); + + /// + /// Resolve any type by the simple name which is registered in the current application domain. + /// + /// The typename to resolve. + /// A resolved or null when not found. + Type ResolveTypeBySimpleName([NotNull] string simpleTypeName); } } diff --git a/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs b/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs index 007b9e4e..bd58c7c1 100644 --- a/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs +++ b/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs @@ -229,10 +229,10 @@ public static IQueryable Cast([NotNull] this IQueryable source, [NotNull] Parsin Check.NotNull(config, nameof(config)); Check.NotEmpty(typeName, nameof(typeName)); - config.AllowNewToEvaluateAnyType = true; - var newExpression = DynamicExpressionParser.ParseLambda(config, null, $"new {typeName}()"); + var finder = new TypeFinder(config, new KeywordsHelper(config)); + Type type = finder.FindTypeByName(typeName, null, true); - return Cast(source, newExpression.Body.Type); + return Cast(source, type); } /// @@ -1025,10 +1025,10 @@ public static IQueryable OfType([NotNull] this IQueryable source, [NotNull] Pars Check.NotNull(config, nameof(config)); Check.NotEmpty(typeName, nameof(typeName)); - config.AllowNewToEvaluateAnyType = true; - var newExpression = DynamicExpressionParser.ParseLambda(config, null, $"new {typeName}()"); + var finder = new TypeFinder(config, new KeywordsHelper(config)); + Type type = finder.FindTypeByName(typeName, null, true); - return OfType(source, newExpression.Body.Type); + return OfType(source, type); } /// @@ -2156,14 +2156,7 @@ private static TResult Execute(MethodInfo operatorMethodInfo, IQueryabl private static MethodInfo GetGenericMethod(string name) { - try - { - return typeof(Queryable).GetTypeInfo().GetDeclaredMethods(name).Single(mi => mi.IsGenericMethod); - } - catch (Exception ex) - { - throw new Exception("Method not found: " + name, ex); - } + return typeof(Queryable).GetTypeInfo().GetDeclaredMethods(name).Single(mi => mi.IsGenericMethod); } private static MethodInfo GetMethod(string name, int parameterCount = 0, Func predicate = null) diff --git a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs index b55e9903..f8ca37d5 100644 --- a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs +++ b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs @@ -25,9 +25,10 @@ public class ExpressionParser private readonly ParsingConfig _parsingConfig; private readonly MethodFinder _methodFinder; - private readonly KeywordsHelper _keywordsHelper; + private readonly IKeywordsHelper _keywordsHelper; private readonly TextParser _textParser; private readonly IExpressionHelper _expressionHelper; + private readonly ITypeFinder _typeFinder; private readonly Dictionary _internals; private readonly Dictionary _symbols; @@ -73,6 +74,7 @@ public ExpressionParser([CanBeNull] ParameterExpression[] parameters, [NotNull] _textParser = new TextParser(expression); _methodFinder = new MethodFinder(_parsingConfig); _expressionHelper = new ExpressionHelper(_parsingConfig); + _typeFinder = new TypeFinder(_parsingConfig, _keywordsHelper); } void ProcessParameters(ParameterExpression[] parameters) @@ -1242,7 +1244,7 @@ Expression ParseNew() _textParser.NextToken(); } - newType = FindType(newTypeName); + newType = _typeFinder.FindTypeByName(newTypeName, new[] { _it, _parent, _root }, false); if (newType == null) { throw ParseError(_textParser.CurrentToken.Pos, Res.TypeNotFound, newTypeName); @@ -1654,54 +1656,6 @@ Expression ParseMemberAccess(Type type, Expression instance) throw ParseError(errorPos, Res.UnknownPropertyOrField, id, TypeHelper.GetTypeName(type)); } - Type FindType(string name) - { - _keywordsHelper.TryGetValue(name, out object type); - - Type result = type as Type; - if (result != null) - { - return result; - } - - if (_it != null && _it.Type.Name == name) - { - return _it.Type; - } - - if (_parent != null && _parent.Type.Name == name) - { - return _parent.Type; - } - - if (_root != null && _root.Type.Name == name) - { - return _root.Type; - } - - if (_it != null && _it.Type.Namespace + "." + _it.Type.Name == name) - { - return _it.Type; - } - - if (_parent != null && _parent.Type.Namespace + "." + _parent.Type.Name == name) - { - return _parent.Type; - } - - if (_root != null && _root.Type.Namespace + "." + _root.Type.Name == name) - { - return _root.Type; - } - - if (_parsingConfig.AllowNewToEvaluateAnyType && _parsingConfig.CustomTypeProvider != null) - { - return _parsingConfig.CustomTypeProvider.ResolveType(name); - } - - return null; - } - Expression ParseAggregate(Expression instance, Type elementType, string methodName, int errorPos, bool isQueryable) { var oldParent = _parent; @@ -1806,7 +1760,7 @@ private Type ResolveTypeFromArgumentExpression(string functionName, Expression a throw ParseError(_textParser.CurrentToken.Pos, Res.FunctionRequiresOneNotNullArg, functionName, typeName); } - Type resultType = FindType(typeName); + Type resultType = _typeFinder.FindTypeByName(typeName, new[] { _it, _parent, _root }, true); if (resultType == null) { throw ParseError(_textParser.CurrentToken.Pos, Res.TypeNotFound, typeName); diff --git a/src/System.Linq.Dynamic.Core/Parser/IKeywordsHelper.cs b/src/System.Linq.Dynamic.Core/Parser/IKeywordsHelper.cs new file mode 100644 index 00000000..52971321 --- /dev/null +++ b/src/System.Linq.Dynamic.Core/Parser/IKeywordsHelper.cs @@ -0,0 +1,7 @@ +namespace System.Linq.Dynamic.Core.Parser +{ + interface IKeywordsHelper + { + bool TryGetValue(string name, out object type); + } +} diff --git a/src/System.Linq.Dynamic.Core/Parser/ITypeFinder.cs b/src/System.Linq.Dynamic.Core/Parser/ITypeFinder.cs new file mode 100644 index 00000000..0de66306 --- /dev/null +++ b/src/System.Linq.Dynamic.Core/Parser/ITypeFinder.cs @@ -0,0 +1,10 @@ +using System.Linq.Expressions; +using JetBrains.Annotations; + +namespace System.Linq.Dynamic.Core.Parser +{ + interface ITypeFinder + { + Type FindTypeByName([NotNull] string name, [CanBeNull] ParameterExpression[] expressions, bool forceUseCustomTypeProvider); + } +} diff --git a/src/System.Linq.Dynamic.Core/Parser/KeywordsHelper.cs b/src/System.Linq.Dynamic.Core/Parser/KeywordsHelper.cs index fdce4c8d..3259cef1 100644 --- a/src/System.Linq.Dynamic.Core/Parser/KeywordsHelper.cs +++ b/src/System.Linq.Dynamic.Core/Parser/KeywordsHelper.cs @@ -2,7 +2,7 @@ namespace System.Linq.Dynamic.Core.Parser { - internal class KeywordsHelper + internal class KeywordsHelper : IKeywordsHelper { public const string SYMBOL_IT = "$"; public const string SYMBOL_PARENT = "^"; diff --git a/src/System.Linq.Dynamic.Core/Parser/TypeFinder.cs b/src/System.Linq.Dynamic.Core/Parser/TypeFinder.cs new file mode 100644 index 00000000..f5048584 --- /dev/null +++ b/src/System.Linq.Dynamic.Core/Parser/TypeFinder.cs @@ -0,0 +1,93 @@ +using JetBrains.Annotations; +using System.Linq.Dynamic.Core.Validation; +using System.Linq.Expressions; + +namespace System.Linq.Dynamic.Core.Parser +{ + internal class TypeFinder : ITypeFinder + { + private readonly IKeywordsHelper _keywordsHelper; + private readonly ParsingConfig _parsingConfig; + + public TypeFinder([NotNull] ParsingConfig parsingConfig, [NotNull] IKeywordsHelper keywordsHelper) + { + Check.NotNull(parsingConfig, nameof(parsingConfig)); + Check.NotNull(keywordsHelper, nameof(keywordsHelper)); + + _keywordsHelper = keywordsHelper; + _parsingConfig = parsingConfig; + } + + public Type FindTypeByName(string name, ParameterExpression[] expressions, bool forceUseCustomTypeProvider) + { + Check.NotEmpty(name, nameof(name)); + + _keywordsHelper.TryGetValue(name, out object type); + + Type result = type as Type; + if (result != null) + { + return result; + } + + if (expressions != null && TryResolveTypeUsingExpressions(name, expressions, out result)) + { + return result; + } + + return ResolveTypeByUsingCustomTypeProvider(name, forceUseCustomTypeProvider); + } + + private Type ResolveTypeByUsingCustomTypeProvider(string name, bool forceUseCustomTypeProvider) + { + if ((forceUseCustomTypeProvider || _parsingConfig.AllowNewToEvaluateAnyType) && _parsingConfig.CustomTypeProvider != null) + { + Type resolvedType = _parsingConfig.CustomTypeProvider.ResolveType(name); + if (resolvedType != null) + { + return resolvedType; + } + + // In case the type is not found based on fullname, try to get the type on simplename if allowed + if (_parsingConfig.ResolveTypesBySimpleName) + { + return _parsingConfig.CustomTypeProvider.ResolveTypeBySimpleName(name); + } + } + + return null; + } + + private bool TryResolveTypeUsingExpressions(string name, ParameterExpression[] expressions, out Type result) + { + foreach (var expression in expressions.Where(e => e != null)) + { + if (expression.Type.Name == name) + { + result = expression.Type; + return true; + } + + if ($"{expression.Type.Namespace}.{expression.Type.Name}" == name) + { + result = expression.Type; + return true; + } + + if (_parsingConfig.ResolveTypesBySimpleName && _parsingConfig.CustomTypeProvider != null) + { + string possibleFullName = $"{expression.Type.Namespace}.{name}"; + var resolvedType = _parsingConfig.CustomTypeProvider.ResolveType(possibleFullName); + if (resolvedType != null) + { + result = resolvedType; + return true; + } + } + } + + result = null; + return false; + } + } +} diff --git a/src/System.Linq.Dynamic.Core/ParsingConfig.cs b/src/System.Linq.Dynamic.Core/ParsingConfig.cs index f14d4492..9807ac05 100644 --- a/src/System.Linq.Dynamic.Core/ParsingConfig.cs +++ b/src/System.Linq.Dynamic.Core/ParsingConfig.cs @@ -131,5 +131,13 @@ public IQueryableAnalyzer QueryableAnalyzer /// where a member access on a non existing member happens. Default value is false. /// public bool DisableMemberAccessToIndexAccessorFallback { get; set; } = false; + + /// + /// By default finding types by a simple name is not suported. + /// Use this flag to use the CustomTypeProvider to resolve types by a simple name like "Employee" instead of "MyDatabase.Entities.Employee". + /// Note that a first matching type is returned and this functionality needs to scan all types from all assemblies, so use with caution. + /// Default value is false. + /// + public bool ResolveTypesBySimpleName { get; set; } = false; } } diff --git a/src/System.Linq.Dynamic.Core/Properties/AssemblyInfo.cs b/src/System.Linq.Dynamic.Core/Properties/AssemblyInfo.cs index 0122483f..4cab5065 100644 --- a/src/System.Linq.Dynamic.Core/Properties/AssemblyInfo.cs +++ b/src/System.Linq.Dynamic.Core/Properties/AssemblyInfo.cs @@ -1,33 +1,4 @@ -//using System.Reflection; +using System.Runtime.CompilerServices; -//// General Information about an assembly is controlled through the following -//// set of attributes. Change these attribute values to modify the information -//// associated with an assembly. -//[assembly: AssemblyTitle("System.Linq.Dynamic.Core")] -//[assembly: AssemblyDescription("")] -//[assembly: AssemblyConfiguration("")] -//[assembly: AssemblyCompany("")] -//[assembly: AssemblyProduct("System.Linq.Dynamic.Core")] -//[assembly: AssemblyCopyright("Copyright © Stef Heyenrath 2016")] -//[assembly: AssemblyTrademark("")] -//[assembly: AssemblyCulture("")] -//#if !(NETSTANDARD || WINDOWS_APP) -//[assembly: System.Resources.NeutralResourcesLanguage("en")] -//#endif - -//// Version information for an assembly consists of the following four values: -//// -//// Major Version -//// Minor Version -//// Build Number -//// Revision -//// -//// You can specify all the values or you can default the Build and Revision Numbers -//// by using the '*' as shown below: -//// [assembly: AssemblyVersion("1.0.*")] -//[assembly: AssemblyVersion("1.0.0.0")] -//[assembly: AssemblyFileVersion("1.0.0.0")] - -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("System.Linq.Dynamic.Core.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001003daf4f4b7d160b1033de9a4a3275f4667a4558144296c3bb593aa0fd213dadf0ea4df5aa69e21763d409ada2a8f8925081bc2e81362be7916e22c624344309eba764edc4f8f84237ae053d2687ab3b888c9f4f3ff8a804bb5fee61e1ceadec97b08994580ef2df6bd7e077df4ad205c6d2bde479c512ab9be6ecc23c10694597")] \ No newline at end of file +[assembly: InternalsVisibleTo("System.Linq.Dynamic.Core.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001003daf4f4b7d160b1033de9a4a3275f4667a4558144296c3bb593aa0fd213dadf0ea4df5aa69e21763d409ada2a8f8925081bc2e81362be7916e22c624344309eba764edc4f8f84237ae053d2687ab3b888c9f4f3ff8a804bb5fee61e1ceadec97b08994580ef2df6bd7e077df4ad205c6d2bde479c512ab9be6ecc23c10694597")] +[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] diff --git a/test-uap/WindowsUniversalTestApp14393/WindowsAppCustomTypeProvider.cs b/test-uap/WindowsUniversalTestApp14393/WindowsAppCustomTypeProvider.cs index 178bd645..bdb6b496 100644 --- a/test-uap/WindowsUniversalTestApp14393/WindowsAppCustomTypeProvider.cs +++ b/test-uap/WindowsUniversalTestApp14393/WindowsAppCustomTypeProvider.cs @@ -23,6 +23,12 @@ public Type ResolveType(string typeName) return ResolveType(assemblies, typeName); } + public Type ResolveTypeBySimpleName(string typeName) + { + var assemblies = GetAssemblyListAsync().Result; + return ResolveTypeBySimpleName(assemblies, typeName); + } + private static async Task> GetAssemblyListAsync() { List assemblies = new List(); diff --git a/test-uap/WindowsUniversalTestApp14393/WindowsUniversalTestApp14393.csproj b/test-uap/WindowsUniversalTestApp14393/WindowsUniversalTestApp14393.csproj index 9a0a20a8..4aa699e5 100644 --- a/test-uap/WindowsUniversalTestApp14393/WindowsUniversalTestApp14393.csproj +++ b/test-uap/WindowsUniversalTestApp14393/WindowsUniversalTestApp14393.csproj @@ -143,7 +143,7 @@ - ..\..\src\System.Linq.Dynamic.Core\bin\$(Configuration)\uap10.0\System.Linq.Dynamic.Core.dll + ..\..\src\System.Linq.Dynamic.Core\bin\Debug\net35\System.Linq.Dynamic.Core.dll diff --git a/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs b/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs index 52bb3f6d..0f9b9007 100644 --- a/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs +++ b/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs @@ -112,6 +112,12 @@ public Type ResolveType(string typeName) { return Type.GetType(typeName); } + + public Type ResolveTypeBySimpleName(string typeName) + { + var assemblies = AppDomain.CurrentDomain.GetAssemblies(); + return ResolveTypeBySimpleName(assemblies, typeName); + } } [Fact] diff --git a/test/System.Linq.Dynamic.Core.Tests/Parser/TypeFinderTests.cs b/test/System.Linq.Dynamic.Core.Tests/Parser/TypeFinderTests.cs new file mode 100644 index 00000000..904e1f9a --- /dev/null +++ b/test/System.Linq.Dynamic.Core.Tests/Parser/TypeFinderTests.cs @@ -0,0 +1,77 @@ +using Moq; +using NFluent; +using System.Linq.Dynamic.Core.CustomTypeProviders; +using System.Linq.Dynamic.Core.Parser; +using System.Linq.Dynamic.Core.Tests.Entities; +using System.Linq.Expressions; +using Xunit; + +namespace System.Linq.Dynamic.Core.Tests.Parser +{ + public class TypeFinderTests + { + private readonly ParsingConfig _parsingConfig = new ParsingConfig(); + private readonly Mock _keywordsHelperMock; + private readonly Mock _dynamicTypeProviderMock; + + private readonly TypeFinder _sut; + + public TypeFinderTests() + { + _dynamicTypeProviderMock = new Mock(); + _dynamicTypeProviderMock.Setup(dt => dt.ResolveType(typeof(BaseEmployee).FullName)).Returns(typeof(BaseEmployee)); + _dynamicTypeProviderMock.Setup(dt => dt.ResolveType(typeof(Boss).FullName)).Returns(typeof(Boss)); + _dynamicTypeProviderMock.Setup(dt => dt.ResolveType(typeof(Worker).FullName)).Returns(typeof(Worker)); + _dynamicTypeProviderMock.Setup(dt => dt.ResolveTypeBySimpleName("Boss")).Returns(typeof(Boss)); + + _parsingConfig = new ParsingConfig + { + CustomTypeProvider = _dynamicTypeProviderMock.Object + }; + + _keywordsHelperMock = new Mock(); + + _sut = new TypeFinder(_parsingConfig, _keywordsHelperMock.Object); + } + + [Fact] + public void TypeFinder_FindTypeByName_With_SimpleTypeName_forceUseCustomTypeProvider_equals_false() + { + // Assign + _parsingConfig.ResolveTypesBySimpleName = true; + + // Act + Type result = _sut.FindTypeByName("Boss", null, forceUseCustomTypeProvider: false); + + // Assert + Check.That(result).IsNull(); + } + + [Fact] + public void TypeFinder_FindTypeByName_With_SimpleTypeName_forceUseCustomTypeProvider_equals_true() + { + // Assign + _parsingConfig.ResolveTypesBySimpleName = true; + + // Act + Type result = _sut.FindTypeByName("Boss", null, forceUseCustomTypeProvider: true); + + // Assert + Check.That(result).Equals(typeof(Boss)); + } + + [Fact] + public void TypeFinder_FindTypeByName_With_SimpleTypeName_basedon_it() + { + // Assign + _parsingConfig.ResolveTypesBySimpleName = true; + var expressions = new[] { Expression.Parameter(typeof(BaseEmployee)) }; + + // Act + Type result = _sut.FindTypeByName("Boss", expressions, forceUseCustomTypeProvider: false); + + // Assert + Check.That(result).Equals(typeof(Boss)); + } + } +} diff --git a/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Is,OfType,As,Cast.cs b/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Is,OfType,As,Cast.cs index 8cc19e9c..6a616eec 100644 --- a/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Is,OfType,As,Cast.cs +++ b/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Is,OfType,As,Cast.cs @@ -103,6 +103,28 @@ public void OfType_Dynamic_ActingOnIt(string function) Check.That(countOfTypeDynamic).Equals(countOfType); } + [Fact] + public void OfType_Dynamic_ActingOnIt_WithSimpleName() + { + // Assign + var config = new ParsingConfig + { + ResolveTypesBySimpleName = true + }; + + var qry = new BaseEmployee[] + { + new Worker { Name = "1" }, new Boss { Name = "b" } + }.AsQueryable(); + + // Act + int countOfType = qry.Count(c => c is Worker); + int countOfTypeDynamic = qry.Count(config, "OfType(\"Worker\")"); + + // Assert + Check.That(countOfTypeDynamic).Equals(countOfType); + } + [Fact] public void As_Dynamic_ActingOnIt() {