diff --git a/src/System.Linq.Dynamic.Core/CustomTypeProviders/IDynamicLinqCustomTypeProvider.cs b/src/System.Linq.Dynamic.Core/CustomTypeProviders/IDynamicLinqCustomTypeProvider.cs index 4d950612..1adc34e9 100644 --- a/src/System.Linq.Dynamic.Core/CustomTypeProviders/IDynamicLinqCustomTypeProvider.cs +++ b/src/System.Linq.Dynamic.Core/CustomTypeProviders/IDynamicLinqCustomTypeProvider.cs @@ -1,38 +1,36 @@ -using JetBrains.Annotations; -using System.Collections.Generic; +using System.Collections.Generic; using System.Reflection; -namespace System.Linq.Dynamic.Core.CustomTypeProviders +namespace System.Linq.Dynamic.Core.CustomTypeProviders; + +/// +/// Interface for providing functionality to find custom types for or resolve any type. +/// +public interface IDynamicLinqCustomTypeProvider { /// - /// Interface for providing functionality to find custom types for or resolve any type. + /// Returns a list of custom types that System.Linq.Dynamic.Core will understand. /// - public interface IDynamicLinqCustomTypeProvider - { - /// - /// Returns a list of custom types that System.Linq.Dynamic.Core will understand. - /// - /// A list of custom types. - HashSet GetCustomTypes(); + /// A list of custom types. + HashSet GetCustomTypes(); - /// - /// Returns a list of custom extension methods that System.Linq.Dynamic.Core will understand. - /// - /// A list of custom extension methods that System.Linq.Dynamic.Core will understand. - Dictionary> GetExtensionMethods(); + /// + /// Returns a list of custom extension methods that System.Linq.Dynamic.Core will understand. + /// + /// A list of custom extension methods that System.Linq.Dynamic.Core will understand. + Dictionary> GetExtensionMethods(); - /// - /// 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(string typeName); + /// + /// 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(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(string simpleTypeName); - } + /// + /// 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(string simpleTypeName); } \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs index e09049d2..d1ea7a90 100644 --- a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs +++ b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs @@ -1696,14 +1696,14 @@ private Expression ParseMemberAccess(Type? type, Expression? expression) } Expression[] args = ParseArgumentList(); - switch (_methodFinder.FindMethod(type, id, expression == null, ref expression, ref args, out var mb)) + switch (_methodFinder.FindMethod(type, id, expression == null, ref expression, ref args, out var methodBase)) { case 0: throw ParseError(errorPos, Res.NoApplicableMethod, id, TypeHelper.GetTypeName(type)); case 1: - MethodInfo method = (MethodInfo)mb!; - if (!PredefinedTypesHelper.IsPredefinedType(_parsingConfig, method.DeclaringType!) && !(method.IsPublic && PredefinedTypesHelper.IsPredefinedType(_parsingConfig, method.ReturnType))) + var method = (MethodInfo)methodBase!; + if (!PredefinedTypesHelper.IsPredefinedType(_parsingConfig, method.DeclaringType!)) { throw ParseError(errorPos, Res.MethodsAreInaccessible, TypeHelper.GetTypeName(method.DeclaringType!)); } diff --git a/src/System.Linq.Dynamic.Core/Parser/PredefinedTypesHelper.cs b/src/System.Linq.Dynamic.Core/Parser/PredefinedTypesHelper.cs index 3bfe8bb6..fb19e85f 100644 --- a/src/System.Linq.Dynamic.Core/Parser/PredefinedTypesHelper.cs +++ b/src/System.Linq.Dynamic.Core/Parser/PredefinedTypesHelper.cs @@ -1,108 +1,109 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq.Dynamic.Core.Validation; -using System.Text.RegularExpressions; -namespace System.Linq.Dynamic.Core.Parser +namespace System.Linq.Dynamic.Core.Parser; + +internal static class PredefinedTypesHelper { - internal static class PredefinedTypesHelper - { - private static readonly string Version = Regex.Match(typeof(PredefinedTypesHelper).AssemblyQualifiedName, @"\d+\.\d+\.\d+\.\d+").ToString(); +#if NETSTANDARD2_0 + private static readonly string Version = Text.RegularExpressions.Regex.Match(typeof(PredefinedTypesHelper).AssemblyQualifiedName!, @"\d+\.\d+\.\d+\.\d+").ToString(); +#endif - // These shorthands have different name than actual type and therefore not recognized by default from the PredefinedTypes. - public static readonly IDictionary PredefinedTypesShorthands = new Dictionary - { - { "int", typeof(int) }, - { "uint", typeof(uint) }, - { "short", typeof(short) }, - { "ushort", typeof(ushort) }, - { "long", typeof(long) }, - { "ulong", typeof(ulong) }, - { "bool", typeof(bool) }, - { "float", typeof(float) } - }; + // These shorthands have different name than actual type and therefore not recognized by default from the PredefinedTypes. + public static readonly IDictionary PredefinedTypesShorthands = new Dictionary + { + { "int", typeof(int) }, + { "uint", typeof(uint) }, + { "short", typeof(short) }, + { "ushort", typeof(ushort) }, + { "long", typeof(long) }, + { "ulong", typeof(ulong) }, + { "bool", typeof(bool) }, + { "float", typeof(float) } + }; - public static readonly IDictionary PredefinedTypes = new ConcurrentDictionary(new Dictionary { - { typeof(object), 0 }, - { typeof(bool), 0 }, - { typeof(char), 0 }, - { typeof(string), 0 }, - { typeof(sbyte), 0 }, - { typeof(byte), 0 }, - { typeof(short), 0 }, - { typeof(ushort), 0 }, - { typeof(int), 0 }, - { typeof(uint), 0 }, - { typeof(long), 0 }, - { typeof(ulong), 0 }, - { typeof(float), 0 }, - { typeof(double), 0 }, - { typeof(decimal), 0 }, - { typeof(DateTime), 0 }, - { typeof(DateTimeOffset), 0 }, - { typeof(TimeSpan), 0 }, - { typeof(Guid), 0 }, - { typeof(Math), 0 }, - { typeof(Convert), 0 }, - { typeof(Uri), 0 }, + public static readonly IDictionary PredefinedTypes = new ConcurrentDictionary(new Dictionary { + { typeof(object), 0 }, + { typeof(bool), 0 }, + { typeof(char), 0 }, + { typeof(string), 0 }, + { typeof(sbyte), 0 }, + { typeof(byte), 0 }, + { typeof(short), 0 }, + { typeof(ushort), 0 }, + { typeof(int), 0 }, + { typeof(uint), 0 }, + { typeof(long), 0 }, + { typeof(ulong), 0 }, + { typeof(float), 0 }, + { typeof(double), 0 }, + { typeof(decimal), 0 }, + { typeof(DateTime), 0 }, + { typeof(DateTimeOffset), 0 }, + { typeof(TimeSpan), 0 }, + { typeof(Guid), 0 }, + { typeof(Math), 0 }, + { typeof(Convert), 0 }, + { typeof(Uri), 0 }, #if NET6_0_OR_GREATER - { typeof(DateOnly), 0 }, - { typeof(TimeOnly), 0 } + { typeof(DateOnly), 0 }, + { typeof(TimeOnly), 0 } #endif - }); + }); - static PredefinedTypesHelper() - { + static PredefinedTypesHelper() + { #if !(NET35 || SILVERLIGHT || NETFX_CORE || WINDOWS_APP || UAP10_0 || NETSTANDARD) - //System.Data.Entity is always here, so overwrite short name of it with EntityFramework if EntityFramework is found. - //EF5(or 4.x??), System.Data.Objects.DataClasses.EdmFunctionAttribute - //There is also an System.Data.Entity, Version=3.5.0.0, but no Functions. - TryAdd("System.Data.Objects.EntityFunctions, System.Data.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 1); - TryAdd("System.Data.Objects.SqlClient.SqlFunctions, System.Data.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 1); - TryAdd("System.Data.Objects.SqlClient.SqlSpatialFunctions, System.Data.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 1); + //System.Data.Entity is always here, so overwrite short name of it with EntityFramework if EntityFramework is found. + //EF5(or 4.x??), System.Data.Objects.DataClasses.EdmFunctionAttribute + //There is also an System.Data.Entity, Version=3.5.0.0, but no Functions. + TryAdd("System.Data.Objects.EntityFunctions, System.Data.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 1); + TryAdd("System.Data.Objects.SqlClient.SqlFunctions, System.Data.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 1); + TryAdd("System.Data.Objects.SqlClient.SqlSpatialFunctions, System.Data.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 1); - //EF6,System.Data.Entity.DbFunctionAttribute - TryAdd("System.Data.Entity.Core.Objects.EntityFunctions, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 2); - TryAdd("System.Data.Entity.DbFunctions, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 2); - TryAdd("System.Data.Entity.Spatial.DbGeography, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 2); - TryAdd("System.Data.Entity.SqlServer.SqlFunctions, EntityFramework.SqlServer, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 2); - TryAdd("System.Data.Entity.SqlServer.SqlSpatialFunctions, EntityFramework.SqlServer, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 2); + //EF6,System.Data.Entity.DbFunctionAttribute + TryAdd("System.Data.Entity.Core.Objects.EntityFunctions, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 2); + TryAdd("System.Data.Entity.DbFunctions, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 2); + TryAdd("System.Data.Entity.Spatial.DbGeography, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 2); + TryAdd("System.Data.Entity.SqlServer.SqlFunctions, EntityFramework.SqlServer, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 2); + TryAdd("System.Data.Entity.SqlServer.SqlSpatialFunctions, EntityFramework.SqlServer, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 2); #endif #if NETSTANDARD2_0 - TryAdd($"Microsoft.EntityFrameworkCore.DynamicLinq.DynamicFunctions, Microsoft.EntityFrameworkCore.DynamicLinq, Version={Version}, Culture=neutral, PublicKeyToken=974e7e1b462f3693", 3); + TryAdd($"Microsoft.EntityFrameworkCore.DynamicLinq.DynamicFunctions, Microsoft.EntityFrameworkCore.DynamicLinq, Version={Version}, Culture=neutral, PublicKeyToken=974e7e1b462f3693", 3); #endif - } + } - private static void TryAdd(string typeName, int x) + private static void TryAdd(string typeName, int x) + { + try { - try + var efType = Type.GetType(typeName); + if (efType != null) { - Type? efType = Type.GetType(typeName); - if (efType != null) - { - PredefinedTypes.Add(efType, x); - } - } - catch - { - // in case of exception, do not add + PredefinedTypes.Add(efType, x); } } - - public static bool IsPredefinedType(ParsingConfig config, Type type) + catch { - Check.NotNull(config, nameof(config)); - Check.NotNull(type, nameof(type)); + // in case of exception, do not add + } + } - var nonNullableType = TypeHelper.GetNonNullableType(type); - if (PredefinedTypes.ContainsKey(nonNullableType)) - { - return true; - } + public static bool IsPredefinedType(ParsingConfig config, Type type) + { + Check.NotNull(config); + Check.NotNull(type); - return config.CustomTypeProvider != null && - (config.CustomTypeProvider.GetCustomTypes().Contains(type) || config.CustomTypeProvider.GetCustomTypes().Contains(nonNullableType)); + var nonNullableType = TypeHelper.GetNonNullableType(type); + if (PredefinedTypes.ContainsKey(nonNullableType)) + { + return true; } + + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract + return config.CustomTypeProvider != null && + (config.CustomTypeProvider.GetCustomTypes().Contains(type) || config.CustomTypeProvider.GetCustomTypes().Contains(nonNullableType)); } -} +} \ No newline at end of file diff --git a/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs b/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs index 3bd1332c..83d99980 100644 --- a/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs +++ b/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs @@ -8,1530 +8,1549 @@ using System.Reflection; using System.Runtime.CompilerServices; using FluentAssertions; +using Moq; using NFluent; using Xunit; -namespace System.Linq.Dynamic.Core.Tests +namespace System.Linq.Dynamic.Core.Tests; + +public class DynamicExpressionParserTests { - public class DynamicExpressionParserTests + public class Foo { - public class Foo - { - public Foo FooValue { get; set; } + public Foo FooValue { get; set; } + + public string Zero() => null; + + public string One(int x) => null; - public string Zero() => null; + public string Two(int x, int y) => null; + } + + private class MyClass + { + public List MyStrings { get; set; } - public string One(int x) => null; + public List MyClasses { get; set; } - public string Two(int x, int y) => null; + public int Foo() + { + return 42; } - private class MyClass + public void Bar() { - public List MyStrings { get; set; } + Name = nameof(Foo); + } - public List MyClasses { get; set; } + public string Name { get; set; } - public int Foo() - { - return 42; - } + public MyClass Child { get; set; } + } - public void Bar() - { - Name = nameof(Foo); - } + private class MyClassCustomTypeProvider : DefaultDynamicLinqCustomTypeProvider + { + public override HashSet GetCustomTypes() + { + var customTypes = base.GetCustomTypes(); + customTypes.Add(typeof(MyClass)); + return customTypes; + } + } - public string Name { get; set; } + private class ComplexParseLambda1Result + { + public int? Age; + public int TotalIncome; + public string Name; + } - public MyClass Child { get; set; } - } + [DynamicLinqType] + public class ComplexParseLambda3Result + { + public int? Age { get; set; } + public int TotalIncome { get; set; } + } + + public class CustomClassWithStaticMethod + { + public static int GetAge(int x) => x; + } - private class MyClassCustomTypeProvider : DefaultDynamicLinqCustomTypeProvider + public class CustomTextClass + { + public CustomTextClass(string origin) { - public override HashSet GetCustomTypes() - { - var customTypes = base.GetCustomTypes(); - customTypes.Add(typeof(MyClass)); - return customTypes; - } + Origin = origin; } - private class ComplexParseLambda1Result + public string Origin { get; } + + public static implicit operator string(CustomTextClass customTextValue) { - public int? Age; - public int TotalIncome; - public string Name; + return customTextValue?.Origin; } - [DynamicLinqType] - public class ComplexParseLambda3Result + public static implicit operator CustomTextClass(string origin) { - public int? Age { get; set; } - public int TotalIncome { get; set; } + return new CustomTextClass(origin); } - public class CustomClassWithStaticMethod + public override string ToString() { - public static int GetAge(int x) => x; + return Origin; } + } - public class CustomTextClass + public class CustomClassWithOneWayImplicitConversion + { + public CustomClassWithOneWayImplicitConversion(string origin) { - public CustomTextClass(string origin) - { - Origin = origin; - } - - public string Origin { get; } - - public static implicit operator string(CustomTextClass customTextValue) - { - return customTextValue?.Origin; - } + Origin = origin; + } - public static implicit operator CustomTextClass(string origin) - { - return new CustomTextClass(origin); - } + public string Origin { get; } - public override string ToString() - { - return Origin; - } + public static implicit operator CustomClassWithOneWayImplicitConversion(string origin) + { + return new CustomClassWithOneWayImplicitConversion(origin); } - public class CustomClassWithOneWayImplicitConversion + public override string ToString() { - public CustomClassWithOneWayImplicitConversion(string origin) - { - Origin = origin; - } - - public string Origin { get; } - - public static implicit operator CustomClassWithOneWayImplicitConversion(string origin) - { - return new CustomClassWithOneWayImplicitConversion(origin); - } - - public override string ToString() - { - return Origin; - } + return Origin; } + } - public class CustomClassWithReversedImplicitConversion + public class CustomClassWithReversedImplicitConversion + { + public CustomClassWithReversedImplicitConversion(string origin) { - public CustomClassWithReversedImplicitConversion(string origin) - { - Origin = origin; - } + Origin = origin; + } - public string Origin { get; } + public string Origin { get; } - public static implicit operator string(CustomClassWithReversedImplicitConversion origin) - { - return origin.ToString(); - } + public static implicit operator string(CustomClassWithReversedImplicitConversion origin) + { + return origin.ToString(); + } - public override string ToString() - { - return Origin; - } + public override string ToString() + { + return Origin; } + } - public class CustomClassWithValueTypeImplicitConversion + public class CustomClassWithValueTypeImplicitConversion + { + public CustomClassWithValueTypeImplicitConversion(int origin) { - public CustomClassWithValueTypeImplicitConversion(int origin) - { - Origin = origin; - } + Origin = origin; + } - public int Origin { get; } + public int Origin { get; } - public static implicit operator CustomClassWithValueTypeImplicitConversion(int origin) - { - return new CustomClassWithValueTypeImplicitConversion(origin); - } + public static implicit operator CustomClassWithValueTypeImplicitConversion(int origin) + { + return new CustomClassWithValueTypeImplicitConversion(origin); + } - public override string ToString() - { - return Origin.ToString(); - } + public override string ToString() + { + return Origin.ToString(); } + } - public class CustomClassWithReversedValueTypeImplicitConversion + public class CustomClassWithReversedValueTypeImplicitConversion + { + public CustomClassWithReversedValueTypeImplicitConversion(int origin) { - public CustomClassWithReversedValueTypeImplicitConversion(int origin) - { - Origin = origin; - } + Origin = origin; + } - public int Origin { get; } + public int Origin { get; } - public static implicit operator int(CustomClassWithReversedValueTypeImplicitConversion origin) - { - return origin.Origin; - } + public static implicit operator int(CustomClassWithReversedValueTypeImplicitConversion origin) + { + return origin.Origin; + } - public override string ToString() - { - return Origin.ToString(); - } + public override string ToString() + { + return Origin.ToString(); } + } - public class TestImplicitConversionContainer + public class TestImplicitConversionContainer + { + public TestImplicitConversionContainer( + CustomClassWithOneWayImplicitConversion oneWay, + CustomClassWithReversedImplicitConversion reversed, + CustomClassWithValueTypeImplicitConversion valueType, + CustomClassWithReversedValueTypeImplicitConversion reversedValueType) { - public TestImplicitConversionContainer( - CustomClassWithOneWayImplicitConversion oneWay, - CustomClassWithReversedImplicitConversion reversed, - CustomClassWithValueTypeImplicitConversion valueType, - CustomClassWithReversedValueTypeImplicitConversion reversedValueType) - { - OneWay = oneWay; - Reversed = reversed; - ValueType = valueType; - ReversedValueType = reversedValueType; - } + OneWay = oneWay; + Reversed = reversed; + ValueType = valueType; + ReversedValueType = reversedValueType; + } - public CustomClassWithOneWayImplicitConversion OneWay { get; } + public CustomClassWithOneWayImplicitConversion OneWay { get; } - public CustomClassWithReversedImplicitConversion Reversed { get; } + public CustomClassWithReversedImplicitConversion Reversed { get; } - public CustomClassWithValueTypeImplicitConversion ValueType { get; } + public CustomClassWithValueTypeImplicitConversion ValueType { get; } - public CustomClassWithReversedValueTypeImplicitConversion ReversedValueType { get; } - } + public CustomClassWithReversedValueTypeImplicitConversion ReversedValueType { get; } + } - public class TextHolder + public class TextHolder + { + public TextHolder(string name, CustomTextClass note) { - public TextHolder(string name, CustomTextClass note) - { - Name = name; - Note = note; - } + Name = name; + Note = note; + } - public string Name { get; } + public string Name { get; } - public CustomTextClass Note { get; } + public CustomTextClass Note { get; } - public override string ToString() - { - return Name + " (" + Note + ")"; - } + public override string ToString() + { + return Name + " (" + Note + ")"; } + } - public static class StaticHelper + public static class StaticHelper + { + public static Guid? GetGuid(string name) { - public static Guid? GetGuid(string name) - { - return Guid.NewGuid(); - } + return Guid.NewGuid(); } + } - public class TestCustomTypeProvider : AbstractDynamicLinqCustomTypeProvider, IDynamicLinkCustomTypeProvider - { - private HashSet _customTypes; + public class TestCustomTypeProvider : AbstractDynamicLinqCustomTypeProvider, IDynamicLinkCustomTypeProvider + { + private HashSet _customTypes; - public virtual HashSet GetCustomTypes() + public virtual HashSet GetCustomTypes() + { + if (_customTypes != null) { - if (_customTypes != null) - { - return _customTypes; - } - - _customTypes = new HashSet(FindTypesMarkedWithDynamicLinqTypeAttribute(new[] { GetType().GetTypeInfo().Assembly })) - { - typeof(CustomClassWithStaticMethod), - typeof(StaticHelper) - }; return _customTypes; } - public Dictionary> GetExtensionMethods() + _customTypes = new HashSet(FindTypesMarkedWithDynamicLinqTypeAttribute(new[] { GetType().GetTypeInfo().Assembly })) { - var types = GetCustomTypes(); + typeof(CustomClassWithStaticMethod), + typeof(StaticHelper) + }; + return _customTypes; + } - var list = new List>(); + public Dictionary> GetExtensionMethods() + { + var types = GetCustomTypes(); - foreach (var type in types) - { - var extensionMethods = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) - .Where(x => x.IsDefined(typeof(ExtensionAttribute), false)).ToList(); + var list = new List>(); - extensionMethods.ForEach(x => list.Add(new Tuple(x.GetParameters()[0].ParameterType, x))); - } + foreach (var type in types) + { + var extensionMethods = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) + .Where(x => x.IsDefined(typeof(ExtensionAttribute), false)).ToList(); - return list.GroupBy(x => x.Item1, tuple => tuple.Item2).ToDictionary(key => key.Key, methods => methods.ToList()); + extensionMethods.ForEach(x => list.Add(new Tuple(x.GetParameters()[0].ParameterType, x))); } - public Type ResolveType(string typeName) - { - return Type.GetType(typeName); - } + return list.GroupBy(x => x.Item1, tuple => tuple.Item2).ToDictionary(key => key.Key, methods => methods.ToList()); + } - public Type ResolveTypeBySimpleName(string typeName) - { - var assemblies = AppDomain.CurrentDomain.GetAssemblies(); - return ResolveTypeBySimpleName(assemblies, typeName); - } + public Type ResolveType(string typeName) + { + return Type.GetType(typeName); } - [Fact] - public void DynamicExpressionParser_ParseLambda_UseParameterizedNamesInDynamicQuery_false_String() + public Type ResolveTypeBySimpleName(string typeName) { - // Assign - var config = new ParsingConfig - { - UseParameterizedNamesInDynamicQuery = false - }; + var assemblies = AppDomain.CurrentDomain.GetAssemblies(); + return ResolveTypeBySimpleName(assemblies, typeName); + } + } - // Act - var expression = DynamicExpressionParser.ParseLambda(config, true, "s => s == \"x\""); + [Fact] + public void DynamicExpressionParser_ParseLambda_UseParameterizedNamesInDynamicQuery_false_String() + { + // Assign + var config = new ParsingConfig + { + UseParameterizedNamesInDynamicQuery = false + }; - // Assert - ConstantExpression constantExpression = (ConstantExpression)((BinaryExpression)expression.Body).Right; - object value = constantExpression.Value; + // Act + var expression = DynamicExpressionParser.ParseLambda(config, true, "s => s == \"x\""); - Check.That(value).IsEqualTo("x"); - } + // Assert + ConstantExpression constantExpression = (ConstantExpression)((BinaryExpression)expression.Body).Right; + object value = constantExpression.Value; - [Fact] - public void DynamicExpressionParser_ParseLambda_UseParameterizedNamesInDynamicQuery_false_DateTime() + Check.That(value).IsEqualTo("x"); + } + + [Fact] + public void DynamicExpressionParser_ParseLambda_UseParameterizedNamesInDynamicQuery_false_DateTime() + { + // Assign + var config = new ParsingConfig { - // Assign - var config = new ParsingConfig - { - UseParameterizedNamesInDynamicQuery = false - }; + UseParameterizedNamesInDynamicQuery = false + }; - // Act - var expression = DynamicExpressionParser.ParseLambda(config, true, "D == \"2022-03-02\""); + // Act + var expression = DynamicExpressionParser.ParseLambda(config, true, "D == \"2022-03-02\""); - // Assert - ConstantExpression constantExpression = (ConstantExpression)((BinaryExpression)expression.Body).Right; - object value = constantExpression.Value; + // Assert + ConstantExpression constantExpression = (ConstantExpression)((BinaryExpression)expression.Body).Right; + object value = constantExpression.Value; - Check.That(value).IsEqualTo(new DateTime(2022, 3, 2)); - } + Check.That(value).IsEqualTo(new DateTime(2022, 3, 2)); + } - [Fact] - public void DynamicExpressionParser_ParseLambda_UseParameterizedNamesInDynamicQuery_true_Int() + [Fact] + public void DynamicExpressionParser_ParseLambda_UseParameterizedNamesInDynamicQuery_true_Int() + { + // Assign + var config = new ParsingConfig { - // Assign - var config = new ParsingConfig - { - UseParameterizedNamesInDynamicQuery = true - }; + UseParameterizedNamesInDynamicQuery = true + }; - // Act - var expression = DynamicExpressionParser.ParseLambda(config, false, "Id = 42"); - var expressionAsString = expression.ToString(); + // Act + var expression = DynamicExpressionParser.ParseLambda(config, false, "Id = 42"); + var expressionAsString = expression.ToString(); - // Assert - expressionAsString.Should().Be("Param_0 => (Param_0.Id == value(System.Linq.Dynamic.Core.Parser.WrappedValue`1[System.Int32]).Value)"); + // Assert + expressionAsString.Should().Be("Param_0 => (Param_0.Id == value(System.Linq.Dynamic.Core.Parser.WrappedValue`1[System.Int32]).Value)"); - ConstantExpression constantExpression = (ConstantExpression)((MemberExpression)((BinaryExpression)expression.Body).Right).Expression; - var wrappedObj = constantExpression!.Value; + ConstantExpression constantExpression = (ConstantExpression)((MemberExpression)((BinaryExpression)expression.Body).Right).Expression; + var wrappedObj = constantExpression!.Value; - PropertyInfo propertyInfo = wrappedObj!.GetType().GetProperty("Value", BindingFlags.Instance | BindingFlags.Public); - object value = propertyInfo!.GetValue(wrappedObj); + PropertyInfo propertyInfo = wrappedObj!.GetType().GetProperty("Value", BindingFlags.Instance | BindingFlags.Public); + object value = propertyInfo!.GetValue(wrappedObj); - Check.That(value).IsEqualTo(42); - } + Check.That(value).IsEqualTo(42); + } - [Fact(Skip = "Issue 645")] - public void DynamicExpressionParser_ParseLambda_UseParameterizedNamesInDynamicQuery_true_DateTime() + [Fact(Skip = "Issue 645")] + public void DynamicExpressionParser_ParseLambda_UseParameterizedNamesInDynamicQuery_true_DateTime() + { + // Assign + var config = new ParsingConfig { - // Assign - var config = new ParsingConfig - { - UseParameterizedNamesInDynamicQuery = true - }; + UseParameterizedNamesInDynamicQuery = true + }; - // Act - var expression = DynamicExpressionParser.ParseLambda(config, false, "D = \"2022-11-16\""); - var expressionAsString = expression.ToString(); + // Act + var expression = DynamicExpressionParser.ParseLambda(config, false, "D = \"2022-11-16\""); + var expressionAsString = expression.ToString(); - // Assert - expressionAsString.Should().Be("Param_0 => (Param_0.D == value(System.Linq.Dynamic.Core.Parser.WrappedValue`1[System.DateTime]).Value)"); + // Assert + expressionAsString.Should().Be("Param_0 => (Param_0.D == value(System.Linq.Dynamic.Core.Parser.WrappedValue`1[System.DateTime]).Value)"); - ConstantExpression constantExpression = (ConstantExpression)((MemberExpression)((BinaryExpression)expression.Body).Right).Expression; - var wrappedObj = constantExpression!.Value; + ConstantExpression constantExpression = (ConstantExpression)((MemberExpression)((BinaryExpression)expression.Body).Right).Expression; + var wrappedObj = constantExpression!.Value; - PropertyInfo propertyInfo = wrappedObj!.GetType().GetProperty("Value", BindingFlags.Instance | BindingFlags.Public); - object value = propertyInfo!.GetValue(wrappedObj); + PropertyInfo propertyInfo = wrappedObj!.GetType().GetProperty("Value", BindingFlags.Instance | BindingFlags.Public); + object value = propertyInfo!.GetValue(wrappedObj); - Check.That(value).IsEqualTo(new DateTime(2022, 11, 16)); - } + Check.That(value).IsEqualTo(new DateTime(2022, 11, 16)); + } - [Theory] - [InlineData("NullableIntValue", "42")] - [InlineData("NullableDoubleValue", "42.23")] - public void DynamicExpressionParser_ParseLambda_UseParameterizedNamesInDynamicQuery_ForNullableProperty_true(string propName, string valueString) + [Theory] + [InlineData("NullableIntValue", "42")] + [InlineData("NullableDoubleValue", "42.23")] + public void DynamicExpressionParser_ParseLambda_UseParameterizedNamesInDynamicQuery_ForNullableProperty_true(string propName, string valueString) + { + // Assign + var culture = CultureInfo.CreateSpecificCulture("en-US"); + var config = new ParsingConfig { - // Assign - var culture = CultureInfo.CreateSpecificCulture("en-US"); - var config = new ParsingConfig - { - UseParameterizedNamesInDynamicQuery = true, - NumberParseCulture = culture - }; - - // Act - var expression = DynamicExpressionParser.ParseLambda(config, false, $"{propName} = {valueString}"); - var expressionAsString = expression.ToString(); + UseParameterizedNamesInDynamicQuery = true, + NumberParseCulture = culture + }; - // Assert - var queriedProp = typeof(SimpleValuesModel).GetProperty(propName, BindingFlags.Instance | BindingFlags.Public)!; - var queriedPropType = queriedProp.PropertyType; - var queriedPropUnderlyingType = Nullable.GetUnderlyingType(queriedPropType); + // Act + var expression = DynamicExpressionParser.ParseLambda(config, false, $"{propName} = {valueString}"); + var expressionAsString = expression.ToString(); - Check.That(expressionAsString).IsEqualTo($"Param_0 => (Param_0.{propName} == {ExpressionString.NullableConversion($"value(System.Linq.Dynamic.Core.Parser.WrappedValue`1[{queriedPropUnderlyingType}]).Value")})"); - dynamic constantExpression = (ConstantExpression)((MemberExpression)((UnaryExpression)((BinaryExpression)expression.Body).Right).Operand).Expression; - object wrapperObj = constantExpression.Value; + // Assert + var queriedProp = typeof(SimpleValuesModel).GetProperty(propName, BindingFlags.Instance | BindingFlags.Public)!; + var queriedPropType = queriedProp.PropertyType; + var queriedPropUnderlyingType = Nullable.GetUnderlyingType(queriedPropType); - var propertyInfo = wrapperObj.GetType().GetProperty("Value", BindingFlags.Instance | BindingFlags.Public)!; - var value = propertyInfo.GetValue(wrapperObj); + Check.That(expressionAsString).IsEqualTo($"Param_0 => (Param_0.{propName} == {ExpressionString.NullableConversion($"value(System.Linq.Dynamic.Core.Parser.WrappedValue`1[{queriedPropUnderlyingType}]).Value")})"); + dynamic constantExpression = (ConstantExpression)((MemberExpression)((UnaryExpression)((BinaryExpression)expression.Body).Right).Operand).Expression; + object wrapperObj = constantExpression.Value; - value.Should().Be(Convert.ChangeType(valueString, Nullable.GetUnderlyingType(queriedPropType) ?? queriedPropType, culture)); - } + var propertyInfo = wrapperObj.GetType().GetProperty("Value", BindingFlags.Instance | BindingFlags.Public)!; + var value = propertyInfo.GetValue(wrapperObj); - [Theory] - [InlineData("Where(x => x.SnowflakeId == {0})")] - [InlineData("Where(x => x.SnowflakeId = {0})")] - public void DynamicExpressionParser_ParseLambda_WithStructWithEquality(string query) - { - // Assign - var testList = User.GenerateSampleModels(51); - var qry = testList.AsQueryable(); + value.Should().Be(Convert.ChangeType(valueString, Nullable.GetUnderlyingType(queriedPropType) ?? queriedPropType, culture)); + } - // Act - var expectedX = (ulong)long.MaxValue + 3; + [Theory] + [InlineData("Where(x => x.SnowflakeId == {0})")] + [InlineData("Where(x => x.SnowflakeId = {0})")] + public void DynamicExpressionParser_ParseLambda_WithStructWithEquality(string query) + { + // Assign + var testList = User.GenerateSampleModels(51); + var qry = testList.AsQueryable(); - query = string.Format(query, expectedX); - var expression = DynamicExpressionParser.ParseLambda(qry.GetType(), null, query); - var del = expression.Compile(); - var result = del.DynamicInvoke(qry) as IEnumerable; + // Act + var expectedX = (ulong)long.MaxValue + 3; - var expected = qry.Where(gg => gg.SnowflakeId == new SnowflakeId(expectedX)).ToList(); + query = string.Format(query, expectedX); + var expression = DynamicExpressionParser.ParseLambda(qry.GetType(), null, query); + var del = expression.Compile(); + var result = del.DynamicInvoke(qry) as IEnumerable; - // Assert - Check.That(result).IsNotNull(); - Check.That(result).HasSize(expected.Count); - Check.That(result.ToArray()[0]).Equals(expected[0]); - } + var expected = qry.Where(gg => gg.SnowflakeId == new SnowflakeId(expectedX)).ToList(); - [Fact] - public void DynamicExpressionParser_ParseLambda_ToList() - { - // Arrange - var testList = User.GenerateSampleModels(51); - var qry = testList.AsQueryable(); - - // Act - var query = "OrderBy(gg => gg.Income).ToList()"; - var expression = DynamicExpressionParser.ParseLambda(qry.GetType(), null, query); - var del = expression.Compile(); - var result = del.DynamicInvoke(qry) as IEnumerable; - - var expected = qry.OrderBy(gg => gg.Income).ToList(); - - // Assert - Check.That(result).IsNotNull(); - Check.That(result).HasSize(expected.Count); - Check.That(result.ToArray()[0]).Equals(expected[0]); - } + // Assert + Check.That(result).IsNotNull(); + Check.That(result).HasSize(expected.Count); + Check.That(result.ToArray()[0]).Equals(expected[0]); + } - [Fact] - public void DynamicExpressionParser_ParseLambda_Complex_1() - { - // Arrange - var testList = User.GenerateSampleModels(51); - var qry = testList.AsQueryable(); + [Fact] + public void DynamicExpressionParser_ParseLambda_ToList() + { + // Arrange + var testList = User.GenerateSampleModels(51); + var qry = testList.AsQueryable(); + + // Act + var query = "OrderBy(gg => gg.Income).ToList()"; + var expression = DynamicExpressionParser.ParseLambda(qry.GetType(), null, query); + var del = expression.Compile(); + var result = del.DynamicInvoke(qry) as IEnumerable; + + var expected = qry.OrderBy(gg => gg.Income).ToList(); + + // Assert + Check.That(result).IsNotNull(); + Check.That(result).HasSize(expected.Count); + Check.That(result.ToArray()[0]).Equals(expected[0]); + } - var externals = new Dictionary - { - {"Users", qry} - }; + [Fact] + public void DynamicExpressionParser_ParseLambda_Complex_1() + { + // Arrange + var testList = User.GenerateSampleModels(51); + var qry = testList.AsQueryable(); + + var externals = new Dictionary + { + {"Users", qry} + }; + + // Act + var query = + "Users.GroupBy(x => new { x.Profile.Age }).OrderBy(gg => gg.Key.Age).Select(j => new (j.Key.Age, j.Sum(k => k.Income) As TotalIncome))"; + var expression = DynamicExpressionParser.ParseLambda(null, query, externals); + var del = expression.Compile(); + var result = del.DynamicInvoke() as IEnumerable; + + var expected = qry.GroupBy(x => new { x.Profile.Age }).OrderBy(gg => gg.Key.Age) + .Select(j => new { j.Key.Age, TotalIncome = j.Sum(k => k.Income) }) + .Select(c => new ComplexParseLambda1Result { Age = c.Age, TotalIncome = c.TotalIncome }).Cast() + .ToArray(); + + // Assert + Check.That(result).IsNotNull(); + Check.That(result).HasSize(expected.Length); + Check.That(result.ToArray()[0]).Equals(expected[0]); + } - // Act - var query = - "Users.GroupBy(x => new { x.Profile.Age }).OrderBy(gg => gg.Key.Age).Select(j => new (j.Key.Age, j.Sum(k => k.Income) As TotalIncome))"; - var expression = DynamicExpressionParser.ParseLambda(null, query, externals); - var del = expression.Compile(); - var result = del.DynamicInvoke() as IEnumerable; - - var expected = qry.GroupBy(x => new { x.Profile.Age }).OrderBy(gg => gg.Key.Age) - .Select(j => new { j.Key.Age, TotalIncome = j.Sum(k => k.Income) }) - .Select(c => new ComplexParseLambda1Result { Age = c.Age, TotalIncome = c.TotalIncome }).Cast() - .ToArray(); - - // Assert - Check.That(result).IsNotNull(); - Check.That(result).HasSize(expected.Length); - Check.That(result.ToArray()[0]).Equals(expected[0]); - } + [Fact] + public void DynamicExpressionParser_ParseLambda_Complex_2() + { + // Arrange + var testList = User.GenerateSampleModels(51); + var qry = testList.AsQueryable(); + + // Act + var query = + "GroupBy(x => new { x.Profile.Age }, it).OrderBy(gg => gg.Key.Age).Select(j => new (j.Key.Age, j.Sum(k => k.Income) As TotalIncome))"; + var expression = DynamicExpressionParser.ParseLambda(qry.GetType(), null, query); + var del = expression.Compile(); + var result = del.DynamicInvoke(qry) as IEnumerable; + + var expected = qry.GroupBy(x => new { x.Profile.Age }, x => x).OrderBy(gg => gg.Key.Age) + .Select(j => new { j.Key.Age, TotalIncome = j.Sum(k => k.Income) }) + .Select(c => new ComplexParseLambda1Result { Age = c.Age, TotalIncome = c.TotalIncome }).Cast() + .ToArray(); + + // Assert + Check.That(result).IsNotNull(); + Check.That(result).HasSize(expected.Length); + Check.That(result.ToArray()[0]).Equals(expected[0]); + } - [Fact] - public void DynamicExpressionParser_ParseLambda_Complex_2() + [Fact] + public void DynamicExpressionParser_ParseLambda_Complex_3() + { + var config = new ParsingConfig { - // Arrange - var testList = User.GenerateSampleModels(51); - var qry = testList.AsQueryable(); - - // Act - var query = - "GroupBy(x => new { x.Profile.Age }, it).OrderBy(gg => gg.Key.Age).Select(j => new (j.Key.Age, j.Sum(k => k.Income) As TotalIncome))"; - var expression = DynamicExpressionParser.ParseLambda(qry.GetType(), null, query); - var del = expression.Compile(); - var result = del.DynamicInvoke(qry) as IEnumerable; - - var expected = qry.GroupBy(x => new { x.Profile.Age }, x => x).OrderBy(gg => gg.Key.Age) - .Select(j => new { j.Key.Age, TotalIncome = j.Sum(k => k.Income) }) - .Select(c => new ComplexParseLambda1Result { Age = c.Age, TotalIncome = c.TotalIncome }).Cast() - .ToArray(); - - // Assert - Check.That(result).IsNotNull(); - Check.That(result).HasSize(expected.Length); - Check.That(result.ToArray()[0]).Equals(expected[0]); - } + CustomTypeProvider = new TestCustomTypeProvider() + }; - [Fact] - public void DynamicExpressionParser_ParseLambda_Complex_3() + // Arrange + var testList = User.GenerateSampleModels(51); + var qry = testList.AsQueryable(); + + var externals = new Dictionary { - var config = new ParsingConfig - { - CustomTypeProvider = new TestCustomTypeProvider() - }; + {"Users", qry} + }; - // Arrange - var testList = User.GenerateSampleModels(51); - var qry = testList.AsQueryable(); + // Act + var stringExpression = + "Users.GroupBy(x => new { x.Profile.Age }).OrderBy(gg => gg.Key.Age).Select(j => new System.Linq.Dynamic.Core.Tests.DynamicExpressionParserTests+ComplexParseLambda3Result{j.Key.Age, j.Sum(k => k.Income) As TotalIncome})"; + var expression = + DynamicExpressionParser.ParseLambda(config, null, stringExpression, externals); + var del = expression.Compile(); + var result = del.DynamicInvoke() as IEnumerable; - var externals = new Dictionary - { - {"Users", qry} - }; + var expected = qry.GroupBy(x => new { x.Profile.Age }).OrderBy(gg => gg.Key.Age) + .Select(j => new ComplexParseLambda3Result { Age = j.Key.Age, TotalIncome = j.Sum(k => k.Income) }) + .Cast().ToArray(); - // Act - var stringExpression = - "Users.GroupBy(x => new { x.Profile.Age }).OrderBy(gg => gg.Key.Age).Select(j => new System.Linq.Dynamic.Core.Tests.DynamicExpressionParserTests+ComplexParseLambda3Result{j.Key.Age, j.Sum(k => k.Income) As TotalIncome})"; - var expression = - DynamicExpressionParser.ParseLambda(config, null, stringExpression, externals); - var del = expression.Compile(); - var result = del.DynamicInvoke() as IEnumerable; - - var expected = qry.GroupBy(x => new { x.Profile.Age }).OrderBy(gg => gg.Key.Age) - .Select(j => new ComplexParseLambda3Result { Age = j.Key.Age, TotalIncome = j.Sum(k => k.Income) }) - .Cast().ToArray(); - - // Assert - Check.That(result).IsNotNull(); - Check.That(result).HasSize(expected.Length); - Check.That(result.ToArray()[0]).Equals(expected[0]); - } + // Assert + Check.That(result).IsNotNull(); + Check.That(result).HasSize(expected.Length); + Check.That(result.ToArray()[0]).Equals(expected[0]); + } + + [Fact] + public void DynamicExpressionParser_ParseLambda_Select_1() + { + // Arrange + var testList = User.GenerateSampleModels(51); + var qry = testList.AsQueryable(); - [Fact] - public void DynamicExpressionParser_ParseLambda_Select_1() + var externals = new Dictionary { - // Arrange - var testList = User.GenerateSampleModels(51); - var qry = testList.AsQueryable(); + {"Users", qry} + }; - var externals = new Dictionary - { - {"Users", qry} - }; + // Act + var query = "Users.Select(j => new User(j.Income As Income))"; + var expression = DynamicExpressionParser.ParseLambda(null, query, externals); + var del = expression.Compile(); + var result = del.DynamicInvoke(); - // Act - var query = "Users.Select(j => new User(j.Income As Income))"; - var expression = DynamicExpressionParser.ParseLambda(null, query, externals); - var del = expression.Compile(); - var result = del.DynamicInvoke(); + // Assert + Assert.NotNull(result); + } - // Assert - Assert.NotNull(result); - } + [Fact] + public void DynamicExpressionParser_ParseLambda_Select_2() + { + // Arrange + var testList = User.GenerateSampleModels(5); + var qry = testList.AsQueryable(); - [Fact] - public void DynamicExpressionParser_ParseLambda_Select_2() + var externals = new Dictionary { - // Arrange - var testList = User.GenerateSampleModels(5); - var qry = testList.AsQueryable(); - - var externals = new Dictionary - { - {"Users", qry} - }; + {"Users", qry} + }; - // Act - var query = "Users.Select(j => j)"; - var expression = DynamicExpressionParser.ParseLambda(null, query, externals); - var del = expression.Compile(); - var result = del.DynamicInvoke(); + // Act + var query = "Users.Select(j => j)"; + var expression = DynamicExpressionParser.ParseLambda(null, query, externals); + var del = expression.Compile(); + var result = del.DynamicInvoke(); - // Assert - Assert.NotNull(result); - } + // Assert + Assert.NotNull(result); + } - // https://github.com/StefH/System.Linq.Dynamic.Core/issues/58 - [Fact] - public void DynamicExpressionParser_ParseLambda_Issue58() + // https://github.com/StefH/System.Linq.Dynamic.Core/issues/58 + [Fact] + public void DynamicExpressionParser_ParseLambda_Issue58() + { + // Arrange + var customTypeProvider = new Mock(); + customTypeProvider.Setup(c => c.GetCustomTypes()).Returns(new HashSet { typeof(MyClass) }); + var config = new ParsingConfig { - var expressionParams = new[] - { - Expression.Parameter(typeof(MyClass), "myObj") - }; + CustomTypeProvider = customTypeProvider.Object + }; + var expressionParams = new[] + { + Expression.Parameter(typeof(MyClass), "myObj") + }; - var myClassInstance = new MyClass(); - var invokersMerge = new List { myClassInstance }; + var myClassInstance = new MyClass(); + var invokersMerge = new List { myClassInstance }; - var expression = - DynamicExpressionParser.ParseLambda(false, expressionParams, null, "myObj.Foo()"); - var del = expression.Compile(); - var result = del.DynamicInvoke(invokersMerge.ToArray()); + // Act + var expression = DynamicExpressionParser.ParseLambda(config, false, expressionParams, null, "myObj.Foo()"); + var del = expression.Compile(); + var result = del.DynamicInvoke(invokersMerge.ToArray()); - Check.That(result).Equals(42); - } + // Assert + Check.That(result).Equals(42); + } - [Fact] - public void DynamicExpressionParser_ParseLambda_DuplicateParameterNames_ThrowsException() + [Fact] + public void DynamicExpressionParser_ParseLambda_DuplicateParameterNames_ThrowsException() + { + // Arrange + var parameters = new[] { - // Arrange - var parameters = new[] - { - Expression.Parameter(typeof(int), "x"), - Expression.Parameter(typeof(int), "x") - }; + Expression.Parameter(typeof(int), "x"), + Expression.Parameter(typeof(int), "x") + }; - // Act and Assert - Check.ThatCode(() => DynamicExpressionParser.ParseLambda(parameters, typeof(bool), "it == 42")) - .Throws() - .WithMessage("The identifier 'x' was defined more than once"); - } + // Act and Assert + Check.ThatCode(() => DynamicExpressionParser.ParseLambda(parameters, typeof(bool), "it == 42")) + .Throws() + .WithMessage("The identifier 'x' was defined more than once"); + } - [Fact] - public void DynamicExpressionParser_ParseLambda_EmptyParameterList() - { - // Arrange - var pEmpty = new ParameterExpression[] { }; + [Fact] + public void DynamicExpressionParser_ParseLambda_EmptyParameterList() + { + // Arrange + var pEmpty = new ParameterExpression[] { }; - // Act - var @delegate = DynamicExpressionParser.ParseLambda(pEmpty, null, "1+2").Compile(); - var result = @delegate.DynamicInvoke() as int?; + // Act + var @delegate = DynamicExpressionParser.ParseLambda(pEmpty, null, "1+2").Compile(); + var result = @delegate.DynamicInvoke() as int?; - // Assert - Check.That(result).Equals(3); - } + // Assert + Check.That(result).Equals(3); + } - [Fact] - public void DynamicExpressionParser_ParseLambda_ParameterName() + [Fact] + public void DynamicExpressionParser_ParseLambda_ParameterName() + { + // Arrange + var parameters = new[] { - // Arrange - var parameters = new[] - { - Expression.Parameter(typeof(int), "x") - }; + Expression.Parameter(typeof(int), "x") + }; - // Assert - var expressionX = DynamicExpressionParser.ParseLambda(parameters, typeof(bool), "x == 42"); - var expressionIT = DynamicExpressionParser.ParseLambda(parameters, typeof(bool), "it == 42"); + // Assert + var expressionX = DynamicExpressionParser.ParseLambda(parameters, typeof(bool), "x == 42"); + var expressionIT = DynamicExpressionParser.ParseLambda(parameters, typeof(bool), "it == 42"); - // Assert - Assert.Equal(typeof(bool), expressionX.Body.Type); - Assert.Equal(typeof(bool), expressionIT.Body.Type); - } + // Assert + Assert.Equal(typeof(bool), expressionX.Body.Type); + Assert.Equal(typeof(bool), expressionIT.Body.Type); + } - [Fact] - public void DynamicExpressionParser_ParseLambda_ParameterName_Empty() + [Fact] + public void DynamicExpressionParser_ParseLambda_ParameterName_Empty() + { + // Arrange + var parameters = new[] { - // Arrange - var parameters = new[] - { - Expression.Parameter(typeof(int), "") - }; + Expression.Parameter(typeof(int), "") + }; - // Assert - var expression = DynamicExpressionParser.ParseLambda(parameters, typeof(bool), "it == 42"); + // Assert + var expression = DynamicExpressionParser.ParseLambda(parameters, typeof(bool), "it == 42"); - // Assert - Assert.Equal(typeof(bool), expression.Body.Type); - } + // Assert + Assert.Equal(typeof(bool), expression.Body.Type); + } - [Fact] - public void DynamicExpressionParser_ParseLambda_ParameterName_Null() + [Fact] + public void DynamicExpressionParser_ParseLambda_ParameterName_Null() + { + // Arrange + var parameters = new[] { - // Arrange - var parameters = new[] - { - Expression.Parameter(typeof(int), null) - }; + Expression.Parameter(typeof(int), null) + }; - // Assert - var expression = DynamicExpressionParser.ParseLambda(parameters, typeof(bool), "it == 42"); + // Assert + var expression = DynamicExpressionParser.ParseLambda(parameters, typeof(bool), "it == 42"); - // Assert - Assert.Equal(typeof(bool), expression.Body.Type); - } + // Assert + Assert.Equal(typeof(bool), expression.Body.Type); + } - [Fact] - public void DynamicExpressionParser_ParseLambda_ParameterExpressionMethodCall_ReturnsIntExpression() - { - var expression = DynamicExpressionParser.ParseLambda(true, - new[] { Expression.Parameter(typeof(int), "x") }, - typeof(int), - "x + 1"); - Assert.Equal(typeof(int), expression.Body.Type); - } + [Fact] + public void DynamicExpressionParser_ParseLambda_ParameterExpressionMethodCall_ReturnsIntExpression() + { + var expression = DynamicExpressionParser.ParseLambda(true, + new[] { Expression.Parameter(typeof(int), "x") }, + typeof(int), + "x + 1"); + Assert.Equal(typeof(int), expression.Body.Type); + } - public static object[][] Decimals() + public static object[][] Decimals() + { + return new object[][] { - return new object[][] - { - new object[] { "de-DE", "1m", 1f }, - new object[] { "de-DE", "-42,0m", -42m }, - new object[] { "de-DE", "3,215m", 3.215m }, + new object[] { "de-DE", "1m", 1f }, + new object[] { "de-DE", "-42,0m", -42m }, + new object[] { "de-DE", "3,215m", 3.215m }, - new object[] { null, "1m", 1f }, - new object[] { null, "-42.0m", -42m }, - new object[] { null, "3.215m", 3.215m } - }; - } - [Theory] - [MemberData(nameof(Decimals))] - public void DynamicExpressionParser_ParseLambda_Decimal(string culture, string expression, decimal expected) + new object[] { null, "1m", 1f }, + new object[] { null, "-42.0m", -42m }, + new object[] { null, "3.215m", 3.215m } + }; + } + [Theory] + [MemberData(nameof(Decimals))] + public void DynamicExpressionParser_ParseLambda_Decimal(string culture, string expression, decimal expected) + { + // Arrange + var config = new ParsingConfig(); + if (culture != null) { - // Arrange - var config = new ParsingConfig(); - if (culture != null) - { - config.NumberParseCulture = CultureInfo.CreateSpecificCulture(culture); - } - - var parameters = new ParameterExpression[0]; - - // Act - var lambda = DynamicExpressionParser.ParseLambda(config, parameters, typeof(decimal), expression); - var result = lambda.Compile().DynamicInvoke(); - - // Assert - result.Should().Be(expected); + config.NumberParseCulture = CultureInfo.CreateSpecificCulture(culture); } - public static object[][] Floats() - { - return new object[][] - { - new object[] { "de-DE", "1F", 1f }, - new object[] { "de-DE", "1f", 1f }, - new object[] { "de-DE", "-42f", -42d }, - new object[] { "de-DE", "3,215f", 3.215d }, - - new object[] { null, "1F", 1f }, - new object[] { null, "1f", 1f }, - new object[] { null, "-42f", -42d }, - new object[] { null, "3.215f", 3.215d }, - }; - } - [Theory] - [MemberData(nameof(Floats))] - public void DynamicExpressionParser_ParseLambda_Float(string culture, string expression, float expected) - { - // Arrange - var config = new ParsingConfig(); - if (culture != null) - { - config.NumberParseCulture = CultureInfo.CreateSpecificCulture(culture); - } + var parameters = new ParameterExpression[0]; - var parameters = new ParameterExpression[0]; + // Act + var lambda = DynamicExpressionParser.ParseLambda(config, parameters, typeof(decimal), expression); + var result = lambda.Compile().DynamicInvoke(); - // Act - var lambda = DynamicExpressionParser.ParseLambda(config, parameters, typeof(float), expression); - var result = lambda.Compile().DynamicInvoke(); - - // Assert - result.Should().Be(expected); - } + // Assert + result.Should().Be(expected); + } - public static IEnumerable Doubles() + public static object[][] Floats() + { + return new object[][] + { + new object[] { "de-DE", "1F", 1f }, + new object[] { "de-DE", "1f", 1f }, + new object[] { "de-DE", "-42f", -42d }, + new object[] { "de-DE", "3,215f", 3.215d }, + + new object[] { null, "1F", 1f }, + new object[] { null, "1f", 1f }, + new object[] { null, "-42f", -42d }, + new object[] { null, "3.215f", 3.215d }, + }; + } + [Theory] + [MemberData(nameof(Floats))] + public void DynamicExpressionParser_ParseLambda_Float(string culture, string expression, float expected) + { + // Arrange + var config = new ParsingConfig(); + if (culture != null) { - return new object[][] - { - new object[] { "de-DE", "1D", 1d }, - new object[] { "de-DE", "1d", 1d }, - new object[] { "de-DE", "-42d", -42d }, - new object[] { "de-DE", "3,215d", 3.215d }, - new object[] { "de-DE", "1,2345E-4", 0.00012345d }, - new object[] { "de-DE", "1,2345E4", 12345d }, - - new object[] { null, "1D", 1d }, - new object[] { null, "1d", 1d }, - new object[] { null, "-42d", -42d }, - new object[] { null, "3.215d", 3.215d }, - new object[] { null, "1.2345E-4", 0.00012345d }, - new object[] { null, "1.2345E4", 12345d } - }; + config.NumberParseCulture = CultureInfo.CreateSpecificCulture(culture); } - [Theory] - [MemberData(nameof(Doubles))] - public void DynamicExpressionParser_ParseLambda_Double(string culture, string expression, double expected) - { - // Arrange - var config = new ParsingConfig(); - if (culture != null) - { - config.NumberParseCulture = CultureInfo.CreateSpecificCulture(culture); - } - var parameters = new ParameterExpression[0]; + var parameters = new ParameterExpression[0]; - // Act - var lambda = DynamicExpressionParser.ParseLambda(config, parameters, typeof(double), expression); - var result = lambda.Compile().DynamicInvoke(); + // Act + var lambda = DynamicExpressionParser.ParseLambda(config, parameters, typeof(float), expression); + var result = lambda.Compile().DynamicInvoke(); - // Assert - result.Should().Be(expected); - } - - [Fact] - public void DynamicExpressionParser_ParseLambda_StringLiteral_ReturnsBooleanLambdaExpression() - { - var expression = DynamicExpressionParser.ParseLambda( - new[] { Expression.Parameter(typeof(string), "Property1") }, typeof(bool), "Property1 == \"test\""); - Assert.Equal(typeof(bool), expression.Body.Type); - } + // Assert + result.Should().Be(expected); + } - [Fact] - public void DynamicExpressionParser_ParseLambda_StringLiteralEmpty_ReturnsBooleanLambdaExpression() + public static IEnumerable Doubles() + { + return new object[][] + { + new object[] { "de-DE", "1D", 1d }, + new object[] { "de-DE", "1d", 1d }, + new object[] { "de-DE", "-42d", -42d }, + new object[] { "de-DE", "3,215d", 3.215d }, + new object[] { "de-DE", "1,2345E-4", 0.00012345d }, + new object[] { "de-DE", "1,2345E4", 12345d }, + + new object[] { null, "1D", 1d }, + new object[] { null, "1d", 1d }, + new object[] { null, "-42d", -42d }, + new object[] { null, "3.215d", 3.215d }, + new object[] { null, "1.2345E-4", 0.00012345d }, + new object[] { null, "1.2345E4", 12345d } + }; + } + [Theory] + [MemberData(nameof(Doubles))] + public void DynamicExpressionParser_ParseLambda_Double(string culture, string expression, double expected) + { + // Arrange + var config = new ParsingConfig(); + if (culture != null) { - var expression = DynamicExpressionParser.ParseLambda( - new[] { Expression.Parameter(typeof(string), "Property1") }, typeof(bool), "Property1 == \"\""); - Assert.Equal(typeof(bool), expression.Body.Type); + config.NumberParseCulture = CultureInfo.CreateSpecificCulture(culture); } - [Fact] - public void DynamicExpressionParser_ParseLambda_Config_StringLiteralEmpty_ReturnsBooleanLambdaExpression() - { - var config = new ParsingConfig(); - var expression = DynamicExpressionParser.ParseLambda( - new[] { Expression.Parameter(typeof(string), "Property1") }, typeof(bool), "Property1 == \"\""); - Assert.Equal(typeof(bool), expression.Body.Type); - } + var parameters = new ParameterExpression[0]; - [Fact] - public void DynamicExpressionParser_ParseLambda_StringLiteralEmbeddedQuote_ReturnsBooleanLambdaExpression() - { - // Act - var expression = DynamicExpressionParser.ParseLambda( - new[] { Expression.Parameter(typeof(string), "Property1") }, - typeof(bool), - string.Format("Property1 == {0}", "\"test \\\"string\"")); - - var rightValue = ((BinaryExpression)expression.Body).Right.ToString(); - Assert.Equal(typeof(bool), expression.Body.Type); - Assert.Equal("\"test \"string\"", rightValue); - } + // Act + var lambda = DynamicExpressionParser.ParseLambda(config, parameters, typeof(double), expression); + var result = lambda.Compile().DynamicInvoke(); - /// - /// @see https://github.com/StefH/System.Linq.Dynamic.Core/issues/294 - /// - [Fact(Skip = "Fails on EntityFramework.DynamicLinq.Tests with 'An unexpected exception occurred while binding a dynamic operation'")] - public void DynamicExpressionParser_ParseLambda_MultipleLambdas() - { - var users = new[] - { - new { name = "Juan", age = 25 }, - new { name = "Juan", age = 25 }, - new { name = "David", age = 12 }, - new { name = "Juan", age = 25 }, - new { name = "Juan", age = 4 }, - new { name = "Pedro", age = 2 }, - new { name = "Juan", age = 25 } - }.ToList(); - - IQueryable query; - - // One lambda - var res1 = "[{\"Key\":{\"name\":\"Juan\"},\"nativeAggregates\":{\"ageSum\":104},\"Grouping\":[{\"name\":\"Juan\",\"age\":25},{\"name\":\"Juan\",\"age\":25},{\"name\":\"Juan\",\"age\":25},{\"name\":\"Juan\",\"age\":4},{\"name\":\"Juan\",\"age\":25}]},{\"Key\":{\"name\":\"David\"},\"nativeAggregates\":{\"ageSum\":12},\"Grouping\":[{\"name\":\"David\",\"age\":12}]},{\"Key\":{\"name\":\"Pedro\"},\"nativeAggregates\":{\"ageSum\":2},\"Grouping\":[{\"name\":\"Pedro\",\"age\":2}]}]"; - query = users.AsQueryable(); - query = query.GroupBy("new(name as name)", "it"); - query = query.Select("new (it.Key as Key, new(it.Sum(x => x.age) as ageSum) as nativeAggregates, it as Grouping)"); - Assert.Equal(res1, Newtonsoft.Json.JsonConvert.SerializeObject(query)); - - // Multiple lambdas - var res2 = "[{\"Key\":{\"name\":\"Juan\"},\"nativeAggregates\":{\"ageSum\":0,\"ageSum2\":104},\"Grouping\":[{\"name\":\"Juan\",\"age\":25},{\"name\":\"Juan\",\"age\":25},{\"name\":\"Juan\",\"age\":25},{\"name\":\"Juan\",\"age\":4},{\"name\":\"Juan\",\"age\":25}]},{\"Key\":{\"name\":\"David\"},\"nativeAggregates\":{\"ageSum\":0,\"ageSum2\":12},\"Grouping\":[{\"name\":\"David\",\"age\":12}]},{\"Key\":{\"name\":\"Pedro\"},\"nativeAggregates\":{\"ageSum\":0,\"ageSum2\":2},\"Grouping\":[{\"name\":\"Pedro\",\"age\":2}]}]"; - query = users.AsQueryable(); - query = query.GroupBy("new(name as name)", "it"); - query = query.Select("new (it.Key as Key, new(it.Sum(x => x.age > 25 ? 1 : 0) as ageSum, it.Sum(x => x.age) as ageSum2) as nativeAggregates, it as Grouping)"); - Assert.Equal(res2, Newtonsoft.Json.JsonConvert.SerializeObject(query)); - } + // Assert + result.Should().Be(expected); + } - [Fact] - public void DynamicExpressionParser_ParseLambda_StringLiteralStartEmbeddedQuote_ReturnsBooleanLambdaExpression() - { - // Act - var expression = DynamicExpressionParser.ParseLambda( - new[] { Expression.Parameter(typeof(string), "Property1") }, - typeof(bool), - string.Format("Property1 == {0}", "\"\\\"test\"")); - - var rightValue = ((BinaryExpression)expression.Body).Right.ToString(); - Assert.Equal(typeof(bool), expression.Body.Type); - Assert.Equal("\"\"test\"", rightValue); - } + [Fact] + public void DynamicExpressionParser_ParseLambda_StringLiteral_ReturnsBooleanLambdaExpression() + { + var expression = DynamicExpressionParser.ParseLambda( + new[] { Expression.Parameter(typeof(string), "Property1") }, typeof(bool), "Property1 == \"test\""); + Assert.Equal(typeof(bool), expression.Body.Type); + } - [Fact] - public void DynamicExpressionParser_ParseLambda_StringLiteral_MissingClosingQuote() - { - var expectedRightValue = "\"test\\\""; + [Fact] + public void DynamicExpressionParser_ParseLambda_StringLiteralEmpty_ReturnsBooleanLambdaExpression() + { + var expression = DynamicExpressionParser.ParseLambda( + new[] { Expression.Parameter(typeof(string), "Property1") }, typeof(bool), "Property1 == \"\""); + Assert.Equal(typeof(bool), expression.Body.Type); + } - Assert.Throws(() => DynamicExpressionParser.ParseLambda( - new[] { Expression.Parameter(typeof(string), "Property1") }, - typeof(bool), - string.Format("Property1 == {0}", expectedRightValue))); - } + [Fact] + public void DynamicExpressionParser_ParseLambda_Config_StringLiteralEmpty_ReturnsBooleanLambdaExpression() + { + var config = new ParsingConfig(); + var expression = DynamicExpressionParser.ParseLambda( + new[] { Expression.Parameter(typeof(string), "Property1") }, typeof(bool), "Property1 == \"\""); + Assert.Equal(typeof(bool), expression.Body.Type); + } - [Fact] - public void DynamicExpressionParser_ParseLambda_StringLiteralEscapedBackslash_ReturnsBooleanLambdaExpression() - { - // Act - var expression = DynamicExpressionParser.ParseLambda( - new[] { Expression.Parameter(typeof(string), "Property1") }, - typeof(bool), - string.Format("Property1 == {0}", "\"test\\\\string\"")); - - var rightValue = ((BinaryExpression)expression.Body).Right.ToString(); - Assert.Equal(typeof(Boolean), expression.Body.Type); - Assert.Equal("\"test\\string\"", rightValue); - } + [Fact] + public void DynamicExpressionParser_ParseLambda_StringLiteralEmbeddedQuote_ReturnsBooleanLambdaExpression() + { + // Act + var expression = DynamicExpressionParser.ParseLambda( + new[] { Expression.Parameter(typeof(string), "Property1") }, + typeof(bool), + string.Format("Property1 == {0}", "\"test \\\"string\"")); + + var rightValue = ((BinaryExpression)expression.Body).Right.ToString(); + Assert.Equal(typeof(bool), expression.Body.Type); + Assert.Equal("\"test \"string\"", rightValue); + } - [Fact] - public void DynamicExpressionParser_ParseLambda_StringLiteralEscapedNewline_ReturnsBooleanLambdaExpression() - { - // Act - var expression = DynamicExpressionParser.ParseLambda( - new[] { Expression.Parameter(typeof(string), "Property1") }, - typeof(bool), - string.Format("Property1 == {0}", "\"test\\\\new\"")); - - var rightValue = ((BinaryExpression)expression.Body).Right.ToString(); - Assert.Equal(typeof(Boolean), expression.Body.Type); - Assert.Equal("\"test\\new\"", rightValue); - } + /// + /// @see https://github.com/StefH/System.Linq.Dynamic.Core/issues/294 + /// + [Fact(Skip = "Fails on EntityFramework.DynamicLinq.Tests with 'An unexpected exception occurred while binding a dynamic operation'")] + public void DynamicExpressionParser_ParseLambda_MultipleLambdas() + { + var users = new[] + { + new { name = "Juan", age = 25 }, + new { name = "Juan", age = 25 }, + new { name = "David", age = 12 }, + new { name = "Juan", age = 25 }, + new { name = "Juan", age = 4 }, + new { name = "Pedro", age = 2 }, + new { name = "Juan", age = 25 } + }.ToList(); + + IQueryable query; + + // One lambda + var res1 = "[{\"Key\":{\"name\":\"Juan\"},\"nativeAggregates\":{\"ageSum\":104},\"Grouping\":[{\"name\":\"Juan\",\"age\":25},{\"name\":\"Juan\",\"age\":25},{\"name\":\"Juan\",\"age\":25},{\"name\":\"Juan\",\"age\":4},{\"name\":\"Juan\",\"age\":25}]},{\"Key\":{\"name\":\"David\"},\"nativeAggregates\":{\"ageSum\":12},\"Grouping\":[{\"name\":\"David\",\"age\":12}]},{\"Key\":{\"name\":\"Pedro\"},\"nativeAggregates\":{\"ageSum\":2},\"Grouping\":[{\"name\":\"Pedro\",\"age\":2}]}]"; + query = users.AsQueryable(); + query = query.GroupBy("new(name as name)", "it"); + query = query.Select("new (it.Key as Key, new(it.Sum(x => x.age) as ageSum) as nativeAggregates, it as Grouping)"); + Assert.Equal(res1, Newtonsoft.Json.JsonConvert.SerializeObject(query)); + + // Multiple lambdas + var res2 = "[{\"Key\":{\"name\":\"Juan\"},\"nativeAggregates\":{\"ageSum\":0,\"ageSum2\":104},\"Grouping\":[{\"name\":\"Juan\",\"age\":25},{\"name\":\"Juan\",\"age\":25},{\"name\":\"Juan\",\"age\":25},{\"name\":\"Juan\",\"age\":4},{\"name\":\"Juan\",\"age\":25}]},{\"Key\":{\"name\":\"David\"},\"nativeAggregates\":{\"ageSum\":0,\"ageSum2\":12},\"Grouping\":[{\"name\":\"David\",\"age\":12}]},{\"Key\":{\"name\":\"Pedro\"},\"nativeAggregates\":{\"ageSum\":0,\"ageSum2\":2},\"Grouping\":[{\"name\":\"Pedro\",\"age\":2}]}]"; + query = users.AsQueryable(); + query = query.GroupBy("new(name as name)", "it"); + query = query.Select("new (it.Key as Key, new(it.Sum(x => x.age > 25 ? 1 : 0) as ageSum, it.Sum(x => x.age) as ageSum2) as nativeAggregates, it as Grouping)"); + Assert.Equal(res2, Newtonsoft.Json.JsonConvert.SerializeObject(query)); + } - [Fact] - public void DynamicExpressionParser_ParseLambda_StringLiteral_Backslash() - { - // Assign - var expectedRightValue = "0"; - - //Act - var expression = DynamicExpressionParser.ParseLambda( - new[] { Expression.Parameter(typeof(string), "Property1") }, - typeof(Boolean), - string.Format("{0} >= {1}", "Property1.IndexOf(\"\\\\\")", expectedRightValue)); - - var leftValue = ((BinaryExpression)expression.Body).Left.ToString(); - var rightValue = ((BinaryExpression)expression.Body).Right.ToString(); - - // Assert - Assert.Equal(typeof(Boolean), expression.Body.Type); - Assert.Equal("Property1.IndexOf(\"\\\")", leftValue); - Assert.Equal(expectedRightValue, rightValue); - } + [Fact] + public void DynamicExpressionParser_ParseLambda_StringLiteralStartEmbeddedQuote_ReturnsBooleanLambdaExpression() + { + // Act + var expression = DynamicExpressionParser.ParseLambda( + new[] { Expression.Parameter(typeof(string), "Property1") }, + typeof(bool), + string.Format("Property1 == {0}", "\"\\\"test\"")); + + var rightValue = ((BinaryExpression)expression.Body).Right.ToString(); + Assert.Equal(typeof(bool), expression.Body.Type); + Assert.Equal("\"\"test\"", rightValue); + } - [Fact] - public void DynamicExpressionParser_ParseLambda_StringLiteral_QuotationMark() - { - var expectedRightValue = "0"; - var expression = DynamicExpressionParser.ParseLambda( - new[] { Expression.Parameter(typeof(string), "Property1") }, - typeof(Boolean), - string.Format("{0} >= {1}", "Property1.IndexOf(\"\\\"\")", expectedRightValue)); - - var leftValue = ((BinaryExpression)expression.Body).Left.ToString(); - var rightValue = ((BinaryExpression)expression.Body).Right.ToString(); - Assert.Equal(typeof(Boolean), expression.Body.Type); - Assert.Equal("Property1.IndexOf(\"\"\")", leftValue); - Assert.Equal(expectedRightValue, rightValue); - } + [Fact] + public void DynamicExpressionParser_ParseLambda_StringLiteral_MissingClosingQuote() + { + var expectedRightValue = "\"test\\\""; - [Fact] - public void DynamicExpressionParser_ParseLambda_TupleToStringMethodCall_ReturnsStringLambdaExpression() - { - var expression = DynamicExpressionParser.ParseLambda( - typeof(Tuple), - typeof(string), - "it.ToString()"); - Assert.Equal(typeof(string), expression.ReturnType); - } + Assert.Throws(() => DynamicExpressionParser.ParseLambda( + new[] { Expression.Parameter(typeof(string), "Property1") }, + typeof(bool), + string.Format("Property1 == {0}", expectedRightValue))); + } - [Fact] - public void DynamicExpressionParser_ParseLambda_IllegalMethodCall_ThrowsException() - { - Check.ThatCode(() => { DynamicExpressionParser.ParseLambda(typeof(IO.FileStream), null, "it.Close()"); }) - .Throws().WithMessage("Methods on type 'Stream' are not accessible"); - } + [Fact] + public void DynamicExpressionParser_ParseLambda_StringLiteralEscapedBackslash_ReturnsBooleanLambdaExpression() + { + // Act + var expression = DynamicExpressionParser.ParseLambda( + new[] { Expression.Parameter(typeof(string), "Property1") }, + typeof(bool), + string.Format("Property1 == {0}", "\"test\\\\string\"")); + + var rightValue = ((BinaryExpression)expression.Body).Right.ToString(); + Assert.Equal(typeof(Boolean), expression.Body.Type); + Assert.Equal("\"test\\string\"", rightValue); + } - [Fact] - public void DynamicExpressionParser_ParseLambda_CustomMethod() - { - // Assign - var config = new ParsingConfig - { - CustomTypeProvider = new TestCustomTypeProvider() - }; + [Fact] + public void DynamicExpressionParser_ParseLambda_StringLiteralEscapedNewline_ReturnsBooleanLambdaExpression() + { + // Act + var expression = DynamicExpressionParser.ParseLambda( + new[] { Expression.Parameter(typeof(string), "Property1") }, + typeof(bool), + string.Format("Property1 == {0}", "\"test\\\\new\"")); + + var rightValue = ((BinaryExpression)expression.Body).Right.ToString(); + Assert.Equal(typeof(Boolean), expression.Body.Type); + Assert.Equal("\"test\\new\"", rightValue); + } - var context = new CustomClassWithStaticMethod(); - var expression = $"{nameof(CustomClassWithStaticMethod)}.{nameof(CustomClassWithStaticMethod.GetAge)}(10)"; + [Fact] + public void DynamicExpressionParser_ParseLambda_StringLiteral_Backslash() + { + // Assign + var expectedRightValue = "0"; + + //Act + var expression = DynamicExpressionParser.ParseLambda( + new[] { Expression.Parameter(typeof(string), "Property1") }, + typeof(Boolean), + string.Format("{0} >= {1}", "Property1.IndexOf(\"\\\\\")", expectedRightValue)); + + var leftValue = ((BinaryExpression)expression.Body).Left.ToString(); + var rightValue = ((BinaryExpression)expression.Body).Right.ToString(); + + // Assert + Assert.Equal(typeof(Boolean), expression.Body.Type); + Assert.Equal("Property1.IndexOf(\"\\\")", leftValue); + Assert.Equal(expectedRightValue, rightValue); + } - // Act - var lambdaExpression = DynamicExpressionParser.ParseLambda(config, typeof(CustomClassWithStaticMethod), null, expression); - var del = lambdaExpression.Compile(); - var result = (int)del.DynamicInvoke(context); + [Fact] + public void DynamicExpressionParser_ParseLambda_StringLiteral_QuotationMark() + { + var expectedRightValue = "0"; + var expression = DynamicExpressionParser.ParseLambda( + new[] { Expression.Parameter(typeof(string), "Property1") }, + typeof(Boolean), + string.Format("{0} >= {1}", "Property1.IndexOf(\"\\\"\")", expectedRightValue)); + + var leftValue = ((BinaryExpression)expression.Body).Left.ToString(); + var rightValue = ((BinaryExpression)expression.Body).Right.ToString(); + Assert.Equal(typeof(Boolean), expression.Body.Type); + Assert.Equal("Property1.IndexOf(\"\"\")", leftValue); + Assert.Equal(expectedRightValue, rightValue); + } - // Assert - Check.That(result).IsEqualTo(10); - } + [Fact] + public void DynamicExpressionParser_ParseLambda_TupleToStringMethodCall_ReturnsStringLambdaExpression() + { + var expression = DynamicExpressionParser.ParseLambda( + typeof(Tuple), + typeof(string), + "it.ToString()"); + Assert.Equal(typeof(string), expression.ReturnType); + } - // [Fact] - public void DynamicExpressionParser_ParseLambda_With_InnerStringLiteral() + [Fact] + public void DynamicExpressionParser_ParseLambda_CustomMethod() + { + // Assign + var config = new ParsingConfig { - // Assign - var originalTrueValue = "simple + \"quoted\""; - var doubleQuotedTrueValue = "simple + \"\"quoted\"\""; - var expressionText = $"iif(1>0, \"{doubleQuotedTrueValue}\", \"false\")"; - - // Act - var lambda = DynamicExpressionParser.ParseLambda(typeof(string), null, expressionText); - var del = lambda.Compile(); - var result = del.DynamicInvoke(string.Empty); - - // Assert - Check.That(result).IsEqualTo(originalTrueValue); - } + CustomTypeProvider = new TestCustomTypeProvider() + }; - [Fact] - public void DynamicExpressionParser_ParseLambda_With_Guid_Equals_Null() - { - // Arrange - var user = new User(); - var guidEmpty = Guid.Empty; - var someId = Guid.NewGuid(); - var expressionText = $"iif(@0.Id == null, @0.Id == Guid.Parse(\"{someId}\"), Id == Id)"; - - // Act - var lambda = DynamicExpressionParser.ParseLambda(typeof(User), null, expressionText, user); - var boolLambda = lambda as Expression>; - Assert.NotNull(boolLambda); - - var del = lambda.Compile(); - var result = (bool)del.DynamicInvoke(user); - - // Assert - Assert.True(result); - } + var context = new CustomClassWithStaticMethod(); + var expression = $"{nameof(CustomClassWithStaticMethod)}.{nameof(CustomClassWithStaticMethod.GetAge)}(10)"; - [Fact] - public void DynamicExpressionParser_ParseLambda_With_Null_Equals_Guid() - { - // Arrange - var user = new User(); - var someId = Guid.NewGuid(); - var expressionText = $"iif(null == @0.Id, @0.Id == Guid.Parse(\"{someId}\"), Id == Id)"; + // Act + var lambdaExpression = DynamicExpressionParser.ParseLambda(config, typeof(CustomClassWithStaticMethod), null, expression); + var del = lambdaExpression.Compile(); + var result = (int)del.DynamicInvoke(context); - // Act - var lambda = DynamicExpressionParser.ParseLambda(typeof(User), null, expressionText, user); - var boolLambda = lambda as Expression>; - Assert.NotNull(boolLambda); + // Assert + Check.That(result).IsEqualTo(10); + } - var del = lambda.Compile(); - var result = (bool)del.DynamicInvoke(user); + // [Fact] + public void DynamicExpressionParser_ParseLambda_With_InnerStringLiteral() + { + // Assign + var originalTrueValue = "simple + \"quoted\""; + var doubleQuotedTrueValue = "simple + \"\"quoted\"\""; + var expressionText = $"iif(1>0, \"{doubleQuotedTrueValue}\", \"false\")"; + + // Act + var lambda = DynamicExpressionParser.ParseLambda(typeof(string), null, expressionText); + var del = lambda.Compile(); + var result = del.DynamicInvoke(string.Empty); + + // Assert + Check.That(result).IsEqualTo(originalTrueValue); + } - // Assert - Assert.True(result); - } + [Fact] + public void DynamicExpressionParser_ParseLambda_With_Guid_Equals_Null() + { + // Arrange + var user = new User(); + var guidEmpty = Guid.Empty; + var someId = Guid.NewGuid(); + var expressionText = $"iif(@0.Id == null, @0.Id == Guid.Parse(\"{someId}\"), Id == Id)"; + + // Act + var lambda = DynamicExpressionParser.ParseLambda(typeof(User), null, expressionText, user); + var boolLambda = lambda as Expression>; + Assert.NotNull(boolLambda); + + var del = lambda.Compile(); + var result = (bool)del.DynamicInvoke(user); + + // Assert + Assert.True(result); + } - [Fact] - public void DynamicExpressionParser_ParseLambda_With_DateTime_Equals_String() - { - // Arrange - var someDateTime = "2022-03-02"; - var user = new Person - { - D = new DateTime(2022, 3, 2) - }; - var expressionText = $"D == \"{someDateTime}\""; + [Fact] + public void DynamicExpressionParser_ParseLambda_With_Null_Equals_Guid() + { + // Arrange + var user = new User(); + var someId = Guid.NewGuid(); + var expressionText = $"iif(null == @0.Id, @0.Id == Guid.Parse(\"{someId}\"), Id == Id)"; - // Act - var lambda = DynamicExpressionParser.ParseLambda(typeof(Person), null, expressionText, user); - var dtLambda = lambda as Expression>; - Assert.NotNull(dtLambda); + // Act + var lambda = DynamicExpressionParser.ParseLambda(typeof(User), null, expressionText, user); + var boolLambda = lambda as Expression>; + Assert.NotNull(boolLambda); - var del = lambda.Compile(); - var result = (bool)del.DynamicInvoke(user); + var del = lambda.Compile(); + var result = (bool)del.DynamicInvoke(user); - // Assert - Assert.True(result); - } + // Assert + Assert.True(result); + } - [Fact] - public void DynamicExpressionParser_ParseLambda_With_Guid_Equals_String() + [Fact] + public void DynamicExpressionParser_ParseLambda_With_DateTime_Equals_String() + { + // Arrange + var someDateTime = "2022-03-02"; + var user = new Person { - // Arrange - var someId = Guid.NewGuid(); - var anotherId = Guid.NewGuid(); - var user = new User - { - Id = someId - }; - var guidEmpty = Guid.Empty; - var expressionText = $"iif(@0.Id == \"{someId}\", Guid.Parse(\"{guidEmpty}\"), Guid.Parse(\"{anotherId}\"))"; + D = new DateTime(2022, 3, 2) + }; + var expressionText = $"D == \"{someDateTime}\""; - // Act - var lambda = DynamicExpressionParser.ParseLambda(typeof(User), null, expressionText, user); - var guidLambda = lambda as Expression>; - Assert.NotNull(guidLambda); + // Act + var lambda = DynamicExpressionParser.ParseLambda(typeof(Person), null, expressionText, user); + var dtLambda = lambda as Expression>; + Assert.NotNull(dtLambda); - var del = lambda.Compile(); - var result = (Guid)del.DynamicInvoke(user); + var del = lambda.Compile(); + var result = (bool)del.DynamicInvoke(user); - // Assert - Assert.Equal(guidEmpty, result); - } + // Assert + Assert.True(result); + } - [Fact] - public void DynamicExpressionParser_ParseLambda_With_Concat_String_CustomType() - { - // Arrange - var name = "name1"; - var note = "note1"; - var textHolder = new TextHolder(name, note); - var expressionText = "Name + \" (\" + Note + \")\""; + [Fact] + public void DynamicExpressionParser_ParseLambda_With_Guid_Equals_String() + { + // Arrange + var someId = Guid.NewGuid(); + var anotherId = Guid.NewGuid(); + var user = new User + { + Id = someId + }; + var guidEmpty = Guid.Empty; + var expressionText = $"iif(@0.Id == \"{someId}\", Guid.Parse(\"{guidEmpty}\"), Guid.Parse(\"{anotherId}\"))"; + + // Act + var lambda = DynamicExpressionParser.ParseLambda(typeof(User), null, expressionText, user); + var guidLambda = lambda as Expression>; + Assert.NotNull(guidLambda); + + var del = lambda.Compile(); + var result = (Guid)del.DynamicInvoke(user); + + // Assert + Assert.Equal(guidEmpty, result); + } - // Act 1 - var lambda = DynamicExpressionParser.ParseLambda(typeof(TextHolder), null, expressionText, textHolder); - var stringLambda = lambda as Expression>; + [Fact] + public void DynamicExpressionParser_ParseLambda_With_Concat_String_CustomType() + { + // Arrange + var name = "name1"; + var note = "note1"; + var textHolder = new TextHolder(name, note); + var expressionText = "Name + \" (\" + Note + \")\""; - // Assert 1 - Assert.NotNull(stringLambda); + // Act 1 + var lambda = DynamicExpressionParser.ParseLambda(typeof(TextHolder), null, expressionText, textHolder); + var stringLambda = lambda as Expression>; - // Act 2 - var del = lambda.Compile(); - var result = (string)del.DynamicInvoke(textHolder); + // Assert 1 + Assert.NotNull(stringLambda); - // Assert 2 - Assert.Equal("name1 (note1)", result); - } + // Act 2 + var del = lambda.Compile(); + var result = (string)del.DynamicInvoke(textHolder); - [Fact] - public void DynamicExpressionParser_ParseLambda_With_Concat_CustomType_String() - { - // Arrange - var name = "name1"; - var note = "note1"; - var textHolder = new TextHolder(name, note); - var expressionText = "Note + \" (\" + Name + \")\""; + // Assert 2 + Assert.Equal("name1 (note1)", result); + } - // Act 1 - var lambda = DynamicExpressionParser.ParseLambda(typeof(TextHolder), null, expressionText, textHolder); - var stringLambda = lambda as Expression>; + [Fact] + public void DynamicExpressionParser_ParseLambda_With_Concat_CustomType_String() + { + // Arrange + var name = "name1"; + var note = "note1"; + var textHolder = new TextHolder(name, note); + var expressionText = "Note + \" (\" + Name + \")\""; - // Assert 1 - Assert.NotNull(stringLambda); + // Act 1 + var lambda = DynamicExpressionParser.ParseLambda(typeof(TextHolder), null, expressionText, textHolder); + var stringLambda = lambda as Expression>; - // Act 2 - var del = lambda.Compile(); - var result = (string)del.DynamicInvoke(textHolder); + // Assert 1 + Assert.NotNull(stringLambda); - // Assert 2 - Assert.Equal("note1 (name1)", result); - } + // Act 2 + var del = lambda.Compile(); + var result = (string)del.DynamicInvoke(textHolder); - [Fact] - public void DynamicExpressionParser_ParseLambda_With_One_Way_Implicit_Conversions() - { - // Arrange - var testString = "test"; - var testInt = 6; - var container = new TestImplicitConversionContainer(testString, new CustomClassWithReversedImplicitConversion(testString), testInt, new CustomClassWithReversedValueTypeImplicitConversion(testInt)); + // Assert 2 + Assert.Equal("note1 (name1)", result); + } - var expressionTextString = $"OneWay == \"{testString}\""; - var expressionTextReversed = $"Reversed == \"{testString}\""; - var expressionTextValueType = $"ValueType == {testInt}"; - var expressionTextReversedValueType = $"ReversedValueType == {testInt}"; + [Fact] + public void DynamicExpressionParser_ParseLambda_With_One_Way_Implicit_Conversions() + { + // Arrange + var testString = "test"; + var testInt = 6; + var container = new TestImplicitConversionContainer(testString, new CustomClassWithReversedImplicitConversion(testString), testInt, new CustomClassWithReversedValueTypeImplicitConversion(testInt)); - var invertedExpressionTextString = $"\"{testString}\" == OneWay"; - var invertedExpressionTextReversed = $"\"{testString}\" == Reversed"; - var invertedExpressionTextValueType = $"{testInt} == ValueType"; - var invertedExpressionTextReversedValueType = $"{testInt} == ReversedValueType"; + var expressionTextString = $"OneWay == \"{testString}\""; + var expressionTextReversed = $"Reversed == \"{testString}\""; + var expressionTextValueType = $"ValueType == {testInt}"; + var expressionTextReversedValueType = $"ReversedValueType == {testInt}"; - // Act 1 - var lambda = DynamicExpressionParser.ParseLambda(ParsingConfig.Default, false, expressionTextString); + var invertedExpressionTextString = $"\"{testString}\" == OneWay"; + var invertedExpressionTextReversed = $"\"{testString}\" == Reversed"; + var invertedExpressionTextValueType = $"{testInt} == ValueType"; + var invertedExpressionTextReversedValueType = $"{testInt} == ReversedValueType"; - // Assert 1 - Assert.NotNull(lambda); + // Act 1 + var lambda = DynamicExpressionParser.ParseLambda(ParsingConfig.Default, false, expressionTextString); - // Act 2 - lambda = DynamicExpressionParser.ParseLambda(ParsingConfig.Default, false, expressionTextReversed); + // Assert 1 + Assert.NotNull(lambda); - // Assert 2 - Assert.NotNull(lambda); + // Act 2 + lambda = DynamicExpressionParser.ParseLambda(ParsingConfig.Default, false, expressionTextReversed); - // Act 3 - lambda = DynamicExpressionParser.ParseLambda(ParsingConfig.Default, false, expressionTextValueType); + // Assert 2 + Assert.NotNull(lambda); - // Assert 3 - Assert.NotNull(lambda); + // Act 3 + lambda = DynamicExpressionParser.ParseLambda(ParsingConfig.Default, false, expressionTextValueType); - // Act 4 - lambda = DynamicExpressionParser.ParseLambda(ParsingConfig.Default, false, expressionTextReversedValueType); + // Assert 3 + Assert.NotNull(lambda); - // Assert 4 - Assert.NotNull(lambda); + // Act 4 + lambda = DynamicExpressionParser.ParseLambda(ParsingConfig.Default, false, expressionTextReversedValueType); - // Act 5 - lambda = DynamicExpressionParser.ParseLambda(ParsingConfig.Default, false, invertedExpressionTextString); + // Assert 4 + Assert.NotNull(lambda); - // Assert 5 - Assert.NotNull(lambda); + // Act 5 + lambda = DynamicExpressionParser.ParseLambda(ParsingConfig.Default, false, invertedExpressionTextString); - // Act 6 - lambda = DynamicExpressionParser.ParseLambda(ParsingConfig.Default, false, invertedExpressionTextReversed); + // Assert 5 + Assert.NotNull(lambda); - // Assert 6 - Assert.NotNull(lambda); + // Act 6 + lambda = DynamicExpressionParser.ParseLambda(ParsingConfig.Default, false, invertedExpressionTextReversed); - // Act 7 - lambda = DynamicExpressionParser.ParseLambda(ParsingConfig.Default, false, invertedExpressionTextValueType); + // Assert 6 + Assert.NotNull(lambda); - // Assert 7 - Assert.NotNull(lambda); + // Act 7 + lambda = DynamicExpressionParser.ParseLambda(ParsingConfig.Default, false, invertedExpressionTextValueType); - // Act 8 - lambda = DynamicExpressionParser.ParseLambda(ParsingConfig.Default, false, invertedExpressionTextReversedValueType); + // Assert 7 + Assert.NotNull(lambda); - // Assert 8 - Assert.NotNull(lambda); - } + // Act 8 + lambda = DynamicExpressionParser.ParseLambda(ParsingConfig.Default, false, invertedExpressionTextReversedValueType); + + // Assert 8 + Assert.NotNull(lambda); + } - [Fact] - public void DynamicExpressionParser_ParseLambda_Operator_Less_Greater_With_Guids() + [Fact] + public void DynamicExpressionParser_ParseLambda_Operator_Less_Greater_With_Guids() + { + var config = new ParsingConfig { - var config = new ParsingConfig - { - CustomTypeProvider = new TestCustomTypeProvider() - }; + CustomTypeProvider = new TestCustomTypeProvider() + }; - // Arrange - var someId = Guid.NewGuid(); - var anotherId = Guid.NewGuid(); - var user = new User - { - Id = someId - }; - var guidEmpty = Guid.Empty; - var expressionText = $"iif(@0.Id == StaticHelper.GetGuid(\"name\"), Guid.Parse(\"{guidEmpty}\"), Guid.Parse(\"{anotherId}\"))"; + // Arrange + var someId = Guid.NewGuid(); + var anotherId = Guid.NewGuid(); + var user = new User + { + Id = someId + }; + var guidEmpty = Guid.Empty; + var expressionText = $"iif(@0.Id == StaticHelper.GetGuid(\"name\"), Guid.Parse(\"{guidEmpty}\"), Guid.Parse(\"{anotherId}\"))"; - // Act - var lambda = DynamicExpressionParser.ParseLambda(config, typeof(User), null, expressionText, user); - var guidLambda = lambda as Expression>; - Assert.NotNull(guidLambda); + // Act + var lambda = DynamicExpressionParser.ParseLambda(config, typeof(User), null, expressionText, user); + var guidLambda = lambda as Expression>; + Assert.NotNull(guidLambda); - var del = lambda.Compile(); - var result = (Guid)del.DynamicInvoke(user); + var del = lambda.Compile(); + var result = (Guid)del.DynamicInvoke(user); - // Assert - Assert.Equal(anotherId, result); - } + // Assert + Assert.Equal(anotherId, result); + } - [Theory] - [InlineData(true, "c => c.Age == 8", "c => (c.Age == 8)")] - [InlineData(true, "c => c.Name == \"test\"", "c => (c.Name == \"test\")")] - [InlineData(false, "c => c.Age == 8", "Param_0 => (Param_0.Age == 8)")] - [InlineData(false, "c => c.Name == \"test\"", "Param_0 => (Param_0.Name == \"test\")")] - public void DynamicExpressionParser_ParseLambda_RenameParameterExpression(bool renameParameterExpression, string expressionAsString, string expected) + [Theory] + [InlineData(true, "c => c.Age == 8", "c => (c.Age == 8)")] + [InlineData(true, "c => c.Name == \"test\"", "c => (c.Name == \"test\")")] + [InlineData(false, "c => c.Age == 8", "Param_0 => (Param_0.Age == 8)")] + [InlineData(false, "c => c.Name == \"test\"", "Param_0 => (Param_0.Name == \"test\")")] + public void DynamicExpressionParser_ParseLambda_RenameParameterExpression(bool renameParameterExpression, string expressionAsString, string expected) + { + // Arrange + var config = new ParsingConfig { - // Arrange - var config = new ParsingConfig - { - RenameParameterExpression = renameParameterExpression - }; + RenameParameterExpression = renameParameterExpression + }; - // Act - var expression = DynamicExpressionParser.ParseLambda(config, true, expressionAsString); - var result = expression.ToString(); + // Act + var expression = DynamicExpressionParser.ParseLambda(config, true, expressionAsString); + var result = expression.ToString(); - // Assert - Check.That(result).IsEqualTo(expected); - } + // Assert + Check.That(result).IsEqualTo(expected); + } - [Theory] - [InlineData("c => c.Age == 8", "([a-z]{16}) =\\> \\(\\1\\.Age == 8\\)")] - [InlineData("c => c.Name == \"test\"", "([a-z]{16}) =\\> \\(\\1\\.Name == \"test\"\\)")] - public void DynamicExpressionParser_ParseLambda_RenameEmptyParameterExpressionNames(string expressionAsString, string expected) + [Theory] + [InlineData("c => c.Age == 8", "([a-z]{16}) =\\> \\(\\1\\.Age == 8\\)")] + [InlineData("c => c.Name == \"test\"", "([a-z]{16}) =\\> \\(\\1\\.Name == \"test\"\\)")] + public void DynamicExpressionParser_ParseLambda_RenameEmptyParameterExpressionNames(string expressionAsString, string expected) + { + // Arrange + var config = new ParsingConfig { - // Arrange - var config = new ParsingConfig - { - RenameEmptyParameterExpressionNames = true - }; + RenameEmptyParameterExpressionNames = true + }; - // Act - var expression = DynamicExpressionParser.ParseLambda(config, true, expressionAsString); - var result = expression.ToString(); + // Act + var expression = DynamicExpressionParser.ParseLambda(config, true, expressionAsString); + var result = expression.ToString(); - // Assert - Check.That(result).Matches(expected); - } + // Assert + Check.That(result).Matches(expected); + } - [Theory] - [InlineData(@"p0.Equals(""Testing"", 3)", "testinG", true)] - [InlineData(@"p0.Equals(""Testing"", StringComparison.InvariantCultureIgnoreCase)", "testinG", true)] - public void DynamicExpressionParser_ParseLambda_SupportEnumerationStringComparison(string expressionAsString, string testValue, bool expectedResult) - { - // Arrange - var p0 = Expression.Parameter(typeof(string), "p0"); + [Theory] + [InlineData(@"p0.Equals(""Testing"", 3)", "testinG", true)] + [InlineData(@"p0.Equals(""Testing"", StringComparison.InvariantCultureIgnoreCase)", "testinG", true)] + public void DynamicExpressionParser_ParseLambda_SupportEnumerationStringComparison(string expressionAsString, string testValue, bool expectedResult) + { + // Arrange + var p0 = Expression.Parameter(typeof(string), "p0"); - // Act - var expression = DynamicExpressionParser.ParseLambda(new[] { p0 }, typeof(bool), expressionAsString); - var del = expression.Compile(); - var result = del.DynamicInvoke(testValue) as bool?; + // Act + var expression = DynamicExpressionParser.ParseLambda(new[] { p0 }, typeof(bool), expressionAsString); + var del = expression.Compile(); + var result = del.DynamicInvoke(testValue) as bool?; - // Assert - Check.That(result).IsEqualTo(expectedResult); - } + // Assert + Check.That(result).IsEqualTo(expectedResult); + } - [Fact] - public void DynamicExpressionParser_ParseLambda_NullPropagation_InstanceMethod_Zero_Arguments() + [Fact] + public void DynamicExpressionParser_ParseLambda_NullPropagation_InstanceMethod_0_Arguments() + { + // Arrange + var customTypeProvider = new Mock(); + customTypeProvider.Setup(c => c.GetCustomTypes()).Returns(new HashSet { typeof(Foo) }); + var config = new ParsingConfig { - // Arrange - var expression = "np(FooValue.Zero().Length)"; + CustomTypeProvider = customTypeProvider.Object + }; + var expression = "np(FooValue.Zero().Length)"; - // Act - var lambdaExpression = DynamicExpressionParser.ParseLambda(typeof(Foo), null, expression, new Foo()); + // Act + var lambdaExpression = DynamicExpressionParser.ParseLambda(config, typeof(Foo), null, expression, new Foo()); - // Assert + // Assert #if NETCOREAPP3_1 - lambdaExpression.ToString().Should().Be("Param_0 => IIF((((Param_0 != null) AndAlso (Param_0.FooValue != null)) AndAlso (Param_0.FooValue.Zero() != null)), Convert(Param_0.FooValue.Zero().Length, Nullable`1), null)"); + lambdaExpression.ToString().Should().Be("Param_0 => IIF((((Param_0 != null) AndAlso (Param_0.FooValue != null)) AndAlso (Param_0.FooValue.Zero() != null)), Convert(Param_0.FooValue.Zero().Length, Nullable`1), null)"); #else - lambdaExpression.ToString().Should().Be("Param_0 => IIF((((Param_0 != null) AndAlso (Param_0.FooValue != null)) AndAlso (Param_0.FooValue.Zero() != null)), Convert(Param_0.FooValue.Zero().Length), null)"); + lambdaExpression.ToString().Should().Be("Param_0 => IIF((((Param_0 != null) AndAlso (Param_0.FooValue != null)) AndAlso (Param_0.FooValue.Zero() != null)), Convert(Param_0.FooValue.Zero().Length), null)"); #endif - } + } - [Fact] - public void DynamicExpressionParser_ParseLambda_NullPropagation_InstanceMethod_One_Argument() + [Fact] + public void DynamicExpressionParser_ParseLambda_NullPropagation_InstanceMethod_1_Argument() + { + // Arrange + var customTypeProvider = new Mock(); + customTypeProvider.Setup(c => c.GetCustomTypes()).Returns(new HashSet { typeof(Foo) }); + var config = new ParsingConfig { - // Arrange - var expression = "np(FooValue.One(1).Length)"; + CustomTypeProvider = customTypeProvider.Object + }; + var expression = "np(FooValue.One(1).Length)"; - // Act - var lambdaExpression = DynamicExpressionParser.ParseLambda(typeof(Foo), null, expression, new Foo()); + // Act + var lambdaExpression = DynamicExpressionParser.ParseLambda(config, typeof(Foo), null, expression, new Foo()); - // Assert + // Assert #if NETCOREAPP3_1 - lambdaExpression.ToString().Should().Be("Param_0 => IIF((((Param_0 != null) AndAlso (Param_0.FooValue != null)) AndAlso (Param_0.FooValue.One(1) != null)), Convert(Param_0.FooValue.One(1).Length, Nullable`1), null)"); + lambdaExpression.ToString().Should().Be("Param_0 => IIF((((Param_0 != null) AndAlso (Param_0.FooValue != null)) AndAlso (Param_0.FooValue.One(1) != null)), Convert(Param_0.FooValue.One(1).Length, Nullable`1), null)"); #else - lambdaExpression.ToString().Should().Be("Param_0 => IIF((((Param_0 != null) AndAlso (Param_0.FooValue != null)) AndAlso (Param_0.FooValue.One(1) != null)), Convert(Param_0.FooValue.One(1).Length), null)"); + lambdaExpression.ToString().Should().Be("Param_0 => IIF((((Param_0 != null) AndAlso (Param_0.FooValue != null)) AndAlso (Param_0.FooValue.One(1) != null)), Convert(Param_0.FooValue.One(1).Length), null)"); #endif - } + } - [Fact] - public void DynamicExpressionParser_ParseLambda_NullPropagation_InstanceMethod_Two_Arguments() + [Fact] + public void DynamicExpressionParser_ParseLambda_NullPropagation_InstanceMethod_2_Arguments() + { + // Arrange + var customTypeProvider = new Mock(); + customTypeProvider.Setup(c => c.GetCustomTypes()).Returns(new HashSet { typeof(Foo) }); + var config = new ParsingConfig { - // Arrange - var expression = "np(FooValue.Two(1, 42).Length)"; + CustomTypeProvider = customTypeProvider.Object + }; + var expression = "np(FooValue.Two(1, 42).Length)"; - // Act - var lambdaExpression = DynamicExpressionParser.ParseLambda(typeof(Foo), null, expression, new Foo()); + // Act + var lambdaExpression = DynamicExpressionParser.ParseLambda(config, typeof(Foo), null, expression, new Foo()); - // Assert + // Assert #if NETCOREAPP3_1 - lambdaExpression.ToString().Should().Be("Param_0 => IIF((((Param_0 != null) AndAlso (Param_0.FooValue != null)) AndAlso (Param_0.FooValue.Two(1, 42) != null)), Convert(Param_0.FooValue.Two(1, 42).Length, Nullable`1), null)"); + lambdaExpression.ToString().Should().Be("Param_0 => IIF((((Param_0 != null) AndAlso (Param_0.FooValue != null)) AndAlso (Param_0.FooValue.Two(1, 42) != null)), Convert(Param_0.FooValue.Two(1, 42).Length, Nullable`1), null)"); #else - lambdaExpression.ToString().Should().Be("Param_0 => IIF((((Param_0 != null) AndAlso (Param_0.FooValue != null)) AndAlso (Param_0.FooValue.Two(1, 42) != null)), Convert(Param_0.FooValue.Two(1, 42).Length), null)"); + lambdaExpression.ToString().Should().Be("Param_0 => IIF((((Param_0 != null) AndAlso (Param_0.FooValue != null)) AndAlso (Param_0.FooValue.Two(1, 42) != null)), Convert(Param_0.FooValue.Two(1, 42).Length), null)"); #endif - } + } - [Fact] - public void DynamicExpressionParser_ParseLambda_NullPropagation_MethodCallExpression() - { - // Arrange - var myClass = new MyClass(); - var dataSource = new { MyClasses = new[] { myClass, null } }; + [Fact] + public void DynamicExpressionParser_ParseLambda_NullPropagation_MethodCallExpression() + { + // Arrange + var myClass = new MyClass(); + var dataSource = new { MyClasses = new[] { myClass, null } }; - var expressionText = "np(MyClasses.FirstOrDefault())"; + var expressionText = "np(MyClasses.FirstOrDefault())"; - // Act - var expression = DynamicExpressionParser.ParseLambda(ParsingConfig.Default, dataSource.GetType(), typeof(MyClass), expressionText); - var del = expression.Compile(); - var result = del.DynamicInvoke(dataSource) as MyClass; + // Act + var expression = DynamicExpressionParser.ParseLambda(ParsingConfig.Default, dataSource.GetType(), typeof(MyClass), expressionText); + var del = expression.Compile(); + var result = del.DynamicInvoke(dataSource) as MyClass; - // Assert - result.Should().Be(myClass); - } + // Assert + result.Should().Be(myClass); + } - [Theory] - [InlineData("np(MyClasses.FirstOrDefault().Name)")] - [InlineData("np(MyClasses.FirstOrDefault(Name == \"a\").Name)")] - public void DynamicExpressionParser_ParseLambda_NullPropagation_MethodCallExpression_With_Property(string expressionText) - { - // Arrange - var dataSource = new MyClass(); + [Theory] + [InlineData("np(MyClasses.FirstOrDefault().Name)")] + [InlineData("np(MyClasses.FirstOrDefault(Name == \"a\").Name)")] + public void DynamicExpressionParser_ParseLambda_NullPropagation_MethodCallExpression_With_Property(string expressionText) + { + // Arrange + var dataSource = new MyClass(); - // Act - var expression = DynamicExpressionParser.ParseLambda(ParsingConfig.Default, dataSource.GetType(), typeof(string), expressionText); - var del = expression.Compile(); - var result = del.DynamicInvoke(dataSource) as string; + // Act + var expression = DynamicExpressionParser.ParseLambda(ParsingConfig.Default, dataSource.GetType(), typeof(string), expressionText); + var del = expression.Compile(); + var result = del.DynamicInvoke(dataSource) as string; - // Assert - result.Should().BeNull(); - } + // Assert + result.Should().BeNull(); + } - [Fact] - public void DynamicExpressionParser_ParseLambda_ActionDelegate_VoidMethodCallExpression() - { - // Arrange - var dataSource = new MyClass(); - var expressionText = "it.Bar()"; - var parsingConfig = new ParsingConfig { CustomTypeProvider = new MyClassCustomTypeProvider() }; - dataSource.Name.Should().BeNull(); - - // Act - var expression = DynamicExpressionParser.ParseLambda(typeof(Action), parsingConfig, dataSource.GetType(), null, expressionText); - var del = expression.Compile(); - del.DynamicInvoke(dataSource); - - // Assert - dataSource.Name.Should().NotBeNullOrEmpty(); - } + [Fact] + public void DynamicExpressionParser_ParseLambda_ActionDelegate_VoidMethodCallExpression() + { + // Arrange + var dataSource = new MyClass(); + var expressionText = "it.Bar()"; + var parsingConfig = new ParsingConfig { CustomTypeProvider = new MyClassCustomTypeProvider() }; + dataSource.Name.Should().BeNull(); + + // Act + var expression = DynamicExpressionParser.ParseLambda(typeof(Action), parsingConfig, dataSource.GetType(), null, expressionText); + var del = expression.Compile(); + del.DynamicInvoke(dataSource); + + // Assert + dataSource.Name.Should().NotBeNullOrEmpty(); + } - [Fact] - public void DynamicExpressionParser_ParseLambda_String_TrimEnd_0_Parameters() - { - // Act - var expression = DynamicExpressionParser.ParseLambda(new ParsingConfig(), false, "TrimEnd().EndsWith(@0)", "test"); + [Fact] + public void DynamicExpressionParser_ParseLambda_String_TrimEnd_0_Parameters() + { + // Act + var expression = DynamicExpressionParser.ParseLambda(new ParsingConfig(), false, "TrimEnd().EndsWith(@0)", "test"); - var @delegate = expression.Compile(); + var @delegate = expression.Compile(); - var result = (bool)@delegate.DynamicInvoke("This is a test "); + var result = (bool)@delegate.DynamicInvoke("This is a test "); - // Assert - result.Should().BeTrue(); - } + // Assert + result.Should().BeTrue(); + } - [Fact] - public void DynamicExpressionParser_ParseLambda_String_TrimEnd_1_Parameter() - { - // Act - var expression = DynamicExpressionParser.ParseLambda(new ParsingConfig(), false, "TrimEnd('.').EndsWith(@0)", "test"); + [Fact] + public void DynamicExpressionParser_ParseLambda_String_TrimEnd_1_Parameter() + { + // Act + var expression = DynamicExpressionParser.ParseLambda(new ParsingConfig(), false, "TrimEnd('.').EndsWith(@0)", "test"); - var @delegate = expression.Compile(); + var @delegate = expression.Compile(); - var result = (bool)@delegate.DynamicInvoke("This is a test..."); + var result = (bool)@delegate.DynamicInvoke("This is a test..."); - // Assert - result.Should().BeTrue(); - } + // Assert + result.Should().BeTrue(); + } - public class DefaultDynamicLinqCustomTypeProviderForGenericExtensionMethod : CustomTypeProviders.DefaultDynamicLinqCustomTypeProvider - { - public override HashSet GetCustomTypes() => new HashSet(base.GetCustomTypes()) { typeof(Methods), typeof(MethodsItemExtension) }; - } + public class DefaultDynamicLinqCustomTypeProviderForGenericExtensionMethod : CustomTypeProviders.DefaultDynamicLinqCustomTypeProvider + { + public override HashSet GetCustomTypes() => new HashSet(base.GetCustomTypes()) { typeof(Methods), typeof(MethodsItemExtension) }; + } - [Fact] - public void DynamicExpressionParser_ParseLambda_GenericExtensionMethod() + [Fact] + public void DynamicExpressionParser_ParseLambda_GenericExtensionMethod() + { + // Arrange + var testList = User.GenerateSampleModels(51); + var config = new ParsingConfig { - // Arrange - var testList = User.GenerateSampleModels(51); - var config = new ParsingConfig - { - CustomTypeProvider = new DefaultDynamicLinqCustomTypeProviderForGenericExtensionMethod(), - PrioritizePropertyOrFieldOverTheType = true - }; + CustomTypeProvider = new DefaultDynamicLinqCustomTypeProviderForGenericExtensionMethod(), + PrioritizePropertyOrFieldOverTheType = true + }; - // Act - var query = "x => MethodsItemExtension.Functions.EfCoreCollate(x.UserName, \"tlh-KX\")==\"User4\" || MethodsItemExtension.Functions.EfCoreCollate(x.UserName, \"tlh-KX\")==\"User2\""; - var expression = DynamicExpressionParser.ParseLambda(config, false, query); - var del = expression.Compile(); + // Act + var query = "x => MethodsItemExtension.Functions.EfCoreCollate(x.UserName, \"tlh-KX\")==\"User4\" || MethodsItemExtension.Functions.EfCoreCollate(x.UserName, \"tlh-KX\")==\"User2\""; + var expression = DynamicExpressionParser.ParseLambda(config, false, query); + var del = expression.Compile(); - var result = Enumerable.Where(testList, del); + var result = Enumerable.Where(testList, del); - var expected = testList.Where(x => new string[] { "User4", "User2" }.Contains(x.UserName)).ToList(); + var expected = testList.Where(x => new string[] { "User4", "User2" }.Contains(x.UserName)).ToList(); - // Assert - Check.That(result).IsNotNull(); - Check.That(result).HasSize(expected.Count); - Check.That(result).Equals(expected); - } + // Assert + Check.That(result).IsNotNull(); + Check.That(result).HasSize(expected.Count); + Check.That(result).Equals(expected); + } - [Fact] - public void DynamicExpressionParser_ParseLambda_Action() - { - // Arrange - var action = (Action)DynamicExpressionParser.ParseLambda(typeof(Action), new[] { Expression.Parameter(typeof(int), "x") }, typeof(int), "x + 1").Compile(); + [Fact] + public void DynamicExpressionParser_ParseLambda_Action() + { + // Arrange + var action = (Action)DynamicExpressionParser.ParseLambda(typeof(Action), new[] { Expression.Parameter(typeof(int), "x") }, typeof(int), "x + 1").Compile(); - // Act - action(3); - } + // Act + action(3); + } - [Fact] - public void DynamicExpressionParser_ParseLambda_Func() - { - // Arrange - var func = (Func)DynamicExpressionParser.ParseLambda( - typeof(Func), - new[] - { - Expression.Parameter(typeof(int), "x") - }, - typeof(int), - "x + 1" - ).Compile(); - - // Act - var result = func(4); - - // Assert - result.Should().Be(5); - } + [Fact] + public void DynamicExpressionParser_ParseLambda_Func() + { + // Arrange + var func = (Func)DynamicExpressionParser.ParseLambda( + typeof(Func), + new[] + { + Expression.Parameter(typeof(int), "x") + }, + typeof(int), + "x + 1" + ).Compile(); - [Theory] - [InlineData("value", "value != null && value == 1", 1, true)] - [InlineData("value", "value != null && value == 1", 5, false)] - [InlineData("x", "value != null && value == 1", 1, true)] - [InlineData("x", "value != null && value == 1", 5, false)] - [InlineData(null, "value != null && value == 1", 1, true)] - [InlineData(null, "value != null && value == 1", 5, false)] - [InlineData("value", "value => value != null && value == 1", 1, true)] - [InlineData("value", "value => value != null && value == 1", 5, false)] - public void DynamicExpressionParser_ParseLambda_Func2(string? paramName, string test, int? input, bool expected) - { - // Arrange - var nullableType = typeof(int?); - var delegateType = typeof(Func<,>).MakeGenericType(nullableType, typeof(bool)); - var valueParameter = paramName is not null ? Expression.Parameter(nullableType, paramName) : Expression.Parameter(nullableType); - - // Act 1 - var expression = DynamicExpressionParser.ParseLambda( - delegateType, - new ParsingConfig(), - new[] { valueParameter }, - typeof(bool), - test - ); - - // Act 2 - var compiledExpression = expression.Compile(); - var result = compiledExpression.DynamicInvoke(input); - - // Assert - result.Should().Be(expected); - } + // Act + var result = func(4); + + // Assert + result.Should().Be(5); + } + + [Theory] + [InlineData("value", "value != null && value == 1", 1, true)] + [InlineData("value", "value != null && value == 1", 5, false)] + [InlineData("x", "value != null && value == 1", 1, true)] + [InlineData("x", "value != null && value == 1", 5, false)] + [InlineData(null, "value != null && value == 1", 1, true)] + [InlineData(null, "value != null && value == 1", 5, false)] + [InlineData("value", "value => value != null && value == 1", 1, true)] + [InlineData("value", "value => value != null && value == 1", 5, false)] + public void DynamicExpressionParser_ParseLambda_Func2(string? paramName, string test, int? input, bool expected) + { + // Arrange + var nullableType = typeof(int?); + var delegateType = typeof(Func<,>).MakeGenericType(nullableType, typeof(bool)); + var valueParameter = paramName is not null ? Expression.Parameter(nullableType, paramName) : Expression.Parameter(nullableType); + + // Act 1 + var expression = DynamicExpressionParser.ParseLambda( + delegateType, + new ParsingConfig(), + new[] { valueParameter }, + typeof(bool), + test + ); + + // Act 2 + var compiledExpression = expression.Compile(); + var result = compiledExpression.DynamicInvoke(input); + + // Assert + result.Should().Be(expected); } } \ No newline at end of file diff --git a/test/System.Linq.Dynamic.Core.Tests/ExpressionTests.MethodCall.cs b/test/System.Linq.Dynamic.Core.Tests/ExpressionTests.MethodCall.cs new file mode 100644 index 00000000..b858dc5c --- /dev/null +++ b/test/System.Linq.Dynamic.Core.Tests/ExpressionTests.MethodCall.cs @@ -0,0 +1,253 @@ +using System.Collections.Generic; +using System.Linq.Dynamic.Core.CustomTypeProviders; +using System.Linq.Dynamic.Core.Tests.Helpers.Models; +using FluentAssertions; +using Moq; +using Xunit; + +namespace System.Linq.Dynamic.Core.Tests; + +public partial class ExpressionTests +{ + private static ParsingConfig CreateParsingConfigForMethodCallTests() + { + var customTypeProvider = new Mock(); + customTypeProvider.Setup(c => c.GetCustomTypes()).Returns(new HashSet { typeof(User), typeof(Methods), typeof(Foo) }); + return new ParsingConfig + { + CustomTypeProvider = customTypeProvider.Object + }; + } + + private class DefaultDynamicLinqCustomTypeProviderForStaticTesting : DefaultDynamicLinqCustomTypeProvider + { + public override HashSet GetCustomTypes() => new(base.GetCustomTypes()) { typeof(Methods), typeof(MethodsItemExtension) }; + } + + private static ParsingConfig CreateParsingConfigForStaticMethodCallTests() + { + return new ParsingConfig + { + CustomTypeProvider = new DefaultDynamicLinqCustomTypeProviderForStaticTesting(), + PrioritizePropertyOrFieldOverTheType = true + }; + } + + [Fact] + public void ExpressionTests_MethodCall_NoParams() + { + // Arrange + var config = CreateParsingConfigForMethodCallTests(); + var users = User.GenerateSampleModels(3); + + // Act + var expected = users.Where(u => u.TestMethod1()); + var result = users.AsQueryable().Where(config, "TestMethod1()"); + + // Assert + Assert.Equal(expected.Count(), result.Count()); + } + + [Fact] + public void ExpressionTests_MethodCall_OneParam_With_it() + { + // Arrange + var config = CreateParsingConfigForMethodCallTests(); + var users = User.GenerateSampleModels(3); + + // Act + var expected = users.Where(u => u.TestMethod2(u)); + var result = users.AsQueryable().Where(config, "TestMethod2(it)"); + + // Assert + Assert.Equal(expected.Count(), result.Count()); + } + + + [Fact] + public void ExpressionTests_MethodCall_OneParam_With_User() + { + // Arrange + var config = CreateParsingConfigForMethodCallTests(); + var users = User.GenerateSampleModels(10); + var testUser = users[2]; + + // Act + var expected = users.Where(u => u.TestMethod3(testUser)); + var result = users.AsQueryable().Where(config, "TestMethod3(@0)", testUser); + + // Assert + Assert.Equal(expected.Count(), result.Count()); + } + + [Fact] + public void ExpressionTests_MethodCall_GenericStatic() + { + // Arrange + var config = CreateParsingConfigForStaticMethodCallTests(); + var list = new[] { 0, 1, 2, 3, 4 }.Select(value => new Methods.Item { Value = value }).ToArray(); + + // Act + var expectedResult = list.Where(x => Methods.StaticGenericMethod(x)); + var result = list.AsQueryable().Where(config, "Methods.StaticGenericMethod(it)"); + + // Assert + Assert.Equal(expectedResult.Count(), result.Count()); + } + + [Fact] + public void ExpressionTests_MethodCall_Generic() + { + // Arrange + var config = CreateParsingConfigForMethodCallTests(); + var list = new[] { 0, 1, 2, 3, 4 }.Select(value => new Methods.Item { Value = value }).ToArray(); + + // Act + var methods = new Methods(); + var expectedResult = list.Where(x => methods.GenericMethod(x)); + var result = list.AsQueryable().Where(config, "@0.GenericMethod(it)", methods); + + // Assert + Assert.Equal(expectedResult.Count(), result.Count()); + } + + [Fact] + public void ExpressionTests_MethodCall_GenericExtension() + { + // Arrange + var config = CreateParsingConfigForStaticMethodCallTests(); + var list = new[] { 0, 1, 2, 3, 4 }.Select(value => new Methods.Item { Value = value }).ToArray(); + + // Act + var expectedResult = list.Where(x => MethodsItemExtension.Functions.EfCoreCollate(x.Value, "tlh-KX") == 2); + var result = list.AsQueryable().Where(config, "MethodsItemExtension.Functions.EfCoreCollate(it.Value,\"tlh-KX\")==2"); + + // Assert + Assert.Equal(expectedResult.Count(), result.Count()); + } + + [Fact] + public void ExpressionTests_MethodCall_ValueTypeToValueTypeParameter() + { + // Arrange + var config = CreateParsingConfigForMethodCallTests(); + var list = new[] { 0, 1, 2, 3, 4 }; + + // Act + var methods = new Methods(); + var expectedResult = list.Where(x => methods.Method1(x)); + var result = list.AsQueryable().Where(config, "@0.Method1(it)", methods); + + // Assert + Assert.Equal(expectedResult.Count(), result.Count()); + } + + [Fact] + public void ExpressionTests_MethodCall_ValueTypeToObjectParameterWithCast() + { + // Arrange + var config = CreateParsingConfigForMethodCallTests(); + var list = new[] { 0, 1, 2, 3, 4 }; + + // Act + var methods = new Methods(); + var expectedResult = list.Where(x => methods.Method2(x)); + var result = list.AsQueryable().Where(config, "@0.Method2(object(it))", methods); + + // Assert + Assert.Equal(expectedResult.Count(), result.Count()); + } + + [Fact] + public void ExpressionTests_MethodCall_ValueTypeToObjectParameterWithoutCast() + { + // Arrange + var config = CreateParsingConfigForMethodCallTests(); + var list = new[] { 0, 1, 2, 3, 4 }; + + // Act + var methods = new Methods(); + var expectedResult = list.Where(x => methods.Method2(x)); + var result = list.AsQueryable().Where(config, "@0.Method2(it)", methods); + + // Assert + Assert.Equal(expectedResult.Count(), result.Count()); + } + + [Fact] + public void ExpressionTests_MethodCall_NullableValueTypeToObjectParameter() + { + // Arrange + var config = CreateParsingConfigForMethodCallTests(); + var list = new int?[] { 0, 1, 2, 3, 4, null }; + + // Act + var methods = new Methods(); + var expectedResult = list.Where(x => methods.Method2(x)); + var result = list.AsQueryable().Where(config, "@0.Method2(it)", methods); + + // Assert + Assert.Equal(expectedResult.Count(), result.Count()); + } + + [Fact] + public void ExpressionTests_MethodCall_ReferenceTypeToObjectParameter() + { + // Arrange + var config = CreateParsingConfigForMethodCallTests(); + var list = new[] { 0, 1, 2, 3, 4 }.Select(value => new Methods.Item { Value = value }).ToArray(); + + // Act + var methods = new Methods(); + var expectedResult = list.Where(x => methods.Method3(x)); + var result = list.AsQueryable().Where(config, "@0.Method3(it)", methods); + + // Assert + Assert.Equal(expectedResult.Count(), result.Count()); + } + + [Fact] + public void ExpressionTests_NullPropagating_InstanceMethod_0_Arguments() + { + // Arrange + var config = CreateParsingConfigForMethodCallTests(); + var expression = "np(FooValue.Zero().Length)"; + var q = new[] { new Foo { FooValue = new Foo() } }.AsQueryable(); + + // Act + var result = q.Select(config, expression).FirstOrDefault() as int?; + + // Assert + result.Should().BeNull(); + } + + [Fact] + public void ExpressionTests_NullPropagating_InstanceMethod_1_Argument() + { + // Arrange + var config = CreateParsingConfigForMethodCallTests(); + var expression = "np(FooValue.One(1).Length)"; + var q = new[] { new Foo { FooValue = new Foo() } }.AsQueryable(); + + // Act + var result = q.Select(config, expression).FirstOrDefault() as int?; + + // Assert + result.Should().BeNull(); + } + + [Fact] + public void ExpressionTests_NullPropagating_InstanceMethod_2_Arguments() + { + // Arrange + var config = CreateParsingConfigForMethodCallTests(); + var expression = "np(FooValue.Two(1, 42).Length)"; + var q = new[] { new Foo { FooValue = new Foo() } }.AsQueryable(); + + // Act + var result = q.Select(config, expression).FirstOrDefault() as int?; + + // Assert + result.Should().BeNull(); + } +} \ No newline at end of file diff --git a/test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs b/test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs index f951d6e3..5d7e7a9c 100644 --- a/test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs +++ b/test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs @@ -23,7 +23,7 @@ public enum TestEnumPublic : sbyte Var6 = 16 } - public class ExpressionTests + public partial class ExpressionTests { public enum TestEnum2 : sbyte { @@ -1480,174 +1480,6 @@ public void ExpressionTests_LogicalAndOr() Assert.Equal(qry.Where(x => (x & 32) > 0).ToArray(), result2.ToArray()); } - [Fact] - public void ExpressionTests_Method_NoParams() - { - // Arrange - var users = User.GenerateSampleModels(3); - - // Act - var expected = users.Where(u => u.TestMethod1()); - var result = users.AsQueryable().Where("TestMethod1()"); - - // Assert - Assert.Equal(expected.Count(), result.Count()); - } - - [Fact] - public void ExpressionTests_Method_OneParam_With_it() - { - // Arrange - var users = User.GenerateSampleModels(3); - - // Act - var expected = users.Where(u => u.TestMethod2(u)); - var result = users.AsQueryable().Where("TestMethod2(it)"); - - // Assert - Assert.Equal(expected.Count(), result.Count()); - } - - public class DefaultDynamicLinqCustomTypeProviderForStaticTesting : CustomTypeProviders.DefaultDynamicLinqCustomTypeProvider - { - public override HashSet GetCustomTypes() => new HashSet(base.GetCustomTypes()) { typeof(Methods), typeof(MethodsItemExtension) }; - } - - [Fact] - public void ExpressionTests_MethodCall_GenericStatic() - { - var config = new ParsingConfig - { - CustomTypeProvider = new DefaultDynamicLinqCustomTypeProviderForStaticTesting() - }; - - // Arrange - var list = new[] { 0, 1, 2, 3, 4 }.Select(value => new Methods.Item { Value = value }).ToArray(); - - // Act - var expectedResult = list.Where(x => Methods.StaticGenericMethod(x)); - var result = list.AsQueryable().Where(config, "Methods.StaticGenericMethod(it)"); - - // Assert - Assert.Equal(expectedResult.Count(), result.Count()); - } - - [Fact] - public void ExpressionTests_MethodCall_Generic() - { - var config = new ParsingConfig - { - CustomTypeProvider = new DefaultDynamicLinqCustomTypeProviderForStaticTesting() - }; - - // Arrange - var list = new[] { 0, 1, 2, 3, 4 }.Select(value => new Methods.Item { Value = value }).ToArray(); - - // Act - var methods = new Methods(); - var expectedResult = list.Where(x => methods.GenericMethod(x)); - var result = list.AsQueryable().Where("@0.GenericMethod(it)", methods); - - // Assert - Assert.Equal(expectedResult.Count(), result.Count()); - } - - [Fact] - public void ExpressionTests_MethodCall_GenericExtension() - { - var config = new ParsingConfig - { - CustomTypeProvider = new DefaultDynamicLinqCustomTypeProviderForStaticTesting(), - PrioritizePropertyOrFieldOverTheType = true - }; - - // Arrange - var list = new[] { 0, 1, 2, 3, 4 }.Select(value => new Methods.Item { Value = value }).ToArray(); - - // Act - var methods = new Methods(); - var expectedResult = list.Where(x => MethodsItemExtension.Functions.EfCoreCollate(x.Value, "tlh-KX") == 2); - var result = list.AsQueryable().Where(config, "MethodsItemExtension.Functions.EfCoreCollate(it.Value,\"tlh-KX\")==2"); - - // Assert - Assert.Equal(expectedResult.Count(), result.Count()); - } - - [Fact] - public void ExpressionTests_MethodCall_ValueTypeToValueTypeParameter() - { - // Arrange - var list = new[] { 0, 1, 2, 3, 4 }; - - // Act - var methods = new Methods(); - var expectedResult = list.Where(x => methods.Method1(x)); - var result = list.AsQueryable().Where("@0.Method1(it)", methods); - - // Assert - Assert.Equal(expectedResult.Count(), result.Count()); - } - - [Fact] - public void ExpressionTests_MethodCall_ValueTypeToObjectParameterWithCast() - { - // Arrange - var list = new[] { 0, 1, 2, 3, 4 }; - - // Act - var methods = new Methods(); - var expectedResult = list.Where(x => methods.Method2(x)); - var result = list.AsQueryable().Where("@0.Method2(object(it))", methods); - - // Assert - Assert.Equal(expectedResult.Count(), result.Count()); - } - - [Fact] - public void ExpressionTests_MethodCall_ValueTypeToObjectParameterWithoutCast() - { - // Arrange - var list = new[] { 0, 1, 2, 3, 4 }; - - // Act - var methods = new Methods(); - var expectedResult = list.Where(x => methods.Method2(x)); - var result = list.AsQueryable().Where("@0.Method2(it)", methods); - - // Assert - Assert.Equal(expectedResult.Count(), result.Count()); - } - - [Fact] - public void ExpressionTests_MethodCall_NullableValueTypeToObjectParameter() - { - // Arrange - var list = new int?[] { 0, 1, 2, 3, 4, null }; - - // Act - var methods = new Methods(); - var expectedResult = list.Where(x => methods.Method2(x)); - var result = list.AsQueryable().Where("@0.Method2(it)", methods); - - // Assert - Assert.Equal(expectedResult.Count(), result.Count()); - } - - [Fact] - public void ExpressionTests_MethodCall_ReferenceTypeToObjectParameter() - { - // Arrange - var list = new[] { 0, 1, 2, 3, 4 }.Select(value => new Methods.Item { Value = value }).ToArray(); - - // Act - var methods = new Methods(); - var expectedResult = list.Where(x => methods.Method3(x)); - var result = list.AsQueryable().Where("@0.Method3(it)", methods); - - // Assert - Assert.Equal(expectedResult.Count(), result.Count()); - } - [Fact] public void ExpressionTests_NewAnonymousType_Paren() { @@ -1676,21 +1508,6 @@ public void ExpressionTests_NewAnonymousType_CurlyParen() Check.That(result).Equals(expectedResult); } - [Fact] - public void ExpressionTests_Method_OneParam_With_user() - { - // Arrange - var users = User.GenerateSampleModels(10); - var testUser = users[2]; - - // Act - var expected = users.Where(u => u.TestMethod3(testUser)); - var result = users.AsQueryable().Where("TestMethod3(@0)", testUser); - - // Assert - Assert.Equal(expected.Count(), result.Count()); - } - [Fact] public void ExpressionTests_Modulo_Number() { @@ -1899,50 +1716,7 @@ public void ExpressionTests_NullPropagating_Config_Has_UseDefault(string test, s queryAsString = queryAsString.Substring(queryAsString.IndexOf(".Select") + 1).TrimEnd(']'); Check.That(queryAsString).Equals(query); } - - - [Fact] - public void ExpressionTests_NullPropagating_InstanceMethod_Zero_Arguments() - { - // Arrange 1 - var expression = "np(FooValue.Zero().Length)"; - var q = new[] { new Foo { FooValue = new Foo() } }.AsQueryable(); - - // Act 2 - var result = q.Select(expression).FirstOrDefault() as int?; - - // Assert 2 - result.Should().BeNull(); - } - - [Fact] - public void ExpressionTests_NullPropagating_InstanceMethod_One_Argument() - { - // Arrange - var expression = "np(FooValue.One(1).Length)"; - var q = new[] { new Foo { FooValue = new Foo() } }.AsQueryable(); - - // Act - var result = q.Select(expression).FirstOrDefault() as int?; - - // Assert - result.Should().BeNull(); - } - - [Fact] - public void ExpressionTests_NullPropagating_InstanceMethod_Two_Arguments() - { - // Arrange - var expression = "np(FooValue.Two(1, 42).Length)"; - var q = new[] { new Foo { FooValue = new Foo() } }.AsQueryable(); - - // Act - var result = q.Select(expression).FirstOrDefault() as int?; - - // Assert - result.Should().BeNull(); - } - + [Fact] public void ExpressionTests_NullPropagation_Method() { diff --git a/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Methods.cs b/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Methods.cs index 21ad0d94..04b14dd4 100644 --- a/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Methods.cs +++ b/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Methods.cs @@ -1,64 +1,84 @@ -using Xunit; +using System.Collections.Generic; +using System.Linq.Dynamic.Core.CustomTypeProviders; +using Moq; +using Xunit; -namespace System.Linq.Dynamic.Core.Tests +namespace System.Linq.Dynamic.Core.Tests; + +public partial class QueryableTests { - public partial class QueryableTests + [Fact] + public void CallMethod() { - [Fact] - public void CallMethod() + // Arrange + var customTypeProvider = new Mock(); + customTypeProvider.Setup(c => c.GetCustomTypes()).Returns(new HashSet { typeof(Test) }); + var config = new ParsingConfig { - // Arrange - var query = new[] { new Test() }.AsQueryable(); + CustomTypeProvider = customTypeProvider.Object + }; + var query = new[] { new Test() }.AsQueryable(); - // Act - var result = query.Select("t => t.GetDecimal()").First(); + // Act + var result = query.Select(config, "t => t.GetDecimal()").First(); - // Assert - Assert.Equal(42, result); - } + // Assert + Assert.Equal(42, result); + } - [Fact] - public void CallMethodWhichReturnsNullable() + [Fact] + public void CallMethodWhichReturnsNullable() + { + // Arrange + var customTypeProvider = new Mock(); + customTypeProvider.Setup(c => c.GetCustomTypes()).Returns(new HashSet { typeof(Test) }); + var config = new ParsingConfig { - // Arrange - var query = new[] { new Test() }.AsQueryable(); + CustomTypeProvider = customTypeProvider.Object + }; + var query = new[] { new Test() }.AsQueryable(); - // Act - var result = query.Select("t => t.GetNullableDecimal()").First(); + // Act + var result = query.Select(config, "t => t.GetNullableDecimal()").First(); - // Assert - Assert.Equal(null, result); - } + // Assert + Assert.Equal(null, result); + } - [Fact] - public void CallMethodWhichReturnsNullable_WithValue() + [Fact] + public void CallMethodWhichReturnsNullable_WithValue() + { + // Arrange + var customTypeProvider = new Mock(); + customTypeProvider.Setup(c => c.GetCustomTypes()).Returns(new HashSet { typeof(Test) }); + var config = new ParsingConfig { - // Arrange - var query = new[] { new Test() }.AsQueryable(); + CustomTypeProvider = customTypeProvider.Object + }; + var query = new[] { new Test() }.AsQueryable(); - // Act - var result = query.Select("t => t.GetNullableDecimalWithValue()").First(); + // Act + var result = query.Select(config, "t => t.GetNullableDecimalWithValue()").First(); - // Assert - Assert.Equal(100, result); - } + // Assert + Assert.Equal(100, result); } +} - class Test +class Test +{ + public decimal GetDecimal() { - public decimal GetDecimal() - { - return 42; - } + return 42; + } - public decimal? GetNullableDecimal() - { - return null; - } + public decimal? GetNullableDecimal() + { + return null; + } - public decimal? GetNullableDecimalWithValue() - { - return 100; - } + public decimal? GetNullableDecimalWithValue() + { + return 100; } -} +} \ No newline at end of file diff --git a/test/System.Linq.Dynamic.Core.Tests/SecurityTests.cs b/test/System.Linq.Dynamic.Core.Tests/SecurityTests.cs new file mode 100644 index 00000000..77fa0c76 --- /dev/null +++ b/test/System.Linq.Dynamic.Core.Tests/SecurityTests.cs @@ -0,0 +1,67 @@ +using System.IO; +using System.Linq.Dynamic.Core.Exceptions; +using System.Net; +using System.Reflection; +using FluentAssertions; +using Xunit; + +namespace System.Linq.Dynamic.Core.Tests; + +public class SecurityTests +{ + class Message + { + public string Sender { get; } + public string Receiver { get; } + + public Message(string sender, string receiver) + { + Sender = sender; + Receiver = receiver; + } + } + + [Fact] + public void MethodsShouldOnlyBeCallableOnPredefinedTypes_Test1() + { + // Arrange + var baseQuery = new[] { 1, 2, 3 }.AsQueryable(); + string predicate = "\"\".GetType().Assembly.DefinedTypes.Where(it.name == \"Assembly\").First().DeclaredMethods.Where(it.Name == \"GetName\").First().Invoke(\"\".GetType().Assembly, new Object[] {} ).Name.ToString() != \"Test\""; + + // Act + Action action = () => baseQuery.OrderBy(predicate); + + // Assert + action.Should().Throw().WithMessage("Methods on type 'MethodBase' are not accessible"); + } + + [Fact] + public void MethodsShouldOnlyBeCallableOnPredefinedTypes_Test2() + { + // Arrange + var messages = new[] + { + new Message("Alice", "Bob"), + new Message("Bob", "Alice") + }.AsQueryable(); + + Action action = () => messages.Where( + "\"\".GetType().Assembly.GetType(\"System.AppDomain\").GetMethods()[104].Invoke(\"\".GetType().Assembly.GetType(\"System.AppDomain\").GetProperty(\"CurrentDomain\").GetValue(null), \"System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;System.Diagnostics.Process\".Split(\";\".ToCharArray())).GetType().GetMethods()[80].Invoke(null, \"cmd;/T:4A /K whoami && echo was HACKED\".Split(\";\".ToCharArray()))" + ); + + // Assert + action.Should().Throw().WithMessage($"Methods on type 'Assembly' are not accessible"); + } + + [Theory] + [InlineData(typeof(FileStream), "Close()", "Stream")] + [InlineData(typeof(Assembly), "GetName().Name.ToString()", "Assembly")] + public void DynamicExpressionParser_ParseLambda_IllegalMethodCall_ThrowsException(Type itType, string expression, string type) + { + // Act + Action action = () => DynamicExpressionParser.ParseLambda(itType, null, expression); + + // Assert + action.Should().Throw().WithMessage($"Methods on type '{type}' are not accessible"); + } +} \ No newline at end of file