diff --git a/CHANGELOG.md b/CHANGELOG.md index 4cb7e830..cbaa99da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +# v1.6.10 (08 November 2025) +- [#953](https://github.com/zzzprojects/System.Linq.Dynamic.Core/pull/953) - Fixed adding Enum and integer [bug] contributed by [StefH](https://github.com/StefH) +- [#954](https://github.com/zzzprojects/System.Linq.Dynamic.Core/pull/954) - Fix ExpressionHelper.TryConvertTypes to generate correct Convert in case left or right is null [bug] contributed by [StefH](https://github.com/StefH) +- [#951](https://github.com/zzzprojects/System.Linq.Dynamic.Core/issues/951) - Parsing error adding numeric constant to enum value [bug] +- [#952](https://github.com/zzzprojects/System.Linq.Dynamic.Core/issues/952) - Json: How to handle not existing member [bug] + +# v1.6.9 (11 October 2025) +- [#950](https://github.com/zzzprojects/System.Linq.Dynamic.Core/pull/950) - DynamicExpressionParser - Handle indexed properties with any number of indices in expression [bug] contributed by [thibault-reigner](https://github.com/thibault-reigner) + # v1.6.8 (28 September 2025) - [#946](https://github.com/zzzprojects/System.Linq.Dynamic.Core/pull/946) - Fix GroupByMany using composite key and normal key [bug] contributed by [StefH](https://github.com/StefH) - [#948](https://github.com/zzzprojects/System.Linq.Dynamic.Core/pull/948) - Add IndexerName attribute to DynamicClass to fix naming issues with Item [bug] contributed by [StefH](https://github.com/StefH) diff --git a/Generate-ReleaseNotes.bat b/Generate-ReleaseNotes.bat index c90be38d..3196cbc2 100644 --- a/Generate-ReleaseNotes.bat +++ b/Generate-ReleaseNotes.bat @@ -1,5 +1,5 @@ rem https://github.com/StefH/GitHubReleaseNotes -SET version=v1.6.8 +SET version=v1.6.10 GitHubReleaseNotes --output CHANGELOG.md --exclude-labels known_issue out_of_scope not_planned invalid question documentation wontfix environment duplicate --language en --version %version% --token %GH_TOKEN% diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs index 7ca9d6e3..8aefa397 100644 --- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs +++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs @@ -821,7 +821,7 @@ public static JArray Where(this JArray source, NewtonsoftJsonParsingConfig confi if (source.Count == 0) { - return new JArray(); + return []; } var queryable = ToQueryable(source, config); @@ -848,7 +848,8 @@ public static JArray Where(this JArray source, NewtonsoftJsonParsingConfig confi private static JArray ToJArray(Func func) { var array = new JArray(); - foreach (var dynamicElement in func()) + var funcResult = func(); + foreach (var dynamicElement in funcResult) { var element = dynamicElement switch { diff --git a/src/System.Linq.Dynamic.Core/Parser/ExpressionHelper.cs b/src/System.Linq.Dynamic.Core/Parser/ExpressionHelper.cs index 7d72ad61..fefd5b66 100644 --- a/src/System.Linq.Dynamic.Core/Parser/ExpressionHelper.cs +++ b/src/System.Linq.Dynamic.Core/Parser/ExpressionHelper.cs @@ -361,7 +361,12 @@ public bool ExpressionQualifiesForNullPropagation(Expression? expression) public Expression GenerateDefaultExpression(Type type) { #if NET35 - return Expression.Constant(Activator.CreateInstance(type)); + if (type.IsValueType) + { + return Expression.Constant(Activator.CreateInstance(type), type); + } + + return Expression.Constant(null, type); #else return Expression.Default(type); #endif @@ -388,11 +393,19 @@ public bool TryConvertTypes(ref Expression left, ref Expression right) if (left.Type == typeof(object)) { - left = Expression.Convert(left, right.Type); + left = Expression.Condition( + Expression.Equal(left, Expression.Constant(null, typeof(object))), + GenerateDefaultExpression(right.Type), + Expression.Convert(left, right.Type) + ); } else if (right.Type == typeof(object)) { - right = Expression.Convert(right, left.Type); + right = Expression.Condition( + Expression.Equal(right, Expression.Constant(null, typeof(object))), + GenerateDefaultExpression(left.Type), + Expression.Convert(right, left.Type) + ); } return true; diff --git a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs index be2a43c1..4cc77a00 100644 --- a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs +++ b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs @@ -2353,20 +2353,22 @@ private Expression ParseElementAccess(Expression expr) switch (_methodFinder.FindIndexer(expr.Type, args, out var mb)) { case 0: - throw ParseError(errorPos, Res.NoApplicableIndexer, - TypeHelper.GetTypeName(expr.Type)); + throw ParseError(errorPos, Res.NoApplicableIndexer, TypeHelper.GetTypeName(expr.Type), args.Length); case 1: var indexMethod = (MethodInfo)mb!; - var indexParameterType = indexMethod.GetParameters().First().ParameterType; + var indexMethodArguments = indexMethod.GetParameters(); - var indexArgumentExpression = args[0]; // Indexer only has 1 parameter, so we can use args[0] here - if (indexParameterType != indexArgumentExpression.Type) + var indexArgumentExpressions = new Expression[args.Length]; + for (var i = 0; i < indexMethodArguments.Length; ++i) { - indexArgumentExpression = Expression.Convert(indexArgumentExpression, indexParameterType); + var indexParameterType = indexMethodArguments[i].ParameterType; + indexArgumentExpressions[i] = indexParameterType != args[i].Type + ? Expression.Convert(args[i], indexParameterType) + : args[i]; } - return Expression.Call(expr, indexMethod, indexArgumentExpression); + return Expression.Call(expr, indexMethod, indexArgumentExpressions); default: throw ParseError(errorPos, Res.AmbiguousIndexerInvocation, TypeHelper.GetTypeName(expr.Type)); diff --git a/src/System.Linq.Dynamic.Core/Parser/ExpressionPromoter.cs b/src/System.Linq.Dynamic.Core/Parser/ExpressionPromoter.cs index 088b755e..49731b24 100644 --- a/src/System.Linq.Dynamic.Core/Parser/ExpressionPromoter.cs +++ b/src/System.Linq.Dynamic.Core/Parser/ExpressionPromoter.cs @@ -122,7 +122,7 @@ public ExpressionPromoter(ParsingConfig config) if (TypeHelper.IsCompatibleWith(returnType, type)) { - if (type == typeof(decimal) && TypeHelper.IsEnumType(sourceExpression.Type)) + if (TypeHelper.TypesAreEqual(type, typeof(decimal)) && TypeHelper.IsEnumType(sourceExpression.Type)) { return Expression.Convert(Expression.Convert(sourceExpression, Enum.GetUnderlyingType(sourceExpression.Type)), type); } diff --git a/src/System.Linq.Dynamic.Core/Parser/IExpressionHelper.cs b/src/System.Linq.Dynamic.Core/Parser/IExpressionHelper.cs index bbc691cd..4e52949b 100644 --- a/src/System.Linq.Dynamic.Core/Parser/IExpressionHelper.cs +++ b/src/System.Linq.Dynamic.Core/Parser/IExpressionHelper.cs @@ -52,5 +52,5 @@ internal interface IExpressionHelper /// /// If the types are different (and not null), try to convert the object type to other type. /// - public bool TryConvertTypes(ref Expression left, ref Expression right); + bool TryConvertTypes(ref Expression left, ref Expression right); } \ No newline at end of file diff --git a/src/System.Linq.Dynamic.Core/Parser/TypeHelper.cs b/src/System.Linq.Dynamic.Core/Parser/TypeHelper.cs index 19002c4f..f4401b63 100644 --- a/src/System.Linq.Dynamic.Core/Parser/TypeHelper.cs +++ b/src/System.Linq.Dynamic.Core/Parser/TypeHelper.cs @@ -83,20 +83,20 @@ public static bool IsCompatibleWith(Type source, Type target) return target.IsAssignableFrom(source); } - Type st = GetNonNullableType(source); - Type tt = GetNonNullableType(target); + var sourceType = GetNonNullableType(source); + var targetType = GetNonNullableType(target); - if (st != source && tt == target) + if (sourceType != source && targetType == target) { return false; } - TypeCode sc = st.GetTypeInfo().IsEnum ? TypeCode.Int64 : Type.GetTypeCode(st); - TypeCode tc = tt.GetTypeInfo().IsEnum ? TypeCode.Int64 : Type.GetTypeCode(tt); - switch (sc) + var sourceTypeCode = sourceType.GetTypeInfo().IsEnum ? TypeCode.Int32 : Type.GetTypeCode(sourceType); + var targetTypeCode = targetType.GetTypeInfo().IsEnum ? TypeCode.Int32 : Type.GetTypeCode(targetType); + switch (sourceTypeCode) { case TypeCode.SByte: - switch (tc) + switch (targetTypeCode) { case TypeCode.SByte: case TypeCode.Int16: @@ -110,7 +110,7 @@ public static bool IsCompatibleWith(Type source, Type target) break; case TypeCode.Byte: - switch (tc) + switch (targetTypeCode) { case TypeCode.Byte: case TypeCode.Int16: @@ -127,7 +127,7 @@ public static bool IsCompatibleWith(Type source, Type target) break; case TypeCode.Int16: - switch (tc) + switch (targetTypeCode) { case TypeCode.Int16: case TypeCode.Int32: @@ -140,7 +140,7 @@ public static bool IsCompatibleWith(Type source, Type target) break; case TypeCode.UInt16: - switch (tc) + switch (targetTypeCode) { case TypeCode.UInt16: case TypeCode.Int32: @@ -155,7 +155,7 @@ public static bool IsCompatibleWith(Type source, Type target) break; case TypeCode.Int32: - switch (tc) + switch (targetTypeCode) { case TypeCode.Int32: case TypeCode.Int64: @@ -167,7 +167,7 @@ public static bool IsCompatibleWith(Type source, Type target) break; case TypeCode.UInt32: - switch (tc) + switch (targetTypeCode) { case TypeCode.UInt32: case TypeCode.Int64: @@ -180,7 +180,7 @@ public static bool IsCompatibleWith(Type source, Type target) break; case TypeCode.Int64: - switch (tc) + switch (targetTypeCode) { case TypeCode.Int64: case TypeCode.Single: @@ -191,7 +191,7 @@ public static bool IsCompatibleWith(Type source, Type target) break; case TypeCode.UInt64: - switch (tc) + switch (targetTypeCode) { case TypeCode.UInt64: case TypeCode.Single: @@ -202,7 +202,7 @@ public static bool IsCompatibleWith(Type source, Type target) break; case TypeCode.Single: - switch (tc) + switch (targetTypeCode) { case TypeCode.Single: case TypeCode.Double: @@ -211,7 +211,7 @@ public static bool IsCompatibleWith(Type source, Type target) break; default: - if (st == tt) + if (sourceType == targetType) { return true; } @@ -471,6 +471,11 @@ public static Type GetUnderlyingType(Type type) return type; } + public static bool TypesAreEqual(Type type, Type typeToCheck) + { + return GetNullableType(type) == GetNullableType(typeToCheck); + } + public static IList GetSelfAndBaseTypes(Type type, bool excludeObject = false) { if (type.GetTypeInfo().IsInterface) diff --git a/src/System.Linq.Dynamic.Core/Res.cs b/src/System.Linq.Dynamic.Core/Res.cs index d2831c24..3d423495 100644 --- a/src/System.Linq.Dynamic.Core/Res.cs +++ b/src/System.Linq.Dynamic.Core/Res.cs @@ -55,7 +55,7 @@ internal static class Res public const string MissingAsClause = "Expression is missing an 'as' clause"; public const string NeitherTypeConvertsToOther = "Neither of the types '{0}' and '{1}' converts to the other"; public const string NewOperatorIsNotAllowed = "Using the new operator is not allowed via the ParsingConfig."; - public const string NoApplicableIndexer = "No applicable indexer exists in type '{0}'"; + public const string NoApplicableIndexer = "No applicable indexer exists in type '{0}' with {1} parameters"; public const string NoApplicableMethod = "No applicable method '{0}' exists in type '{1}'"; public const string NoItInScope = "No 'it' is in scope"; public const string NoMatchingConstructor = "No matching constructor in type '{0}'"; diff --git a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs index 2e94a212..40185d45 100644 --- a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs @@ -1,4 +1,5 @@ -using FluentAssertions; +using System.Linq.Dynamic.Core.NewtonsoftJson.Config; +using FluentAssertions; using Newtonsoft.Json.Linq; using Xunit; @@ -506,4 +507,62 @@ public void Where_With_Select() var first = result.First(); first.Value().Should().Be("Doe"); } + + //[Fact] + //public void Where_OptionalProperty() + //{ + // // Arrange + // var config = new NewtonsoftJsonParsingConfig + // { + // ConvertObjectToSupportComparison = true + // }; + // var array = + // """ + // [ + // { + // "Name": "John", + // "Age": 30 + // }, + // { + // "Name": "Doe" + // } + // ] + // """; + + // // Act + // var result = JArray.Parse(array).Where(config, "Age > 30").Select("Name"); + + // // Assert + // result.Should().HaveCount(1); + // var first = result.First(); + // first.Value().Should().Be("John"); + //} + + [Theory] + [InlineData("notExisting == true")] + [InlineData("notExisting == \"true\"")] + [InlineData("notExisting == 1")] + [InlineData("notExisting == \"1\"")] + [InlineData("notExisting == \"something\"")] + [InlineData("notExisting > 1")] + [InlineData("true == notExisting")] + [InlineData("\"true\" == notExisting")] + [InlineData("1 == notExisting")] + [InlineData("\"1\" == notExisting")] + [InlineData("\"something\" == notExisting")] + [InlineData("1 < notExisting")] + public void Where_NonExistingMember_EmptyResult(string predicate) + { + // Arrange + var config = new NewtonsoftJsonParsingConfig + { + ConvertObjectToSupportComparison = true + }; + + // Act + var result = _source.Where(config, predicate); + + // Assert + result.Should().BeEmpty(); + } } \ No newline at end of file diff --git a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs index f5dee221..1e817664 100644 --- a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs +++ b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs @@ -1,4 +1,5 @@ -using System.Text.Json; +using System.Linq.Dynamic.Core.SystemTextJson.Config; +using System.Text.Json; using FluentAssertions; using Xunit; @@ -535,4 +536,32 @@ public void Where_With_Select() array.Should().HaveCount(1); array.First().GetString().Should().Be("Doe"); } + + [Theory] + [InlineData("notExisting == true")] + [InlineData("notExisting == \"true\"")] + [InlineData("notExisting == 1")] + [InlineData("notExisting == \"1\"")] + [InlineData("notExisting == \"something\"")] + [InlineData("notExisting > 1")] + [InlineData("true == notExisting")] + [InlineData("\"true\" == notExisting")] + [InlineData("1 == notExisting")] + [InlineData("\"1\" == notExisting")] + [InlineData("\"something\" == notExisting")] + [InlineData("1 < notExisting")] + public void Where_NonExistingMember_EmptyResult(string predicate) + { + // Arrange + var config = new SystemTextJsonParsingConfig + { + ConvertObjectToSupportComparison = true + }; + + // Act + var result = _source.Where(config, predicate).RootElement.EnumerateArray(); + + // Assert + result.Should().BeEmpty(); + } } \ No newline at end of file diff --git a/test/System.Linq.Dynamic.Core.Tests/DynamicClassTest.cs b/test/System.Linq.Dynamic.Core.Tests/DynamicClassTest.cs index 32dd3002..6d33cae8 100644 --- a/test/System.Linq.Dynamic.Core.Tests/DynamicClassTest.cs +++ b/test/System.Linq.Dynamic.Core.Tests/DynamicClassTest.cs @@ -131,20 +131,24 @@ public void DynamicClass_GettingValue_ByIndex_Should_Work() public void DynamicClass_SettingExistingPropertyValue_ByIndex_Should_Work() { // Arrange - var test = "Test"; - var newTest = "abc"; - var range = new List + var originalValue = "Test"; + var newValue = "abc"; + var array = new object[] { - new { FieldName = test, Value = 3.14159 } + new + { + FieldName = originalValue, + Value = 3.14159 + } }; // Act - var rangeResult = range.AsQueryable().Select("new(FieldName as FieldName)").ToDynamicList(); + var rangeResult = array.AsQueryable().Select("new(FieldName as FieldName)").ToDynamicList(); var item = rangeResult.First(); - item["FieldName"] = newTest; + item["FieldName"] = newValue; var value = item["FieldName"] as string; - value.Should().Be(newTest); + value.Should().Be(newValue); } [Fact] diff --git a/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs b/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs index a65012b0..4d5d19b5 100644 --- a/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs +++ b/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs @@ -70,6 +70,18 @@ public override HashSet GetCustomTypes() } } + private class ClassWithIndexers + { + public int this[int i1] + { + get => i1 + 1; + } + public string this[int i1, string i2] + { + get => i1 + "-" + i2; + } + } + private class ComplexParseLambda1Result { public int? Age; @@ -660,6 +672,79 @@ public void DynamicExpressionParser_ParseLambda_Issue58() Check.That(result).Equals(42); } + [Fact] + public void DynamicExpressionParser_ParseLambda_Indexer1D() + { + // Arrange + var customTypeProvider = new Mock(); + customTypeProvider.Setup(c => c.GetCustomTypes()).Returns([typeof(ClassWithIndexers)]); + var config = new ParsingConfig + { + CustomTypeProvider = customTypeProvider.Object + }; + var expressionParams = new[] + { + Expression.Parameter(typeof(ClassWithIndexers), "myObj") + }; + + var myClassInstance = new ClassWithIndexers(); + var invokersMerge = new List { myClassInstance }; + + // Act + var expression = DynamicExpressionParser.ParseLambda(config, false, expressionParams, null, "myObj[3]"); + var del = expression.Compile(); + var result = del.DynamicInvoke(invokersMerge.ToArray()); + + // Assert + Check.That(result).Equals(4); + } + + [Fact] + public void DynamicExpressionParser_ParseLambda_Indexer2D() + { + // Arrange + var customTypeProvider = new Mock(); + customTypeProvider.Setup(c => c.GetCustomTypes()).Returns([typeof(ClassWithIndexers)]); + var config = new ParsingConfig + { + CustomTypeProvider = customTypeProvider.Object + }; + var expressionParams = new[] + { + Expression.Parameter(typeof(ClassWithIndexers), "myObj") + }; + + var myClassInstance = new ClassWithIndexers(); + var invokersMerge = new List { myClassInstance }; + + // Act + var expression = DynamicExpressionParser.ParseLambda(config, false, expressionParams, null, "myObj[3,\"1\"]"); + var del = expression.Compile(); + var result = del.DynamicInvoke(invokersMerge.ToArray()); + + // Assert + Check.That(result).Equals("3-1"); + } + + [Fact] + public void DynamicExpressionParser_ParseLambda_IndexerParameterMismatch() + { + // Arrange + var customTypeProvider = new Mock(); + customTypeProvider.Setup(c => c.GetCustomTypes()).Returns([typeof(ClassWithIndexers)]); + var config = new ParsingConfig + { + CustomTypeProvider = customTypeProvider.Object + }; + var expressionParams = new[] + { + Expression.Parameter(typeof(ClassWithIndexers), "myObj") + }; + + Assert.Throws(() => + DynamicExpressionParser.ParseLambda(config, false, expressionParams, null, "myObj[3,\"1\",1]")); + } + [Fact] public void DynamicExpressionParser_ParseLambda_DuplicateParameterNames_ThrowsException() { diff --git a/test/System.Linq.Dynamic.Core.Tests/Parser/TypeHelperTests.cs b/test/System.Linq.Dynamic.Core.Tests/Parser/TypeHelperTests.cs index c7a534ba..e6286834 100644 --- a/test/System.Linq.Dynamic.Core.Tests/Parser/TypeHelperTests.cs +++ b/test/System.Linq.Dynamic.Core.Tests/Parser/TypeHelperTests.cs @@ -45,7 +45,7 @@ public void TypeHelper_IsCompatibleWith_SameTypes_True() } [Fact] - public void TypeHelper_IsCompatibleWith_True() + public void TypeHelper_IsCompatibleWith_Int_And_Long_Returns_True() { // Assign + Act var result = TypeHelper.IsCompatibleWith(typeof(int), typeof(long)); @@ -54,8 +54,52 @@ public void TypeHelper_IsCompatibleWith_True() Check.That(result).IsTrue(); } + [Theory] + + // True (enum underlying Int32 compatible targets) + [InlineData(typeof(DayOfWeek), true)] + [InlineData(typeof(DayOfWeek?), true)] + [InlineData(typeof(int), true)] + [InlineData(typeof(int?), true)] + [InlineData(typeof(long), true)] + [InlineData(typeof(long?), true)] + [InlineData(typeof(float), true)] + [InlineData(typeof(float?), true)] + [InlineData(typeof(double), true)] + [InlineData(typeof(double?), true)] + [InlineData(typeof(decimal), true)] + [InlineData(typeof(decimal?), true)] + [InlineData(typeof(object), true)] + + // False (not compatible with enum's Int32 widening rules or reference types) + [InlineData(typeof(char), false)] + [InlineData(typeof(char?), false)] + [InlineData(typeof(short), false)] + [InlineData(typeof(short?), false)] + [InlineData(typeof(byte), false)] + [InlineData(typeof(byte?), false)] + [InlineData(typeof(sbyte), false)] + [InlineData(typeof(sbyte?), false)] + [InlineData(typeof(ushort), false)] + [InlineData(typeof(ushort?), false)] + [InlineData(typeof(uint), false)] + [InlineData(typeof(uint?), false)] + [InlineData(typeof(ulong), false)] + [InlineData(typeof(ulong?), false)] + [InlineData(typeof(bool), false)] + [InlineData(typeof(bool?), false)] + [InlineData(typeof(string), false)] + public void TypeHelper_IsCompatibleWith_Enum(Type targetType, bool expected) + { + // Assign + Act + var result = TypeHelper.IsCompatibleWith(typeof(DayOfWeek), targetType); + + // Assert + result.Should().Be(expected); + } + [Fact] - public void TypeHelper_IsCompatibleWith_False() + public void TypeHelper_IsCompatibleWith_Long_And_Int_Returns_False() { // Assign + Act var result = TypeHelper.IsCompatibleWith(typeof(long), typeof(int)); diff --git a/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Select.cs b/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Select.cs index 364419a3..83d17e9c 100644 --- a/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Select.cs +++ b/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Select.cs @@ -202,6 +202,32 @@ public void Select_Dynamic_Add_Strings() Assert.Equal(range.Select(x => x + "c").ToArray(), rangeResult.Cast().ToArray()); } + [Fact] + public void Select_Dynamic_Add_DayOfWeekEnum_And_Integer() + { + // Arrange + var range = new DayOfWeek[] { DayOfWeek.Monday }; + + // Act + var rangeResult = range.AsQueryable().Select("it + 1"); + + // Assert + Assert.Equal(range.Select(x => x + 1).ToArray(), rangeResult.Cast().ToArray()); + } + + [Fact] + public void Select_Dynamic_Add_Integer_And_DayOfWeekEnum() + { + // Arrange + var range = new int[] { 1 }; + + // Act + var rangeResult = range.AsQueryable().Select("it + DayOfWeek.Monday"); + + // Assert + Assert.Equal(range.Select(x => x + DayOfWeek.Monday).Cast().ToArray(), rangeResult.Cast().ToArray()); + } + [Fact] public void Select_Dynamic_WithIncludes() { diff --git a/version.xml b/version.xml index 07e54a69..79743c43 100644 --- a/version.xml +++ b/version.xml @@ -1,5 +1,5 @@ - 8 + 10 \ No newline at end of file