Skip to content

Commit 82408c3

Browse files
authored
Fixed String.Concat when types differ (#847)
1 parent 45c801a commit 82408c3

File tree

4 files changed

+62
-3
lines changed

4 files changed

+62
-3
lines changed

src-console/ConsoleApp_net6.0/Program.cs

+5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using System.Linq;
44
using System.Linq.Dynamic.Core;
5+
using System.Linq.Dynamic.Core.Parser;
56
using System.Linq.Expressions;
67

78
namespace ConsoleApp_net6._0
@@ -21,6 +22,10 @@ class Program
2122
{
2223
static void Main(string[] args)
2324
{
25+
var parser = new ExpressionParser(new[] { Expression.Parameter(typeof(int), "VarA") }, "\"foo\" & VarA", new object[0], new ParsingConfig { ConvertObjectToSupportComparison = true});
26+
27+
var expression = parser.Parse(typeof(string));
28+
2429
Issue389DoesNotWork();
2530
return;
2631
Issue389_Works();

src/System.Linq.Dynamic.Core/Parser/ExpressionHelper.cs

+18-3
Original file line numberDiff line numberDiff line change
@@ -477,15 +477,30 @@ private void TryConvertTypes(ref Expression left, ref Expression right)
477477

478478
private static Expression GenerateStaticMethodCall(string methodName, Expression left, Expression right)
479479
{
480-
return Expression.Call(null, GetStaticMethod(methodName, left, right), new[] { left, right });
480+
var methodInfo = GetStaticMethod(methodName, left, right);
481+
var parameters = methodInfo.GetParameters();
482+
var parameterTypeLeft = parameters[0].ParameterType;
483+
var parameterTypeRight = parameters[1].ParameterType;
484+
485+
if (parameterTypeLeft != left.Type && !Constants.IsNull(left))
486+
{
487+
left = Expression.Convert(left, parameterTypeLeft);
488+
}
489+
490+
if (parameterTypeRight != right.Type && !Constants.IsNull(right))
491+
{
492+
right = Expression.Convert(right, parameterTypeRight);
493+
}
494+
495+
return Expression.Call(null, methodInfo, [left, right]);
481496
}
482497

483498
private static MethodInfo GetStaticMethod(string methodName, Expression left, Expression right)
484499
{
485-
var methodInfo = left.Type.GetMethod(methodName, new[] { left.Type, right.Type });
500+
var methodInfo = left.Type.GetMethod(methodName, [left.Type, right.Type]);
486501
if (methodInfo == null)
487502
{
488-
methodInfo = right.Type.GetMethod(methodName, new[] { left.Type, right.Type })!;
503+
methodInfo = right.Type.GetMethod(methodName, [left.Type, right.Type])!;
489504
}
490505

491506
return methodInfo;

test/System.Linq.Dynamic.Core.Tests/Parser/ExpressionParserTests.cs

+20
Original file line numberDiff line numberDiff line change
@@ -405,4 +405,24 @@ public void Parse_When_PrioritizePropertyOrFieldOverTheType_IsFalse(string expre
405405
// Assert
406406
parsedExpression.Should().StartWith(result);
407407
}
408+
409+
[Theory]
410+
[InlineData("99 & \"txt\"", "Concat(Convert(99, Object), Convert(\"txt\", Object))")]
411+
[InlineData("\"txt\" & 99", "Concat(Convert(\"txt\", Object), Convert(99, Object))")]
412+
[InlineData("\"txt\" & \"abc\"", "Concat(\"txt\", \"abc\")")]
413+
[InlineData("99 + \"txt\"", "Concat(Convert(99, Object), Convert(\"txt\", Object))")]
414+
[InlineData("\"txt\" + 99", "Concat(Convert(\"txt\", Object), Convert(99, Object))")]
415+
[InlineData("\"txt\" + \"abc\"", "Concat(\"txt\", \"abc\")")]
416+
public void Parse_StringConcat(string expression, string result)
417+
{
418+
// Arrange
419+
var parameters = new[] { Expression.Parameter(typeof(int), "VarA") };
420+
var parser = new ExpressionParser(parameters, expression, [], new ParsingConfig { ConvertObjectToSupportComparison = true });
421+
422+
// Act
423+
var parsedExpression = parser.Parse(typeof(string)).ToString();
424+
425+
// Assert
426+
parsedExpression.Should().Be(result);
427+
}
408428
}

test/System.Linq.Dynamic.Core.Tests/QueryableTests.Select.cs

+19
Original file line numberDiff line numberDiff line change
@@ -509,5 +509,24 @@ public void Select_Dynamic_Nested_With_SubString()
509509
// Assert
510510
resultDynamic.Should().BeEquivalentTo(result);
511511
}
512+
513+
// 845
514+
[Theory]
515+
[InlineData("it + \"txt\" & 99", "_a_txt99")]
516+
[InlineData("it & \"txt\" + 99", "_a_txt99")]
517+
[InlineData("99 + it & \"txt\"", "99_a_txt")]
518+
[InlineData("99 & it + \"txt\"", "99_a_txt")]
519+
public void Select_Dynamic_StringConcatDifferentTypes(string expression, string expectedResult)
520+
{
521+
// Arrange
522+
var config = new ParsingConfig
523+
{
524+
ConvertObjectToSupportComparison = true
525+
};
526+
var queryable = new[] { "_a_" }.AsQueryable();
527+
528+
// Act
529+
queryable.Select(config, expression).ToDynamicArray<string>()[0].Should().Be(expectedResult);
530+
}
512531
}
513532
}

0 commit comments

Comments
 (0)