From 7fc67440b931a89b148adaf0fd3ed57ccb887e13 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Mon, 7 Aug 2023 22:25:33 +0200 Subject: [PATCH] Add logic to convert any array to object array. --- src-console/ConsoleApp_net6.0/Program.cs | 53 ++- .../Parser/ExpressionHelper.cs | 36 +- .../Parser/ExpressionParser.cs | 16 +- .../Parser/IExpressionHelper.cs | 2 + .../Parser/SupportedMethods/MethodFinder.cs | 34 +- .../DynamicExpressionParserTests.cs | 63 +++ .../Parser/ExpressionHelperTests.cs | 435 +++++++++--------- 7 files changed, 411 insertions(+), 228 deletions(-) diff --git a/src-console/ConsoleApp_net6.0/Program.cs b/src-console/ConsoleApp_net6.0/Program.cs index b471cac2..15323cc9 100644 --- a/src-console/ConsoleApp_net6.0/Program.cs +++ b/src-console/ConsoleApp_net6.0/Program.cs @@ -1,6 +1,8 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Linq.Dynamic.Core; +using System.Linq.Expressions; namespace ConsoleApp_net6._0 { @@ -19,6 +21,11 @@ class Program { static void Main(string[] args) { + Issue389DoesNotWork(); + return; + Issue389_Works(); + return; + var q = new[] { new X { Key = "x" }, @@ -32,6 +39,50 @@ static void Main(string[] args) Dynamic(); } + private static void Issue389_Works() + { + var strArray = new[] { "1", "2", "3", "4" }; + var x = new List(); + x.Add(Expression.Parameter(strArray.GetType(), "strArray")); + + string query = "string.Join(\",\", strArray)"; + + var e = DynamicExpressionParser.ParseLambda(x.ToArray(), null, query); + Delegate del = e.Compile(); + var result1 = del.DynamicInvoke(new object?[] { strArray }); + Console.WriteLine(result1); + } + + private static void Issue389WorksWithInts() + { + var intArray = new object[] { 1, 2, 3, 4 }; + var x = new List(); + x.Add(Expression.Parameter(intArray.GetType(), "intArray")); + + string query = "string.Join(\",\", intArray)"; + + var e = DynamicExpressionParser.ParseLambda(x.ToArray(), null, query); + Delegate del = e.Compile(); + var result = del.DynamicInvoke(new object?[] { intArray }); + + Console.WriteLine(result); + } + + private static void Issue389DoesNotWork() + { + var intArray = new [] { 1, 2, 3, 4 }; + var x = new List(); + x.Add(Expression.Parameter(intArray.GetType(), "intArray")); + + string query = "string.Join(\",\", intArray)"; + + var e = DynamicExpressionParser.ParseLambda(x.ToArray(), null, query); + Delegate del = e.Compile(); + var result = del.DynamicInvoke(new object?[] { intArray }); + + Console.WriteLine(result); + } + private static void Normal() { var e = new int[0].AsQueryable(); diff --git a/src/System.Linq.Dynamic.Core/Parser/ExpressionHelper.cs b/src/System.Linq.Dynamic.Core/Parser/ExpressionHelper.cs index fa6a6b95..b5e722fe 100644 --- a/src/System.Linq.Dynamic.Core/Parser/ExpressionHelper.cs +++ b/src/System.Linq.Dynamic.Core/Parser/ExpressionHelper.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System.Collections; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq.Dynamic.Core.Validation; @@ -302,11 +303,6 @@ public Expression ConvertToExpandoObjectAndCreateDynamicExpression(Expression ex #endif } - private Expression GenerateStaticMethodCall(string methodName, Expression left, Expression right) - { - return Expression.Call(null, GetStaticMethod(methodName, left, right), new[] { left, right }); - } - private void WrapConstantExpressions(ref Expression left, ref Expression right) { if (_parsingConfig.UseParameterizedNamesInDynamicQuery) @@ -359,6 +355,17 @@ public Expression GenerateDefaultExpression(Type type) #endif } + public Expression ConvertAnyArrayToObjectArray(Expression arrayExpression) + { + Check.NotNull(arrayExpression); + + return Expression.Call( + null, + typeof(ExpressionHelper).GetMethod(nameof(ConvertIfIEnumerableHasValues), BindingFlags.Static | BindingFlags.NonPublic)!, + arrayExpression + ); + } + private Expression? GetMemberExpression(Expression? expression) { if (ExpressionQualifiesForNullPropagation(expression)) @@ -436,6 +443,11 @@ private List CollectExpressions(bool addSelf, Expression sourceExpre return list; } + private static Expression GenerateStaticMethodCall(string methodName, Expression left, Expression right) + { + return Expression.Call(null, GetStaticMethod(methodName, left, right), new[] { left, right }); + } + private static MethodInfo GetStaticMethod(string methodName, Expression left, Expression right) { var methodInfo = left.Type.GetMethod(methodName, new[] { left.Type, right.Type }); @@ -463,4 +475,16 @@ private static MethodInfo GetStaticMethod(string methodName, Expression left, Ex { return unaryExpression?.Operand; } + + private static object[] ConvertIfIEnumerableHasValues(IEnumerable? input) + { + // ReSharper disable once PossibleMultipleEnumeration + if (input != null && input.Cast().Any()) + { + // ReSharper disable once PossibleMultipleEnumeration + return input.Cast().ToArray(); + } + + return new object[0]; + } } \ 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 dca30c44..3d376822 100644 --- a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs +++ b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs @@ -74,8 +74,8 @@ public ExpressionParser(ParameterExpression[]? parameters, string expression, ob _keywordsHelper = new KeywordsHelper(_parsingConfig); _textParser = new TextParser(_parsingConfig, expression); _numberParser = new NumberParser(parsingConfig); - _methodFinder = new MethodFinder(_parsingConfig); _expressionHelper = new ExpressionHelper(_parsingConfig); + _methodFinder = new MethodFinder(_parsingConfig, _expressionHelper); _typeFinder = new TypeFinder(_parsingConfig, _keywordsHelper); _typeConverterFactory = new TypeConverterFactory(_parsingConfig); @@ -1725,10 +1725,15 @@ private bool TryGenerateConversion(Expression sourceExpression, Type destination private Expression ParseMemberAccess(Type? type, Expression? expression) { + var isStaticAccess = false; if (expression != null) { type = expression.Type; } + else + { + isStaticAccess = true; + } int errorPos = _textParser.CurrentToken.Pos; string id = GetIdentifier(); @@ -1736,18 +1741,18 @@ private Expression ParseMemberAccess(Type? type, Expression? expression) if (_textParser.CurrentToken.Id == TokenId.OpenParen) { - if (expression != null && type != typeof(string)) + if (!isStaticAccess && type != typeof(string)) { var enumerableType = TypeHelper.FindGenericType(typeof(IEnumerable<>), type); if (enumerableType != null) { Type elementType = enumerableType.GetTypeInfo().GetGenericTypeArguments()[0]; - return ParseEnumerable(expression, elementType, id, errorPos, type); + return ParseEnumerable(expression!, elementType, id, errorPos, type); } } Expression[] args = ParseArgumentList(); - switch (_methodFinder.FindMethod(type, id, expression == null, ref expression, ref args, out var methodBase)) + switch (_methodFinder.FindMethod(type, id, isStaticAccess, ref expression, ref args, out var methodBase)) { case 0: throw ParseError(errorPos, Res.NoApplicableMethod, id, TypeHelper.GetTypeName(type)); @@ -1764,9 +1769,10 @@ private Expression ParseMemberAccess(Type? type, Expression? expression) var genericParameters = method.GetParameters().Where(p => p.ParameterType.IsGenericParameter); var typeArguments = genericParameters.Select(a => args[a.Position].Type); var constructedMethod = method.MakeGenericMethod(typeArguments.ToArray()); + return Expression.Call(expression, constructedMethod, args); } - + return Expression.Call(expression, method, args); default: diff --git a/src/System.Linq.Dynamic.Core/Parser/IExpressionHelper.cs b/src/System.Linq.Dynamic.Core/Parser/IExpressionHelper.cs index c09cf596..ce4b902e 100644 --- a/src/System.Linq.Dynamic.Core/Parser/IExpressionHelper.cs +++ b/src/System.Linq.Dynamic.Core/Parser/IExpressionHelper.cs @@ -46,4 +46,6 @@ internal interface IExpressionHelper Expression ConvertToExpandoObjectAndCreateDynamicExpression(Expression expression, Type type, string propertyName); Expression GenerateDefaultExpression(Type type); + + Expression ConvertAnyArrayToObjectArray(Expression arrayExpression); } \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core/Parser/SupportedMethods/MethodFinder.cs b/src/System.Linq.Dynamic.Core/Parser/SupportedMethods/MethodFinder.cs index 40b41848..3a926a2e 100644 --- a/src/System.Linq.Dynamic.Core/Parser/SupportedMethods/MethodFinder.cs +++ b/src/System.Linq.Dynamic.Core/Parser/SupportedMethods/MethodFinder.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq.Dynamic.Core.Validation; using System.Linq.Expressions; using System.Reflection; @@ -7,14 +8,12 @@ namespace System.Linq.Dynamic.Core.Parser.SupportedMethods internal class MethodFinder { private readonly ParsingConfig _parsingConfig; + private readonly IExpressionHelper _expressionHelper; - /// - /// Get an instance - /// - /// - public MethodFinder(ParsingConfig parsingConfig) + public MethodFinder(ParsingConfig parsingConfig, IExpressionHelper expressionHelper) { - _parsingConfig = parsingConfig; + _parsingConfig = Check.NotNull(parsingConfig); + _expressionHelper = Check.NotNull(expressionHelper); } public bool ContainsMethod(Type type, string methodName, bool staticAccess, Expression? instance, ref Expression[] args) @@ -116,7 +115,26 @@ public int FindBestMethodBasedOnArguments(IEnumerable methods, ref E method = methodData.MethodBase; } - args = methodData.Args; + if (args.Length == 0 || args.Length != methodData.Args.Length) + { + args = methodData.Args; + } + else + { + for (var i = 0; i < args.Length; i++) + { + if (args[i].Type != methodData.Args[i].Type && + args[i].Type.IsArray && methodData.Args[i].Type.IsArray && + args[i].Type != typeof(string) && methodData.Args[i].Type == typeof(object[])) + { + args[i] = _expressionHelper.ConvertAnyArrayToObjectArray(args[i]); + } + else + { + args[i] = methodData.Args[i]; + } + } + } } else { @@ -134,7 +152,7 @@ public int FindIndexer(Type type, Expression[] args, out MethodBase? method) if (members.Length != 0) { IEnumerable methods = members.OfType(). -#if !(NETFX_CORE || WINDOWS_APP || UAP10_0 || NETSTANDARD) +#if !(NETFX_CORE || WINDOWS_APP || UAP10_0 || NETSTANDARD) Select(p => (MethodBase)p.GetGetMethod()). Where(m => m != null); #else diff --git a/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs b/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs index 73cfedc6..acf8748e 100644 --- a/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs +++ b/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs @@ -1696,6 +1696,69 @@ public void DynamicExpressionParser_ParseComparisonOperator_DynamicClass_For_Str isValid.Should().BeTrue(); } + [Fact] + public void DynamicExpressionParser_ParseLambda_HandleStringArray_For_StringJoin() + { + // Arrange + var strArray = new[] { "a", "b", "c" }; + var parameterExpressions = new List + { + Expression.Parameter(strArray.GetType(), "strArray") + }; + + var expression = "string.Join(\",\", strArray)"; + + // Act + var lambdaExpression = DynamicExpressionParser.ParseLambda(parameterExpressions.ToArray(), null, expression); + var @delegate = lambdaExpression.Compile(); + var result = (string)@delegate.DynamicInvoke(new object[] { strArray }); + + // Assert + result.Should().Be("a,b,c"); + } + + [Fact] + public void DynamicExpressionParser_ParseLambda_HandleObjectArray_For_StringJoin() + { + // Arrange + var objectArray = new object[] { 1, 2, 3 }; + var parameterExpressions = new List + { + Expression.Parameter(objectArray.GetType(), "objectArray") + }; + + var expression = "string.Join(\",\", objectArray)"; + + // Act + var lambdaExpression = DynamicExpressionParser.ParseLambda(parameterExpressions.ToArray(), null, expression); + var @delegate = lambdaExpression.Compile(); + var result = (string)@delegate.DynamicInvoke(new object[] { objectArray }); + + // Assert + result.Should().Be("1,2,3"); + } + + [Fact] + public void DynamicExpressionParser_ParseLambda_HandleIntArray_For_StringJoin() + { + // Arrange + var intArray = new[] { 1, 2, 3 }; + var parameterExpressions = new List + { + Expression.Parameter(intArray.GetType(), "intArray") + }; + + var expression = "string.Join(\",\", intArray)"; + + // Act + var lambdaExpression = DynamicExpressionParser.ParseLambda(parameterExpressions.ToArray(), null, expression); + var @delegate = lambdaExpression.Compile(); + var result = (string)@delegate.DynamicInvoke(intArray); + + // Assert + result.Should().Be("1,2,3"); + } + public class DefaultDynamicLinqCustomTypeProviderForGenericExtensionMethod : DefaultDynamicLinqCustomTypeProvider { public override HashSet GetCustomTypes() => new HashSet(base.GetCustomTypes()) { typeof(Methods), typeof(MethodsItemExtension) }; diff --git a/test/System.Linq.Dynamic.Core.Tests/Parser/ExpressionHelperTests.cs b/test/System.Linq.Dynamic.Core.Tests/Parser/ExpressionHelperTests.cs index 212df610..41fa5d18 100644 --- a/test/System.Linq.Dynamic.Core.Tests/Parser/ExpressionHelperTests.cs +++ b/test/System.Linq.Dynamic.Core.Tests/Parser/ExpressionHelperTests.cs @@ -1,256 +1,275 @@ using FluentAssertions; using NFluent; +using System.Collections; using System.Linq.Dynamic.Core.Parser; using System.Linq.Expressions; using Xunit; -namespace System.Linq.Dynamic.Core.Tests.Parser +namespace System.Linq.Dynamic.Core.Tests.Parser; + +public class ExpressionHelperTests { - public class ExpressionHelperTests + private readonly ExpressionHelper _sut; + + public ExpressionHelperTests() { - private readonly ExpressionHelper _expressionHelper; + _sut = new ExpressionHelper(ParsingConfig.Default); + } - public ExpressionHelperTests() + [Fact] + public void ExpressionHelper_WrapConstantExpression_false() + { + // Assign + var config = new ParsingConfig { - _expressionHelper = new ExpressionHelper(ParsingConfig.Default); - } + UseParameterizedNamesInDynamicQuery = false + }; + var expressionHelper = new ExpressionHelper(config); - [Fact] - public void ExpressionHelper_WrapConstantExpression_false() - { - // Assign - var config = new ParsingConfig - { - UseParameterizedNamesInDynamicQuery = false - }; - var expressionHelper = new ExpressionHelper(config); - - string value = "test"; - Expression expression = Expression.Constant(value); - - // Act - expressionHelper.WrapConstantExpression(ref expression); - - // Assert - Check.That(expression).IsInstanceOf(); - Check.That(expression.ToString()).Equals("\"test\""); - } - - [Fact] - public void ExpressionHelper_WrapNullableConstantExpression_false() - { - // Assign - var config = new ParsingConfig - { - UseParameterizedNamesInDynamicQuery = false - }; - var expressionHelper = new ExpressionHelper(config); - - int? value = 42; - Expression expression = Expression.Constant(value); - - // Act - expressionHelper.WrapConstantExpression(ref expression); - - // Assert - Check.That(expression).IsInstanceOf(); - Check.That(expression.ToString()).Equals("42"); - } - - [Fact] - public void ExpressionHelper_WrapConstantExpression_true() - { - // Assign - var config = new ParsingConfig - { - UseParameterizedNamesInDynamicQuery = true - }; - var expressionHelper = new ExpressionHelper(config); - - string value = "test"; - Expression expression = Expression.Constant(value); - - // Act - expressionHelper.WrapConstantExpression(ref expression); - expressionHelper.WrapConstantExpression(ref expression); - - // Assert - Check.That(expression.GetType().FullName).Equals("System.Linq.Expressions.PropertyExpression"); - Check.That(expression.ToString()).Equals("value(System.Linq.Dynamic.Core.Parser.WrappedValue`1[System.String]).Value"); - } - - [Fact] - public void ExpressionHelper_WrapNullableConstantExpression_true() - { - // Assign - var config = new ParsingConfig - { - UseParameterizedNamesInDynamicQuery = true - }; - var expressionHelper = new ExpressionHelper(config); - - int? value = 42; - Expression expression = Expression.Constant(value); - - // Act - expressionHelper.WrapConstantExpression(ref expression); - expressionHelper.WrapConstantExpression(ref expression); - - // Assert - Check.That(expression.GetType().FullName).Equals("System.Linq.Expressions.PropertyExpression"); - Check.That(expression.ToString()).Equals("value(System.Linq.Dynamic.Core.Parser.WrappedValue`1[System.Int32]).Value"); - } - - [Fact] - public void ExpressionHelper_OptimizeStringForEqualityIfPossible_Guid() - { - // Assign - string guidAsString = Guid.NewGuid().ToString(); + string value = "test"; + Expression expression = Expression.Constant(value); - // Act - Expression result = _expressionHelper.OptimizeStringForEqualityIfPossible(guidAsString, typeof(Guid)); + // Act + expressionHelper.WrapConstantExpression(ref expression); - // Assert - Check.That(result).IsInstanceOf(); - Check.That(result.ToString()).Equals(guidAsString); - } + // Assert + Check.That(expression).IsInstanceOf(); + Check.That(expression.ToString()).Equals("\"test\""); + } - [Fact] - public void ExpressionHelper_OptimizeStringForEqualityIfPossible_Guid_Invalid() + [Fact] + public void ExpressionHelper_WrapNullableConstantExpression_false() + { + // Assign + var config = new ParsingConfig { - // Assign - string guidAsString = "x"; + UseParameterizedNamesInDynamicQuery = false + }; + var expressionHelper = new ExpressionHelper(config); - // Act - Expression result = _expressionHelper.OptimizeStringForEqualityIfPossible(guidAsString, typeof(Guid)); + int? value = 42; + Expression expression = Expression.Constant(value); - // Assert - Check.That(result).IsNull(); - } + // Act + expressionHelper.WrapConstantExpression(ref expression); + + // Assert + Check.That(expression).IsInstanceOf(); + Check.That(expression.ToString()).Equals("42"); + } - [Fact] - public void ExpressionHelper_TryGenerateAndAlsoNotNullExpression_Nested3NonNullable() + [Fact] + public void ExpressionHelper_WrapConstantExpression_true() + { + // Assign + var config = new ParsingConfig { - // Assign - Expression> expression = x => x.Relation1.Relation2.Id; + UseParameterizedNamesInDynamicQuery = true + }; + var expressionHelper = new ExpressionHelper(config); + + string value = "test"; + Expression expression = Expression.Constant(value); - // Act - bool result = _expressionHelper.TryGenerateAndAlsoNotNullExpression(expression, true, out Expression generatedExpression); + // Act + expressionHelper.WrapConstantExpression(ref expression); + expressionHelper.WrapConstantExpression(ref expression); - // Assert - Check.That(result).IsTrue(); - Check.That(generatedExpression.ToString()).IsEqualTo("((((x != null) AndAlso (x.Relation1 != null)) AndAlso (x.Relation1.Relation2 != null)) AndAlso (x => x.Relation1.Relation2.Id != null))"); - } + // Assert + Check.That(expression.GetType().FullName).Equals("System.Linq.Expressions.PropertyExpression"); + Check.That(expression.ToString()).Equals("value(System.Linq.Dynamic.Core.Parser.WrappedValue`1[System.String]).Value"); + } - [Fact] - public void ExpressionHelper_TryGenerateAndAlsoNotNullExpression_Nested3NonNullable_Config_Has_UseDefault() + [Fact] + public void ExpressionHelper_WrapNullableConstantExpression_true() + { + // Assign + var config = new ParsingConfig { - // Assign - var config = new ParsingConfig - { - NullPropagatingUseDefaultValueForNonNullableValueTypes = true - }; - var expressionHelper = new ExpressionHelper(config); + UseParameterizedNamesInDynamicQuery = true + }; + var expressionHelper = new ExpressionHelper(config); - Expression> expression = x => x.Relation1.Relation2.Id; + int? value = 42; + Expression expression = Expression.Constant(value); - // Act - bool result = expressionHelper.TryGenerateAndAlsoNotNullExpression(expression, true, out Expression generatedExpression); + // Act + expressionHelper.WrapConstantExpression(ref expression); + expressionHelper.WrapConstantExpression(ref expression); - // Assert - Check.That(result).IsTrue(); - Check.That(generatedExpression.ToString()).IsEqualTo("((((x != null) AndAlso (x.Relation1 != null)) AndAlso (x.Relation1.Relation2 != null)) AndAlso (x => x.Relation1.Relation2.Id != null))"); - } + // Assert + Check.That(expression.GetType().FullName).Equals("System.Linq.Expressions.PropertyExpression"); + Check.That(expression.ToString()).Equals("value(System.Linq.Dynamic.Core.Parser.WrappedValue`1[System.Int32]).Value"); + } - [Fact] - public void ExpressionHelper_TryGenerateAndAlsoNotNullExpression_Nested1NullableInt() - { - // Assign - Expression> expression = x => x.IdNullable; + [Fact] + public void ExpressionHelper_OptimizeStringForEqualityIfPossible_Guid() + { + // Assign + string guidAsString = Guid.NewGuid().ToString(); - // Act - bool result = _expressionHelper.TryGenerateAndAlsoNotNullExpression(expression, true, out Expression generatedExpression); + // Act + Expression result = _sut.OptimizeStringForEqualityIfPossible(guidAsString, typeof(Guid)); - // Assert - Check.That(result).IsTrue(); - Check.That(generatedExpression.ToString()).IsEqualTo("((x != null) AndAlso (x => x.IdNullable != null))"); - } + // Assert + Check.That(result).IsInstanceOf(); + Check.That(result.ToString()).Equals(guidAsString); + } - [Fact] - public void ExpressionHelper_TryGenerateAndAlsoNotNullExpression_Nested1NullableString() - { - // Assign - Expression> expression = x => x.S; + [Fact] + public void ExpressionHelper_OptimizeStringForEqualityIfPossible_Guid_Invalid() + { + // Assign + string guidAsString = "x"; - // Act - bool result = _expressionHelper.TryGenerateAndAlsoNotNullExpression(expression, true, out Expression generatedExpression); + // Act + Expression result = _sut.OptimizeStringForEqualityIfPossible(guidAsString, typeof(Guid)); - // Assert - Check.That(result).IsTrue(); - Check.That(generatedExpression.ToString()).IsEqualTo("((x != null) AndAlso (x => x.S != null))"); - } + // Assert + Check.That(result).IsNull(); + } - [Fact] - public void ExpressionHelper_TryGenerateAndAlsoNotNullExpression_Nested3Nullable_AddSelfFalse() - { - // Assign - Expression> expression = x => x.Relation1.Relation2.IdNullable; + [Fact] + public void ExpressionHelper_TryGenerateAndAlsoNotNullExpression_Nested3NonNullable() + { + // Assign + Expression> expression = x => x.Relation1.Relation2.Id; - // Act - bool result = _expressionHelper.TryGenerateAndAlsoNotNullExpression(expression, false, out Expression generatedExpression); + // Act + bool result = _sut.TryGenerateAndAlsoNotNullExpression(expression, true, out Expression generatedExpression); - // Assert - Check.That(result).IsTrue(); - Check.That(generatedExpression.ToString()).IsEqualTo("(((x != null) AndAlso (x.Relation1 != null)) AndAlso (x.Relation1.Relation2 != null))"); - } + // Assert + Check.That(result).IsTrue(); + Check.That(generatedExpression.ToString()).IsEqualTo("((((x != null) AndAlso (x.Relation1 != null)) AndAlso (x.Relation1.Relation2 != null)) AndAlso (x => x.Relation1.Relation2.Id != null))"); + } - [Fact] - public void ExpressionHelper_TryGenerateAndAlsoNotNullExpression_Nested3Nullable_AddSelfTrue() + [Fact] + public void ExpressionHelper_TryGenerateAndAlsoNotNullExpression_Nested3NonNullable_Config_Has_UseDefault() + { + // Assign + var config = new ParsingConfig { - // Assign - Expression> expression = x => x.Relation1.Relation2.IdNullable; + NullPropagatingUseDefaultValueForNonNullableValueTypes = true + }; + var expressionHelper = new ExpressionHelper(config); - // Act - bool result = _expressionHelper.TryGenerateAndAlsoNotNullExpression(expression, true, out Expression generatedExpression); + Expression> expression = x => x.Relation1.Relation2.Id; - // Assert - Check.That(result).IsTrue(); - Check.That(generatedExpression.ToString()).IsEqualTo("((((x != null) AndAlso (x.Relation1 != null)) AndAlso (x.Relation1.Relation2 != null)) AndAlso (x => x.Relation1.Relation2.IdNullable != null))"); - } + // Act + bool result = expressionHelper.TryGenerateAndAlsoNotNullExpression(expression, true, out Expression generatedExpression); - [Fact] - public void ExpressionHelper_TryGenerateAndAlsoNotNullExpression_Nullable() - { - // Assign - Expression> expression = x => x.Id; + // Assert + Check.That(result).IsTrue(); + Check.That(generatedExpression.ToString()).IsEqualTo("((((x != null) AndAlso (x.Relation1 != null)) AndAlso (x.Relation1.Relation2 != null)) AndAlso (x => x.Relation1.Relation2.Id != null))"); + } - // Act - bool result = _expressionHelper.TryGenerateAndAlsoNotNullExpression(expression, true, out Expression generatedExpression); + [Fact] + public void ExpressionHelper_TryGenerateAndAlsoNotNullExpression_Nested1NullableInt() + { + // Assign + Expression> expression = x => x.IdNullable; - // Assert - result.Should().BeTrue(); - generatedExpression.ToString().Should().StartWith("((x != null) AndAlso (x =>").And.EndWith("!= null))"); - } + // Act + bool result = _sut.TryGenerateAndAlsoNotNullExpression(expression, true, out Expression generatedExpression); - class Item - { - public int Id { get; set; } - public Relation1 Relation1 { get; set; } - } + // Assert + Check.That(result).IsTrue(); + Check.That(generatedExpression.ToString()).IsEqualTo("((x != null) AndAlso (x => x.IdNullable != null))"); + } - class Relation1 - { - public int Id { get; set; } - public Relation2 Relation2 { get; set; } - } + [Fact] + public void ExpressionHelper_TryGenerateAndAlsoNotNullExpression_Nested1NullableString() + { + // Assign + Expression> expression = x => x.S; - class Relation2 - { - public int Id { get; set; } + // Act + bool result = _sut.TryGenerateAndAlsoNotNullExpression(expression, true, out Expression generatedExpression); + + // Assert + Check.That(result).IsTrue(); + Check.That(generatedExpression.ToString()).IsEqualTo("((x != null) AndAlso (x => x.S != null))"); + } + + [Fact] + public void ExpressionHelper_TryGenerateAndAlsoNotNullExpression_Nested3Nullable_AddSelfFalse() + { + // Assign + Expression> expression = x => x.Relation1.Relation2.IdNullable; + + // Act + bool result = _sut.TryGenerateAndAlsoNotNullExpression(expression, false, out Expression generatedExpression); + + // Assert + Check.That(result).IsTrue(); + Check.That(generatedExpression.ToString()).IsEqualTo("(((x != null) AndAlso (x.Relation1 != null)) AndAlso (x.Relation1.Relation2 != null))"); + } + + [Fact] + public void ExpressionHelper_TryGenerateAndAlsoNotNullExpression_Nested3Nullable_AddSelfTrue() + { + // Assign + Expression> expression = x => x.Relation1.Relation2.IdNullable; + + // Act + bool result = _sut.TryGenerateAndAlsoNotNullExpression(expression, true, out Expression generatedExpression); + + // Assert + Check.That(result).IsTrue(); + Check.That(generatedExpression.ToString()).IsEqualTo("((((x != null) AndAlso (x.Relation1 != null)) AndAlso (x.Relation1.Relation2 != null)) AndAlso (x => x.Relation1.Relation2.IdNullable != null))"); + } + + [Fact] + public void ExpressionHelper_TryGenerateAndAlsoNotNullExpression_Nullable() + { + // Assign + Expression> expression = x => x.Id; + + // Act + bool result = _sut.TryGenerateAndAlsoNotNullExpression(expression, true, out Expression generatedExpression); + + // Assert + result.Should().BeTrue(); + generatedExpression.ToString().Should().StartWith("((x != null) AndAlso (x =>").And.EndWith("!= null))"); + } + + [Fact] + public void ConvertAnyArrayToObjectArray_ShouldConvertIntArrayToObjectArray() + { + // Arrange + var array = new[] { 1, 2, 3 }; + var arrayExpression = Expression.Constant(array); + + // Act + var expression = _sut.ConvertAnyArrayToObjectArray(arrayExpression); + + // Assert + expression.Should().NotBeNull(); + + var lambdaExpressionCompiled = Expression.Lambda(expression).Compile(); + var result = (object[]) lambdaExpressionCompiled.DynamicInvoke(); + + result.Should().HaveCount(array.Length).And.ContainInOrder((object) 1, (object) 2, (object) 3); + } + + class Item + { + public int Id { get; set; } + public Relation1 Relation1 { get; set; } + } + + class Relation1 + { + public int Id { get; set; } + public Relation2 Relation2 { get; set; } + } + + class Relation2 + { + public int Id { get; set; } - public int? IdNullable { get; set; } + public int? IdNullable { get; set; } - public string S { get; set; } - } + public string S { get; set; } } -} +} \ No newline at end of file