From eca21dae00c11ec8194bb8f6a525cdb66f108071 Mon Sep 17 00:00:00 2001 From: Oleg Nadymov Date: Thu, 2 Aug 2018 00:33:09 +0300 Subject: [PATCH 01/11] Fix for ParseLambda with itType and resultType: correct order of arguments --- .../DynamicExpressionParser.cs | 2 +- .../DynamicExpressionParserTests.cs | 28 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/System.Linq.Dynamic.Core/DynamicExpressionParser.cs b/src/System.Linq.Dynamic.Core/DynamicExpressionParser.cs index 50ac719d..327901df 100644 --- a/src/System.Linq.Dynamic.Core/DynamicExpressionParser.cs +++ b/src/System.Linq.Dynamic.Core/DynamicExpressionParser.cs @@ -173,7 +173,7 @@ public static LambdaExpression ParseLambda([NotNull] Type itType, [CanBeNull] Ty [PublicAPI] public static LambdaExpression ParseLambda([CanBeNull] ParsingConfig parsingConfig, [NotNull] Type itType, [CanBeNull] Type resultType, string expression, params object[] values) { - return ParseLambda(true, itType, resultType, expression, parsingConfig, values); + return ParseLambda(parsingConfig, true, itType, resultType, expression, values); } /// diff --git a/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs b/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs index da600982..4af2f273 100644 --- a/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs +++ b/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs @@ -32,6 +32,11 @@ public class ComplexParseLambda3Result public int TotalIncome { get; set; } } + public class CustomClassWithStaticMethod + { + public static int GetAge(int x) => x; + } + private class TestCustomTypeProvider : AbstractDynamicLinqCustomTypeProvider, IDynamicLinkCustomTypeProvider { private HashSet _customTypes; @@ -44,6 +49,7 @@ public virtual HashSet GetCustomTypes() } _customTypes = new HashSet(FindTypesMarkedWithDynamicLinqTypeAttribute(new[] { GetType().GetTypeInfo().Assembly })); + _customTypes.Add(typeof(CustomClassWithStaticMethod)); return _customTypes; } } @@ -413,5 +419,27 @@ public void ParseLambda_IllegalMethodCall_ThrowsException() }) .Throws().WithMessage("Methods on type 'Stream' are not accessible"); } + + [Fact] + public void ParseLambda_CustomMethod() + { + var config = new ParsingConfig + { + CustomTypeProvider = new TestCustomTypeProvider() + }; + + var context = new CustomClassWithStaticMethod(); + var original = $"{nameof(CustomClassWithStaticMethod)}.{nameof(CustomClassWithStaticMethod.GetAge)}(10)"; + int result = 0; + Check.ThatCode(() => + { + var expression = System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda(config, typeof(CustomClassWithStaticMethod), null, original); + Delegate del = expression.Compile(); + result = (int) del.DynamicInvoke(context); + }) + .DoesNotThrow(); + + Check.That(result).IsEqualTo(10); + } } } From 4b7d15a0f9c4869c8904db7ba296e31466a872e9 Mon Sep 17 00:00:00 2001 From: Oleg Nadymov Date: Thu, 2 Aug 2018 00:33:49 +0300 Subject: [PATCH 02/11] Add references for missing classes --- .../ConsoleApp_net40_sqlite_original.csproj | 2 ++ .../Helpers/Models/UserProfileDetails.cs | 9 +++++++++ .../Helpers/Models/UserState.cs | 10 ++++++++++ 3 files changed, 21 insertions(+) create mode 100644 src-console/System.Linq.Dynamic.Core.ConsoleTestApp.net40/Helpers/Models/UserProfileDetails.cs create mode 100644 src-console/System.Linq.Dynamic.Core.ConsoleTestApp.net40/Helpers/Models/UserState.cs diff --git a/src-console/System.Linq.Dynamic.Core.ConsoleTestApp.net40/ConsoleApp_net40_sqlite_original.csproj b/src-console/System.Linq.Dynamic.Core.ConsoleTestApp.net40/ConsoleApp_net40_sqlite_original.csproj index c4b12873..713fde4b 100644 --- a/src-console/System.Linq.Dynamic.Core.ConsoleTestApp.net40/ConsoleApp_net40_sqlite_original.csproj +++ b/src-console/System.Linq.Dynamic.Core.ConsoleTestApp.net40/ConsoleApp_net40_sqlite_original.csproj @@ -97,6 +97,8 @@ Helpers\TestEnum.cs + + diff --git a/src-console/System.Linq.Dynamic.Core.ConsoleTestApp.net40/Helpers/Models/UserProfileDetails.cs b/src-console/System.Linq.Dynamic.Core.ConsoleTestApp.net40/Helpers/Models/UserProfileDetails.cs new file mode 100644 index 00000000..ae58aa82 --- /dev/null +++ b/src-console/System.Linq.Dynamic.Core.ConsoleTestApp.net40/Helpers/Models/UserProfileDetails.cs @@ -0,0 +1,9 @@ +namespace System.Linq.Dynamic.Core.Tests.Helpers.Models +{ + public class UserProfileDetails + { + private long Id { get; set; } + + private long Id2 { get; set; } + } +} diff --git a/src-console/System.Linq.Dynamic.Core.ConsoleTestApp.net40/Helpers/Models/UserState.cs b/src-console/System.Linq.Dynamic.Core.ConsoleTestApp.net40/Helpers/Models/UserState.cs new file mode 100644 index 00000000..60c6bb68 --- /dev/null +++ b/src-console/System.Linq.Dynamic.Core.ConsoleTestApp.net40/Helpers/Models/UserState.cs @@ -0,0 +1,10 @@ +namespace System.Linq.Dynamic.Core.Tests.Helpers.Models +{ + public class UserState + { + public Guid StatusCode { get; set; } + public string Description { get; set; } + + public static implicit operator Guid(UserState state) => state?.StatusCode ?? Guid.Empty; + } +} From cd4b420d221c53b1e00315aa66c22be94651aef2 Mon Sep 17 00:00:00 2001 From: Oleg Nadymov Date: Thu, 2 Aug 2018 00:44:50 +0300 Subject: [PATCH 03/11] Fix for missing classes: UserState and UserProfileDetails --- .../ConsoleApp_net40_sqlite_original.csproj | 8 ++++++-- .../Helpers/Models/UserProfileDetails.cs | 9 --------- .../Helpers/Models/UserState.cs | 10 ---------- 3 files changed, 6 insertions(+), 21 deletions(-) delete mode 100644 src-console/System.Linq.Dynamic.Core.ConsoleTestApp.net40/Helpers/Models/UserProfileDetails.cs delete mode 100644 src-console/System.Linq.Dynamic.Core.ConsoleTestApp.net40/Helpers/Models/UserState.cs diff --git a/src-console/System.Linq.Dynamic.Core.ConsoleTestApp.net40/ConsoleApp_net40_sqlite_original.csproj b/src-console/System.Linq.Dynamic.Core.ConsoleTestApp.net40/ConsoleApp_net40_sqlite_original.csproj index 713fde4b..459c5903 100644 --- a/src-console/System.Linq.Dynamic.Core.ConsoleTestApp.net40/ConsoleApp_net40_sqlite_original.csproj +++ b/src-console/System.Linq.Dynamic.Core.ConsoleTestApp.net40/ConsoleApp_net40_sqlite_original.csproj @@ -97,8 +97,12 @@ Helpers\TestEnum.cs - - + + Helpers\Models\UserProfileDetails.cs + + + Helpers\Models\UserState.cs + diff --git a/src-console/System.Linq.Dynamic.Core.ConsoleTestApp.net40/Helpers/Models/UserProfileDetails.cs b/src-console/System.Linq.Dynamic.Core.ConsoleTestApp.net40/Helpers/Models/UserProfileDetails.cs deleted file mode 100644 index ae58aa82..00000000 --- a/src-console/System.Linq.Dynamic.Core.ConsoleTestApp.net40/Helpers/Models/UserProfileDetails.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace System.Linq.Dynamic.Core.Tests.Helpers.Models -{ - public class UserProfileDetails - { - private long Id { get; set; } - - private long Id2 { get; set; } - } -} diff --git a/src-console/System.Linq.Dynamic.Core.ConsoleTestApp.net40/Helpers/Models/UserState.cs b/src-console/System.Linq.Dynamic.Core.ConsoleTestApp.net40/Helpers/Models/UserState.cs deleted file mode 100644 index 60c6bb68..00000000 --- a/src-console/System.Linq.Dynamic.Core.ConsoleTestApp.net40/Helpers/Models/UserState.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace System.Linq.Dynamic.Core.Tests.Helpers.Models -{ - public class UserState - { - public Guid StatusCode { get; set; } - public string Description { get; set; } - - public static implicit operator Guid(UserState state) => state?.StatusCode ?? Guid.Empty; - } -} From c7d29f4a6f1e817fbc8f4656d5f8a37b5904fd99 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Thu, 2 Aug 2018 13:17:18 +0200 Subject: [PATCH 04/11] Small update on unit-test (no need to check for `DoesNotThrow`) --- .../DynamicExpressionParserTests.cs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs b/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs index 4af2f273..93b3bd76 100644 --- a/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs +++ b/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs @@ -423,22 +423,21 @@ public void ParseLambda_IllegalMethodCall_ThrowsException() [Fact] public void ParseLambda_CustomMethod() { + // Assign var config = new ParsingConfig { CustomTypeProvider = new TestCustomTypeProvider() }; var context = new CustomClassWithStaticMethod(); - var original = $"{nameof(CustomClassWithStaticMethod)}.{nameof(CustomClassWithStaticMethod.GetAge)}(10)"; - int result = 0; - Check.ThatCode(() => - { - var expression = System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda(config, typeof(CustomClassWithStaticMethod), null, original); - Delegate del = expression.Compile(); - result = (int) del.DynamicInvoke(context); - }) - .DoesNotThrow(); + string expression = $"{nameof(CustomClassWithStaticMethod)}.{nameof(CustomClassWithStaticMethod.GetAge)}(10)"; + // Act + var lambdaExpression = DynamicExpressionParser.ParseLambda(config, typeof(CustomClassWithStaticMethod), null, expression); + Delegate del = lambdaExpression.Compile(); + int result = (int)del.DynamicInvoke(context); + + // Assert Check.That(result).IsEqualTo(10); } } From e6ddce6bd6174d1017c0ce056517ed456a07f6f4 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Thu, 2 Aug 2018 13:54:34 +0200 Subject: [PATCH 05/11] add comment to appveyor --- appveyor.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index b21afa23..441c9fe6 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -19,11 +19,15 @@ install: environment: PATH: $(PATH);$(PROGRAMFILES)\dotnet\ + + # https://www.appveyor.com/docs/build-configuration/#secure-variables + # However, secure variables are not decoded during Pull Request builds which prevents someone from submitting PR with malicious build script displaying those variables. In more controlled environment through with a trusted team and private GitHub repositories there is an option on General tab of project settings to allow secure variables for PRs. COVERALLS_REPO_TOKEN: secure: tsTABRbCmdWFLT194XNIrpurerOfjN6cEoxt2RaSUfLmUIgra/+CwuqVkv0sPRop SONAR_TOKEN: secure: guog1+ttdnlD8sVgvizlewksm3qbO7dy2oZUcR8WhurWYvdOByinxXo732hmSaMT + before_build: # Remove UAP10 and netstandard20 from csproj #- cmd: copy /Y src\System.Linq.Dynamic.Core\System.Linq.Dynamic.Core.AppVeyor.csproj src\System.Linq.Dynamic.Core\System.Linq.Dynamic.Core.csproj From 9a5e5e3d315a0dd33fe75edd4cab6f1692113475 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Thu, 2 Aug 2018 14:10:04 +0200 Subject: [PATCH 06/11] Only run SonarScanner on master branch --- appveyor.yml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 441c9fe6..f4be8b72 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -42,8 +42,10 @@ before_build: - dotnet restore test\EntityFramework.DynamicLinq.Tests\EntityFramework.DynamicLinq.Tests.csproj build_script: -# Begin SonarScanner -- dotnet sonarscanner begin /k:"system.linq.dynamic.core" /d:sonar.organization="stefh-github" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.login="%SONAR_TOKEN%" /v:"%APPVEYOR_BUILD_NUMBER%" /d:sonar.cs.opencover.reportsPaths="%CD%\coverage.xml" +if ($env:APPVEYOR_REPO_BRANCH -eq 'master') { + # Begin SonarScanner + - dotnet sonarscanner begin /k:"system.linq.dynamic.core" /d:sonar.organization="stefh-github" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.login="%SONAR_TOKEN%" /v:"%APPVEYOR_BUILD_NUMBER%" /d:sonar.cs.opencover.reportsPaths="%CD%\coverage.xml" +} # Build Code - dotnet build src\EntityFramework.DynamicLinq\EntityFramework.DynamicLinq.csproj -c %CONFIGURATION% @@ -63,7 +65,10 @@ test_script: - cmd: '"OpenCover\tools\OpenCover.Console.exe" -target:dotnet.exe -targetargs:"test test\System.Linq.Dynamic.Core.Tests\System.Linq.Dynamic.Core.Tests.csproj --configuration %CONFIGURATION% --framework netcoreapp1.1 --no-build" -output:coverage.xml -register:user -filter:"+[Microsoft.EntityFrameworkCore.DynamicLinq]* +[System.Linq.Dynamic.Core]* -[*Tests*]*" -nodefaultfilters -returntargetcode -oldstyle' - codecov -f "coverage.xml" - coveralls.net\tools\csmacnz.Coveralls.exe --opencover -i .\coverage.xml -- dotnet sonarscanner end /d:sonar.login="%SONAR_TOKEN%" + +if ($env:APPVEYOR_REPO_BRANCH -eq 'master') { + - dotnet sonarscanner end /d:sonar.login="%SONAR_TOKEN%" +} # Run tests for EntityFramework.DynamicLinq - dotnet test -c %CONFIGURATION% --no-build test\EntityFramework.DynamicLinq.Tests\EntityFramework.DynamicLinq.Tests.csproj From 02bda3357ebff67fc01b6256364353888da83b9d Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Thu, 2 Aug 2018 14:23:54 +0200 Subject: [PATCH 07/11] APPVEYOR_REPO_BRANCH --- appveyor.yml | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index f4be8b72..25af2952 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -42,10 +42,8 @@ before_build: - dotnet restore test\EntityFramework.DynamicLinq.Tests\EntityFramework.DynamicLinq.Tests.csproj build_script: -if ($env:APPVEYOR_REPO_BRANCH -eq 'master') { - # Begin SonarScanner - - dotnet sonarscanner begin /k:"system.linq.dynamic.core" /d:sonar.organization="stefh-github" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.login="%SONAR_TOKEN%" /v:"%APPVEYOR_BUILD_NUMBER%" /d:sonar.cs.opencover.reportsPaths="%CD%\coverage.xml" -} +# Begin SonarScanner +- IF %APPVEYOR_REPO_BRANCH%==master dotnet sonarscanner begin /k:"system.linq.dynamic.core" /d:sonar.organization="stefh-github" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.login="%SONAR_TOKEN%" /v:"%APPVEYOR_BUILD_NUMBER%" /d:sonar.cs.opencover.reportsPaths="%CD%\coverage.xml" # Build Code - dotnet build src\EntityFramework.DynamicLinq\EntityFramework.DynamicLinq.csproj -c %CONFIGURATION% @@ -65,10 +63,7 @@ test_script: - cmd: '"OpenCover\tools\OpenCover.Console.exe" -target:dotnet.exe -targetargs:"test test\System.Linq.Dynamic.Core.Tests\System.Linq.Dynamic.Core.Tests.csproj --configuration %CONFIGURATION% --framework netcoreapp1.1 --no-build" -output:coverage.xml -register:user -filter:"+[Microsoft.EntityFrameworkCore.DynamicLinq]* +[System.Linq.Dynamic.Core]* -[*Tests*]*" -nodefaultfilters -returntargetcode -oldstyle' - codecov -f "coverage.xml" - coveralls.net\tools\csmacnz.Coveralls.exe --opencover -i .\coverage.xml - -if ($env:APPVEYOR_REPO_BRANCH -eq 'master') { - - dotnet sonarscanner end /d:sonar.login="%SONAR_TOKEN%" -} +- IF %APPVEYOR_REPO_BRANCH%==master dotnet sonarscanner end /d:sonar.login="%SONAR_TOKEN%" # Run tests for EntityFramework.DynamicLinq - dotnet test -c %CONFIGURATION% --no-build test\EntityFramework.DynamicLinq.Tests\EntityFramework.DynamicLinq.Tests.csproj From e77c116ca6e32d48746a46d04b3bfe74345dd289 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Thu, 2 Aug 2018 14:53:29 +0200 Subject: [PATCH 08/11] APPVEYOR_PULL_REQUEST_NUMBER --- appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 25af2952..c5efbec4 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -43,7 +43,7 @@ before_build: build_script: # Begin SonarScanner -- IF %APPVEYOR_REPO_BRANCH%==master dotnet sonarscanner begin /k:"system.linq.dynamic.core" /d:sonar.organization="stefh-github" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.login="%SONAR_TOKEN%" /v:"%APPVEYOR_BUILD_NUMBER%" /d:sonar.cs.opencover.reportsPaths="%CD%\coverage.xml" +- ps: if (-Not $env:APPVEYOR_PULL_REQUEST_NUMBER) { & "dotnet sonarscanner begin /k:\"system.linq.dynamic.core\" /d:sonar.organization=\"stefh-github\" /d:sonar.host.url=\"https://sonarcloud.io\" /d:sonar.login=\"%SONAR_TOKEN%\" /v:\"%APPVEYOR_BUILD_NUMBER%\" /d:sonar.cs.opencover.reportsPaths=\"%CD%\coverage.xml\"" } # Build Code - dotnet build src\EntityFramework.DynamicLinq\EntityFramework.DynamicLinq.csproj -c %CONFIGURATION% @@ -63,7 +63,7 @@ test_script: - cmd: '"OpenCover\tools\OpenCover.Console.exe" -target:dotnet.exe -targetargs:"test test\System.Linq.Dynamic.Core.Tests\System.Linq.Dynamic.Core.Tests.csproj --configuration %CONFIGURATION% --framework netcoreapp1.1 --no-build" -output:coverage.xml -register:user -filter:"+[Microsoft.EntityFrameworkCore.DynamicLinq]* +[System.Linq.Dynamic.Core]* -[*Tests*]*" -nodefaultfilters -returntargetcode -oldstyle' - codecov -f "coverage.xml" - coveralls.net\tools\csmacnz.Coveralls.exe --opencover -i .\coverage.xml -- IF %APPVEYOR_REPO_BRANCH%==master dotnet sonarscanner end /d:sonar.login="%SONAR_TOKEN%" +- ps: if (-Not $env:APPVEYOR_PULL_REQUEST_NUMBER) { & "dotnet sonarscanner end /d:sonar.login=\"%SONAR_TOKEN%\"" } # Run tests for EntityFramework.DynamicLinq - dotnet test -c %CONFIGURATION% --no-build test\EntityFramework.DynamicLinq.Tests\EntityFramework.DynamicLinq.Tests.csproj From d7d432b3b6b916ad0f42a22e0be8156f3a6fb5e7 Mon Sep 17 00:00:00 2001 From: Oleg Nadymov Date: Fri, 10 Aug 2018 10:31:16 +0300 Subject: [PATCH 09/11] No working unit test for inner double quotes --- .../DynamicExpressionParserTests.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs b/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs index 93b3bd76..0241975f 100644 --- a/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs +++ b/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs @@ -440,5 +440,17 @@ public void ParseLambda_CustomMethod() // Assert Check.That(result).IsEqualTo(10); } + + [Fact] + public void ParseLambda_With_InnerStringLiteral() + { + var originalTrueValue = "simple + \"quoted\""; + var doubleQuotedTrueValue = "simple + \"\"quoted\"\""; + var expressionText = $"iif(1>0, \"{doubleQuotedTrueValue}\", \"false\")"; + var lambda = DynamicExpressionParser.ParseLambda(typeof(string), null, expressionText); + var del = lambda.Compile(); + object result = del.DynamicInvoke(String.Empty); + Check.That(result).IsEqualTo(originalTrueValue); + } } } From 2c840f8957df469d249479ca5202c25fd5798e71 Mon Sep 17 00:00:00 2001 From: Oleg Nadymov Date: Fri, 10 Aug 2018 10:57:26 +0300 Subject: [PATCH 10/11] Fix for inner double quotes --- .../Parser/ExpressionParser.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs index ab4bd3c4..f9761fd8 100644 --- a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs +++ b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs @@ -755,6 +755,16 @@ Expression ParseStringLiteral() _textParser.ValidateToken(TokenId.StringLiteral); char quote = _textParser.CurrentToken.Text[0]; string s = _textParser.CurrentToken.Text.Substring(1, _textParser.CurrentToken.Text.Length - 2); + int index1 = 0; + while (true) + { + int index2 = s.IndexOf(quote, index1); + if (index2 < 0) + break; + if (index2 + 1 < s.Length && s[index2 + 1] == quote) + s = s.Remove(index2, 1); + index1 = index2 + 1; + } if (quote == '\'') { From e13c9d1236f9aee17a9da3d5807a39fae4fc899e Mon Sep 17 00:00:00 2001 From: Oleg Nadymov Date: Fri, 10 Aug 2018 15:11:38 +0300 Subject: [PATCH 11/11] Using {} --- src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs index f9761fd8..89d0af08 100644 --- a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs +++ b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs @@ -760,9 +760,14 @@ Expression ParseStringLiteral() { int index2 = s.IndexOf(quote, index1); if (index2 < 0) + { break; + } + if (index2 + 1 < s.Length && s[index2 + 1] == quote) + { s = s.Remove(index2, 1); + } index1 = index2 + 1; }