diff --git a/src/System.Linq.Dynamic.Core/ParsingConfig.cs b/src/System.Linq.Dynamic.Core/ParsingConfig.cs
index 66f41fab..96f6d46c 100644
--- a/src/System.Linq.Dynamic.Core/ParsingConfig.cs
+++ b/src/System.Linq.Dynamic.Core/ParsingConfig.cs
@@ -4,221 +4,220 @@
using System.Linq.Dynamic.Core.CustomTypeProviders;
using System.Linq.Dynamic.Core.Parser;
-namespace System.Linq.Dynamic.Core
+namespace System.Linq.Dynamic.Core;
+
+///
+/// Configuration class for System.Linq.Dynamic.Core.
+///
+public class ParsingConfig
{
///
- /// Configuration class for System.Linq.Dynamic.Core.
+ /// Default ParsingConfig
///
- public class ParsingConfig
+ public static ParsingConfig Default { get; } = new ParsingConfig();
+
+ ///
+ /// Default ParsingConfig for EntityFramework Core 2.1 and higher
+ ///
+ public static ParsingConfig DefaultEFCore21 { get; } = new ParsingConfig
{
- ///
- /// Default ParsingConfig
- ///
- public static ParsingConfig Default { get; } = new ParsingConfig();
-
- ///
- /// Default ParsingConfig for EntityFramework Core 2.1 and higher
- ///
- public static ParsingConfig DefaultEFCore21 { get; } = new ParsingConfig
- {
- EvaluateGroupByAtDatabase = true
- };
+ EvaluateGroupByAtDatabase = true
+ };
- /// Gets or sets if parameter, method, and properties resolution should be case sensitive or not (false by default).
- public bool IsCaseSensitive { get; set; }
+ /// Gets or sets if parameter, method, and properties resolution should be case sensitive or not (false by default).
+ public bool IsCaseSensitive { get; set; }
- ///
- /// Default ParsingConfig for CosmosDb
- ///
- public static ParsingConfig DefaultCosmosDb { get; } = new ParsingConfig
- {
- RenameEmptyParameterExpressionNames = true
- };
+ ///
+ /// Default ParsingConfig for CosmosDb
+ ///
+ public static ParsingConfig DefaultCosmosDb { get; } = new ParsingConfig
+ {
+ RenameEmptyParameterExpressionNames = true
+ };
- private IDynamicLinkCustomTypeProvider? _customTypeProvider;
+ private IDynamicLinkCustomTypeProvider? _customTypeProvider;
- private IExpressionPromoter? _expressionPromoter;
+ private IExpressionPromoter? _expressionPromoter;
- private IQueryableAnalyzer? _queryableAnalyzer;
+ private IQueryableAnalyzer? _queryableAnalyzer;
- ///
- /// Gets or sets the .
- ///
- public IDynamicLinkCustomTypeProvider CustomTypeProvider
+ ///
+ /// Gets or sets the .
+ ///
+ public IDynamicLinkCustomTypeProvider CustomTypeProvider
+ {
+ get
{
- get
- {
#if !( WINDOWS_APP || UAP10_0 || NETSTANDARD)
- // only use DefaultDynamicLinqCustomTypeProvider for full .NET Framework and NET Core App 2.x
- return _customTypeProvider ??= new DefaultDynamicLinqCustomTypeProvider();
+ // only use DefaultDynamicLinqCustomTypeProvider for full .NET Framework and NET Core App 2.x
+ return _customTypeProvider ??= new DefaultDynamicLinqCustomTypeProvider();
#else
- return _customTypeProvider;
+ return _customTypeProvider;
#endif
- }
+ }
- set
+ set
+ {
+ // ReSharper disable once RedundantCheckBeforeAssignment
+ if (_customTypeProvider != value)
{
- // ReSharper disable once RedundantCheckBeforeAssignment
- if (_customTypeProvider != value)
- {
- _customTypeProvider = value;
- }
+ _customTypeProvider = value;
}
}
+ }
- ///
- /// Gets or sets the .
- ///
- public IExpressionPromoter ExpressionPromoter
- {
- get => _expressionPromoter ??= new ExpressionPromoter(this);
+ ///
+ /// Gets or sets the .
+ ///
+ public IExpressionPromoter ExpressionPromoter
+ {
+ get => _expressionPromoter ??= new ExpressionPromoter(this);
- set
+ set
+ {
+ // ReSharper disable once RedundantCheckBeforeAssignment
+ if (_expressionPromoter != value)
{
- // ReSharper disable once RedundantCheckBeforeAssignment
- if (_expressionPromoter != value)
- {
- _expressionPromoter = value;
- }
+ _expressionPromoter = value;
}
}
+ }
- ///
- /// Gets or sets the .
- ///
- public IQueryableAnalyzer QueryableAnalyzer
+ ///
+ /// Gets or sets the .
+ ///
+ public IQueryableAnalyzer QueryableAnalyzer
+ {
+ get
{
- get
- {
- return _queryableAnalyzer ??= new DefaultQueryableAnalyzer();
- }
+ return _queryableAnalyzer ??= new DefaultQueryableAnalyzer();
+ }
- set
+ set
+ {
+ // ReSharper disable once RedundantCheckBeforeAssignment
+ if (_queryableAnalyzer != value)
{
- // ReSharper disable once RedundantCheckBeforeAssignment
- if (_queryableAnalyzer != value)
- {
- _queryableAnalyzer = value;
- }
+ _queryableAnalyzer = value;
}
}
-
- ///
- /// Determines if the context keywords (it, parent, and root) are valid and usable inside a Dynamic Linq string expression.
- /// Does not affect the usability of the equivalent context symbols ($, ^ and ~).
- ///
- /// Default value is false.
- ///
- public bool AreContextKeywordsEnabled { get; set; } = true;
-
- ///
- /// Gets or sets a value indicating whether the EntityFramework version supports evaluating GroupBy at database level.
- /// See https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-2.1#linq-groupby-translation
- ///
- /// Remark: when this setting is set to 'true', make sure to supply this ParsingConfig as first parameter on the extension methods.
- ///
- /// Default value is false.
- ///
- public bool EvaluateGroupByAtDatabase { get; set; }
-
- ///
- /// Use Parameterized Names in generated dynamic SQL query.
- /// See https://github.com/graeme-hill/gblog/blob/master/source_content/articles/2014.139_entity-framework-dynamic-queries-and-parameterization.mkd
- ///
- /// Default value is false.
- ///
- public bool UseParameterizedNamesInDynamicQuery { get; set; } = false;
-
- ///
- /// Allows the New() keyword to evaluate any available Type.
- ///
- /// Default value is false.
- ///
- public bool AllowNewToEvaluateAnyType { get; set; } = false;
-
- ///
- /// Renames the (Typed)ParameterExpression empty Name to a the correct supplied name from `it`.
- ///
- /// Default value is false.
- ///
- public bool RenameParameterExpression { get; set; } = false;
-
- ///
- /// Prevents any System.Linq.Expressions.ParameterExpression.Name value from being empty by substituting a random 16 character word.
- ///
- /// Default value is false.
- ///
- public bool RenameEmptyParameterExpressionNames { get; set; }
-
- ///
- /// By default when a member is not found in a type and the type has a string based index accessor it will be parsed as an index accessor. Use
- /// this flag to disable this behaviour and have parsing fail when parsing an expression
- /// where a member access on a non existing member happens.
- ///
- /// Default value is false.
- ///
- public bool DisableMemberAccessToIndexAccessorFallback { get; set; } = false;
-
- ///
- /// By default finding types by a simple name is not supported.
- /// Use this flag to use the CustomTypeProvider to resolve types by a simple name like "Employee" instead of "MyDatabase.Entities.Employee".
- /// Note that a first matching type is returned and this functionality needs to scan all types from all assemblies, so use with caution.
- ///
- /// Default value is false.
- ///
- public bool ResolveTypesBySimpleName { get; set; } = false;
-
- ///
- /// Support enumeration-types from the System namespace in mscorlib. An example could be "StringComparison".
- ///
- /// Default value is true.
- ///
- public bool SupportEnumerationsFromSystemNamespace { get; set; } = true;
-
- ///
- /// By default DateTime (like 'Fri, 10 May 2019 11:03:17 GMT') is parsed as local time.
- /// Use this flag to parse all DateTime strings as UTC.
- ///
- /// Default value is false.
- ///
- public bool DateTimeIsParsedAsUTC { get; set; } = false;
-
- ///
- /// The number parsing culture.
- ///
- /// Default value is CultureInfo.InvariantCulture
- ///
- public CultureInfo? NumberParseCulture { get; set; } = CultureInfo.InvariantCulture;
-
- ///
- /// Additional TypeConverters
- ///
- public IDictionary? TypeConverters { get; set; }
-
- ///
- /// When using the NullPropagating function np(...), use a "default value" for non-nullable value types instead of "null value".
- ///
- /// Default value is false.
- ///
- public bool NullPropagatingUseDefaultValueForNonNullableValueTypes { get; set; } = false;
-
- ///
- /// Support casting to a full qualified type using a string (double quoted value).
- ///
- /// var result = queryable.Select($"\"System.DateTime\"(LastUpdate)");
- ///
- ///
- /// Default value is true.
- ///
- public bool SupportCastingToFullyQualifiedTypeAsString { get; set; } = true;
-
- ///
- /// When the type and property have the same name the parser takes the property instead of type when this setting is set to true.
- ///
- /// The value from this setting should also be set to true when ExtensionMethods are used.
- ///
- /// Default value is false.
- ///
- public bool PrioritizePropertyOrFieldOverTheType { get; set; }
}
+
+ ///
+ /// Determines if the context keywords (it, parent, and root) are valid and usable inside a Dynamic Linq string expression.
+ /// Does not affect the usability of the equivalent context symbols ($, ^ and ~).
+ ///
+ /// Default value is false.
+ ///
+ public bool AreContextKeywordsEnabled { get; set; } = true;
+
+ ///
+ /// Gets or sets a value indicating whether the EntityFramework version supports evaluating GroupBy at database level.
+ /// See https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-2.1#linq-groupby-translation
+ ///
+ /// Remark: when this setting is set to 'true', make sure to supply this ParsingConfig as first parameter on the extension methods.
+ ///
+ /// Default value is false.
+ ///
+ public bool EvaluateGroupByAtDatabase { get; set; }
+
+ ///
+ /// Use Parameterized Names in generated dynamic SQL query.
+ /// See https://github.com/graeme-hill/gblog/blob/master/source_content/articles/2014.139_entity-framework-dynamic-queries-and-parameterization.mkd
+ ///
+ /// Default value is false.
+ ///
+ public bool UseParameterizedNamesInDynamicQuery { get; set; } = false;
+
+ ///
+ /// Allows the New() keyword to evaluate any available Type.
+ ///
+ /// Default value is false.
+ ///
+ public bool AllowNewToEvaluateAnyType { get; set; } = false;
+
+ ///
+ /// Renames the (Typed)ParameterExpression empty Name to a the correct supplied name from `it`.
+ ///
+ /// Default value is false.
+ ///
+ public bool RenameParameterExpression { get; set; } = false;
+
+ ///
+ /// Prevents any System.Linq.Expressions.ParameterExpression.Name value from being empty by substituting a random 16 character word.
+ ///
+ /// Default value is false.
+ ///
+ public bool RenameEmptyParameterExpressionNames { get; set; }
+
+ ///
+ /// By default when a member is not found in a type and the type has a string based index accessor it will be parsed as an index accessor. Use
+ /// this flag to disable this behaviour and have parsing fail when parsing an expression
+ /// where a member access on a non existing member happens.
+ ///
+ /// Default value is false.
+ ///
+ public bool DisableMemberAccessToIndexAccessorFallback { get; set; } = false;
+
+ ///
+ /// By default finding types by a simple name is not supported.
+ /// Use this flag to use the CustomTypeProvider to resolve types by a simple name like "Employee" instead of "MyDatabase.Entities.Employee".
+ /// Note that a first matching type is returned and this functionality needs to scan all types from all assemblies, so use with caution.
+ ///
+ /// Default value is false.
+ ///
+ public bool ResolveTypesBySimpleName { get; set; } = false;
+
+ ///
+ /// Support enumeration-types from the System namespace in mscorlib. An example could be "StringComparison".
+ ///
+ /// Default value is true.
+ ///
+ public bool SupportEnumerationsFromSystemNamespace { get; set; } = true;
+
+ ///
+ /// By default DateTime (like 'Fri, 10 May 2019 11:03:17 GMT') is parsed as local time.
+ /// Use this flag to parse all DateTime strings as UTC.
+ ///
+ /// Default value is false.
+ ///
+ public bool DateTimeIsParsedAsUTC { get; set; } = false;
+
+ ///
+ /// The number parsing culture.
+ ///
+ /// Default value is CultureInfo.InvariantCulture
+ ///
+ public CultureInfo? NumberParseCulture { get; set; } = CultureInfo.InvariantCulture;
+
+ ///
+ /// Additional TypeConverters
+ ///
+ public IDictionary? TypeConverters { get; set; }
+
+ ///
+ /// When using the NullPropagating function np(...), use a "default value" for non-nullable value types instead of "null value".
+ ///
+ /// Default value is false.
+ ///
+ public bool NullPropagatingUseDefaultValueForNonNullableValueTypes { get; set; } = false;
+
+ ///
+ /// Support casting to a full qualified type using a string (double quoted value).
+ ///
+ /// var result = queryable.Select($"\"System.DateTime\"(LastUpdate)");
+ ///
+ ///
+ /// Default value is true.
+ ///
+ public bool SupportCastingToFullyQualifiedTypeAsString { get; set; } = true;
+
+ ///
+ /// When the type and property have the same name the parser takes the property instead of type when this setting is set to true.
+ ///
+ /// This setting is also used for calling ExtensionMethods.
+ ///
+ /// Default value is true.
+ ///
+ public bool PrioritizePropertyOrFieldOverTheType { get; set; } = true;
}
\ 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 83d99980..17309c5b 100644
--- a/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs
+++ b/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs
@@ -1082,6 +1082,20 @@ public void DynamicExpressionParser_ParseLambda_With_DateTime_Equals_String()
Assert.True(result);
}
+ [Fact]
+ public void DynamicExpressionParser_ParseLambda_With_DateTime_UtcNow_AddHours_ToString_Issue675()
+ {
+ // Arrange
+ var expressionText = "DateTime.UtcNow.AddHours(2).ToString(\"o\")";
+ var parameterExpression = Expression.Parameter(typeof(string));
+
+ // Act
+ var lambda = DynamicExpressionParser.ParseLambda(new[] { parameterExpression }, typeof(object), expressionText);
+
+ // Assert
+ lambda.Should().NotBeNull();
+ }
+
[Fact]
public void DynamicExpressionParser_ParseLambda_With_Guid_Equals_String()
{
@@ -1459,7 +1473,7 @@ public void DynamicExpressionParser_ParseLambda_String_TrimEnd_1_Parameter()
result.Should().BeTrue();
}
- public class DefaultDynamicLinqCustomTypeProviderForGenericExtensionMethod : CustomTypeProviders.DefaultDynamicLinqCustomTypeProvider
+ 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/ExpressionParserTests.TypeAccess.cs b/test/System.Linq.Dynamic.Core.Tests/Parser/ExpressionParserTests.TypeAccess.cs
index 0129e36e..9c92c1b3 100644
--- a/test/System.Linq.Dynamic.Core.Tests/Parser/ExpressionParserTests.TypeAccess.cs
+++ b/test/System.Linq.Dynamic.Core.Tests/Parser/ExpressionParserTests.TypeAccess.cs
@@ -4,229 +4,228 @@
using FluentAssertions;
using Xunit;
-namespace System.Linq.Dynamic.Core.Tests.Parser
+namespace System.Linq.Dynamic.Core.Tests.Parser;
+
+partial class ExpressionParserTests
{
- partial class ExpressionParserTests
+ [Fact]
+ public void ParseTypeAccess_Via_Constructor_CharAndInt_To_String()
+ {
+ // Arrange
+ var parameter = Expression.Parameter(typeof(string));
+
+ // Act
+ var parser = new ExpressionParser(new[] { parameter }, "string('c', 3)", new object[] { }, ParsingConfig.Default);
+ var expression = parser.Parse(typeof(string));
+
+ // Assert
+ expression.ToString().Should().Be("new String(c, 3)");
+ }
+
+ [Theory]
+ [InlineData(123, "new DateTime(123)")]
+ [InlineData(633979008000000000, "new DateTime(633979008000000000)")]
+ public void ParseTypeAccess_Via_Constructor_Ticks_To_DateTime(object ticks, string result)
+ {
+ // Arrange
+ var parameter = Expression.Parameter(typeof(DateTime));
+
+ // Act
+ var parser = new ExpressionParser(new[] { parameter }, $"DateTime({ticks})", new object[] { }, ParsingConfig.Default);
+ var expression = parser.Parse(typeof(DateTime));
+
+ // Assert
+ expression.ToString().Should().Be(result);
+ }
+
+ [Fact]
+ public void ParseTypeAccess_Via_Constructor_String_To_DateTime_Valid()
+ {
+ // Arrange
+ string str = "\"2020-10-31 09:15:11\"";
+ var parameter = Expression.Parameter(typeof(DateTime));
+
+ // Act
+ var parser = new ExpressionParser(new[] { parameter }, $"DateTime({str})", new object[] { }, ParsingConfig.Default);
+ var expression = parser.Parse(typeof(DateTime));
+
+ // Assert
+ expression.ToString().Should().NotBeEmpty();
+ }
+
+ [Fact]
+ public void ParseTypeAccess_Via_Constructor_Arguments_To_DateTime_Valid()
{
- [Fact]
- public void ParseTypeAccess_Via_Constructor_CharAndInt_To_String()
- {
- // Arrange
- var parameter = Expression.Parameter(typeof(string));
-
- // Act
- var parser = new ExpressionParser(new[] { parameter }, $"string('c', 3)", new object[] { }, ParsingConfig.Default);
- var expression = parser.Parse(typeof(string));
-
- // Assert
- expression.ToString().Should().Be("new String(c, 3)");
- }
-
- [Theory]
- [InlineData(123, "new DateTime(123)")]
- [InlineData(633979008000000000, "new DateTime(633979008000000000)")]
- public void ParseTypeAccess_Via_Constructor_Ticks_To_DateTime(object ticks, string result)
- {
- // Arrange
- var parameter = Expression.Parameter(typeof(DateTime));
-
- // Act
- var parser = new ExpressionParser(new[] { parameter }, $"DateTime({ticks})", new object[] { }, ParsingConfig.Default);
- var expression = parser.Parse(typeof(DateTime));
-
- // Assert
- expression.ToString().Should().Be(result);
- }
-
- [Fact]
- public void ParseTypeAccess_Via_Constructor_String_To_DateTime_Valid()
- {
- // Arrange
- string str = "\"2020-10-31 09:15:11\"";
- var parameter = Expression.Parameter(typeof(DateTime));
-
- // Act
- var parser = new ExpressionParser(new[] { parameter }, $"DateTime({str})", new object[] { }, ParsingConfig.Default);
- var expression = parser.Parse(typeof(DateTime));
-
- // Assert
- expression.ToString().Should().NotBeEmpty();
- }
-
- [Fact]
- public void ParseTypeAccess_Via_Constructor_Arguments_To_DateTime_Valid()
- {
- // Arrange
- var arguments = "2022, 10, 31, 9, 15, 11";
- var parameter = Expression.Parameter(typeof(DateTime));
-
- // Act
- var parser = new ExpressionParser(new[] { parameter }, $"DateTime({arguments})", new object[] { }, ParsingConfig.Default);
- var expression = parser.Parse(typeof(DateTime));
-
- // Assert
- expression.ToString().Should().NotBeEmpty();
- }
-
- [Theory]
- [InlineData(null)]
- [InlineData("\"abc\"")]
- public void ParseTypeAccess_Via_Constructor_Any_To_DateTime_Invalid(object any)
- {
- // Arrange
- var parameter = Expression.Parameter(typeof(DateTime));
-
- // Act
- var parser = new ExpressionParser(new[] { parameter }, $"DateTime({any})", new object[] { }, ParsingConfig.Default);
- Action a = () => parser.Parse(typeof(DateTime));
-
- // Assert
- a.Should().Throw();
- }
+ // Arrange
+ var arguments = "2022, 10, 31, 9, 15, 11";
+ var parameter = Expression.Parameter(typeof(DateTime));
+
+ // Act
+ var parser = new ExpressionParser(new[] { parameter }, $"DateTime({arguments})", new object[] { }, ParsingConfig.Default);
+ var expression = parser.Parse(typeof(DateTime));
+
+ // Assert
+ expression.ToString().Should().NotBeEmpty();
+ }
+
+ [Theory]
+ [InlineData(null)]
+ [InlineData("\"abc\"")]
+ public void ParseTypeAccess_Via_Constructor_Any_To_DateTime_Invalid(object any)
+ {
+ // Arrange
+ var parameter = Expression.Parameter(typeof(DateTime));
+
+ // Act
+ var parser = new ExpressionParser(new[] { parameter }, $"DateTime({any})", new object[] { }, ParsingConfig.Default);
+ Action a = () => parser.Parse(typeof(DateTime));
+
+ // Assert
+ a.Should().Throw();
+ }
#if NET6_0_OR_GREATER
- [Fact]
- public void ParseTypeAccess_Via_Constructor_String_To_DateOnly_Valid()
- {
- // Arrange
- string str = "\"2020-10-31\"";
- var parameter = Expression.Parameter(typeof(DateOnly));
-
- // Act
- var parser = new ExpressionParser(new[] { parameter }, $"DateOnly({str})", new object[] { }, ParsingConfig.Default);
- var expression = parser.Parse(typeof(DateOnly));
-
- // Assert
- expression.ToString().Should().NotBeEmpty();
- }
-
- [Fact]
- public void ParseTypeAccess_Via_Constructor_Arguments_To_DateOnly_Valid()
- {
- // Arrange
- var arguments = "2022, 10, 31";
- var parameter = Expression.Parameter(typeof(DateOnly));
-
- // Act
- var parser = new ExpressionParser(new[] { parameter }, $"DateOnly({arguments})", new object[] { }, ParsingConfig.Default);
- var expression = parser.Parse(typeof(DateOnly));
-
- // Assert
- expression.ToString().Should().NotBeEmpty();
- }
-
- [Theory]
- [InlineData(null)]
- [InlineData("\"abc\"")]
- public void ParseTypeAccess_Via_Constructor_Any_To_DateOnly_Invalid(object any)
- {
- // Arrange
- var parameter = Expression.Parameter(typeof(DateOnly));
-
- // Act
- var parser = new ExpressionParser(new[] { parameter }, $"DateOnly({any})", new object[] { }, ParsingConfig.Default);
- Action a = () => parser.Parse(typeof(DateOnly));
-
- // Assert
- a.Should().Throw();
- }
-
- [Fact]
- public void ParseTypeAccess_Via_Constructor_String_To_TimeOnly_Valid()
- {
- // Arrange
- string str = "\"09:15:11\"";
- var parameter = Expression.Parameter(typeof(TimeOnly));
-
- // Act
- var parser = new ExpressionParser(new[] { parameter }, $"TimeOnly({str})", new object[] { }, ParsingConfig.Default);
- var expression = parser.Parse(typeof(TimeOnly));
-
- // Assert
- expression.ToString().Should().NotBeEmpty();
- }
-
- [Fact]
- public void ParseTypeAccess_Via_Constructor_Arguments_To_TimeOnly_Valid()
- {
- // Arrange
- var arguments = "9, 15, 11";
- var parameter = Expression.Parameter(typeof(TimeOnly));
-
- // Act
- var parser = new ExpressionParser(new[] { parameter }, $"TimeOnly({arguments})", new object[] { }, ParsingConfig.Default);
- var expression = parser.Parse(typeof(TimeOnly));
-
- // Assert
- expression.ToString().Should().NotBeEmpty();
- }
-
- [Theory]
- [InlineData(null)]
- [InlineData("\"abc\"")]
- public void ParseTypeAccess_Via_Constructor_Any_To_TimeOnly_Invalid(object any)
- {
- // Arrange
- var parameter = Expression.Parameter(typeof(TimeOnly));
-
- // Act
- var parser = new ExpressionParser(new[] { parameter }, $"TimeOnly({any})", new object[] { }, ParsingConfig.Default);
- Action a = () => parser.Parse(typeof(TimeOnly));
-
- // Assert
- a.Should().Throw();
- }
+ [Fact]
+ public void ParseTypeAccess_Via_Constructor_String_To_DateOnly_Valid()
+ {
+ // Arrange
+ string str = "\"2020-10-31\"";
+ var parameter = Expression.Parameter(typeof(DateOnly));
+
+ // Act
+ var parser = new ExpressionParser(new[] { parameter }, $"DateOnly({str})", new object[] { }, ParsingConfig.Default);
+ var expression = parser.Parse(typeof(DateOnly));
+
+ // Assert
+ expression.ToString().Should().NotBeEmpty();
+ }
+
+ [Fact]
+ public void ParseTypeAccess_Via_Constructor_Arguments_To_DateOnly_Valid()
+ {
+ // Arrange
+ var arguments = "2022, 10, 31";
+ var parameter = Expression.Parameter(typeof(DateOnly));
+
+ // Act
+ var parser = new ExpressionParser(new[] { parameter }, $"DateOnly({arguments})", new object[] { }, ParsingConfig.Default);
+ var expression = parser.Parse(typeof(DateOnly));
+
+ // Assert
+ expression.ToString().Should().NotBeEmpty();
+ }
+
+ [Theory]
+ [InlineData(null)]
+ [InlineData("\"abc\"")]
+ public void ParseTypeAccess_Via_Constructor_Any_To_DateOnly_Invalid(object any)
+ {
+ // Arrange
+ var parameter = Expression.Parameter(typeof(DateOnly));
+
+ // Act
+ var parser = new ExpressionParser(new[] { parameter }, $"DateOnly({any})", new object[] { }, ParsingConfig.Default);
+ Action a = () => parser.Parse(typeof(DateOnly));
+
+ // Assert
+ a.Should().Throw();
+ }
+
+ [Fact]
+ public void ParseTypeAccess_Via_Constructor_String_To_TimeOnly_Valid()
+ {
+ // Arrange
+ string str = "\"09:15:11\"";
+ var parameter = Expression.Parameter(typeof(TimeOnly));
+
+ // Act
+ var parser = new ExpressionParser(new[] { parameter }, $"TimeOnly({str})", new object[] { }, ParsingConfig.Default);
+ var expression = parser.Parse(typeof(TimeOnly));
+
+ // Assert
+ expression.ToString().Should().NotBeEmpty();
+ }
+
+ [Fact]
+ public void ParseTypeAccess_Via_Constructor_Arguments_To_TimeOnly_Valid()
+ {
+ // Arrange
+ var arguments = "9, 15, 11";
+ var parameter = Expression.Parameter(typeof(TimeOnly));
+
+ // Act
+ var parser = new ExpressionParser(new[] { parameter }, $"TimeOnly({arguments})", new object[] { }, ParsingConfig.Default);
+ var expression = parser.Parse(typeof(TimeOnly));
+
+ // Assert
+ expression.ToString().Should().NotBeEmpty();
+ }
+
+ [Theory]
+ [InlineData(null)]
+ [InlineData("\"abc\"")]
+ public void ParseTypeAccess_Via_Constructor_Any_To_TimeOnly_Invalid(object any)
+ {
+ // Arrange
+ var parameter = Expression.Parameter(typeof(TimeOnly));
+
+ // Act
+ var parser = new ExpressionParser(new[] { parameter }, $"TimeOnly({any})", new object[] { }, ParsingConfig.Default);
+ Action a = () => parser.Parse(typeof(TimeOnly));
+
+ // Assert
+ a.Should().Throw();
+ }
#endif
- [Fact]
- public void ParseTypeAccess_Via_Constructor_String_To_Uri()
- {
- // Arrange
- string selector = "Uri(\"https://www.example.com/\")";
- var parameter = Expression.Parameter(typeof(Uri));
-
- // Act
- var parser = new ExpressionParser(new[] { parameter }, selector, new object[] { }, ParsingConfig.Default);
- var expression = parser.Parse(typeof(Uri));
-
- // Assert
- expression.ToString().Should().Be("https://www.example.com/");
- }
-
- [Fact]
- public void ParseTypeAccess_Via_Constructor_String_And_UriKind_To_Uri()
- {
- // Arrange
- string selector = "Uri(\"https://www.example.com/\", UriKind.Absolute)";
- var parameter = Expression.Parameter(typeof(Uri));
-
- // Act
- var parser = new ExpressionParser(new[] { parameter }, selector, new object[] { }, ParsingConfig.Default);
- var expression = parser.Parse(typeof(Uri));
-
- // Assert
- expression.ToString().Should().Be("new Uri(\"https://www.example.com/\", Absolute)");
- }
-
- [Theory]
- [InlineData("new(1 as a, 2 as b)", "new*(a = 1, b = 2)")]
- [InlineData("new(2 as b, 1 as a)", "new*(a = 1, b = 2)")]
- public void ParseTypeAccess_Via_Constructor_DynamicType_To_String(string newExpression, string newExpression2)
- {
- // Arrange
- var parameter = Expression.Parameter(typeof(int));
- var parameter2 = Expression.Parameter(typeof(int));
- var returnType = DynamicClassFactory.CreateType(new List {
- new DynamicProperty("a", typeof(int)),
- new DynamicProperty("b", typeof(int))
- });
-
- // Act
- var parser = new ExpressionParser(new[] { parameter, parameter2 }, newExpression, new object[] { }, ParsingConfig.Default);
-
- var expression = parser.Parse(returnType);
- // Assert
- expression.ToString().Should().Match(newExpression2);
- }
+ [Fact]
+ public void ParseTypeAccess_Via_Constructor_String_To_Uri()
+ {
+ // Arrange
+ string selector = "Uri(\"https://www.example.com/\")";
+ var parameter = Expression.Parameter(typeof(Uri));
+
+ // Act
+ var parser = new ExpressionParser(new[] { parameter }, selector, new object[] { }, ParsingConfig.Default);
+ var expression = parser.Parse(typeof(Uri));
+
+ // Assert
+ expression.ToString().Should().Be("https://www.example.com/");
+ }
+
+ [Fact]
+ public void ParseTypeAccess_Via_Constructor_String_And_UriKind_To_Uri()
+ {
+ // Arrange
+ string selector = "Uri(\"https://www.example.com/\", UriKind.Absolute)";
+ var parameter = Expression.Parameter(typeof(Uri));
+
+ // Act
+ var parser = new ExpressionParser(new[] { parameter }, selector, new object[] { }, ParsingConfig.Default);
+ var expression = parser.Parse(typeof(Uri));
+
+ // Assert
+ expression.ToString().Should().Be("new Uri(\"https://www.example.com/\", Absolute)");
+ }
+
+ [Theory]
+ [InlineData("new(1 as a, 2 as b)", "new*(a = 1, b = 2)")]
+ [InlineData("new(2 as b, 1 as a)", "new*(a = 1, b = 2)")]
+ public void ParseTypeAccess_Via_Constructor_DynamicType_To_String(string newExpression, string newExpression2)
+ {
+ // Arrange
+ var parameter = Expression.Parameter(typeof(int));
+ var parameter2 = Expression.Parameter(typeof(int));
+ var returnType = DynamicClassFactory.CreateType(new List {
+ new DynamicProperty("a", typeof(int)),
+ new DynamicProperty("b", typeof(int))
+ });
+
+ // Act
+ var parser = new ExpressionParser(new[] { parameter, parameter2 }, newExpression, new object[] { }, ParsingConfig.Default);
+
+ var expression = parser.Parse(returnType);
+ // Assert
+ expression.ToString().Should().Match(newExpression2);
}
-}
+}
\ No newline at end of file
diff --git a/test/System.Linq.Dynamic.Core.Tests/Parser/ExpressionParserTests.cs b/test/System.Linq.Dynamic.Core.Tests/Parser/ExpressionParserTests.cs
index b32c151c..d246327a 100644
--- a/test/System.Linq.Dynamic.Core.Tests/Parser/ExpressionParserTests.cs
+++ b/test/System.Linq.Dynamic.Core.Tests/Parser/ExpressionParserTests.cs
@@ -172,9 +172,9 @@ public void Parse_CastStringIntShouldReturnConstantExpression(string expression,
[Theory]
#if NET452
- [InlineData("int?(5)", typeof(int?), "Convert(5)")]
- [InlineData("int?(null)", typeof(int?), "Convert(null)")]
- [InlineData("string(null)", typeof(string), "Convert(null)")]
+ [InlineData("int?(5)", typeof(int?), "Convert(5)")]
+ [InlineData("int?(null)", typeof(int?), "Convert(null)")]
+ [InlineData("string(null)", typeof(string), "Convert(null)")]
#else
[InlineData("int?(5)", typeof(int?), "Convert(5, Nullable`1)")]
[InlineData("int?(null)", typeof(int?), "Convert(null, Nullable`1)")]
@@ -199,15 +199,14 @@ public void Parse_NullableShouldReturnNullable(string expression, object resultT
[InlineData("@MainCompany.Companies.Count() > 0", "(company.MainCompany.Companies.Count() > 0)")]
[InlineData("Company.Equals(null, null)", "Equals(null, null)")]
[InlineData("MainCompany.Name", "company.MainCompany.Name")]
- [InlineData("Company.Name", "No property or field 'Name' exists in type 'Company'")]
+ [InlineData("Name", "company.Name")]
[InlineData("DateTime", "company.DateTime")]
public void Parse_When_PrioritizePropertyOrFieldOverTheType_IsTrue(string expression, string result)
{
// Arrange
var config = new ParsingConfig
{
- CustomTypeProvider = _dynamicTypeProviderMock.Object,
- PrioritizePropertyOrFieldOverTheType = true
+ CustomTypeProvider = _dynamicTypeProviderMock.Object
};
ParameterExpression[] parameters = { ParameterExpressionHelper.CreateParameterExpression(typeof(Company), "company") };
var sut = new ExpressionParser(parameters, expression, null, config);
@@ -230,20 +229,25 @@ public void Parse_When_PrioritizePropertyOrFieldOverTheType_IsTrue(string expres
[Theory]
[InlineData("it.MainCompany.Name != null", "(company.MainCompany.Name != null)")]
[InlineData("@MainCompany.Companies.Count() > 0", "(company.MainCompany.Companies.Count() > 0)")]
- [InlineData("Company.Equals(null, null)", "Equals(null, null)")]
- [InlineData("MainCompany.Name", "Static property requires null instance, non-static property requires non-null instance.")] // Exception
+ [InlineData("Company.Equals(null, null)", "No applicable method 'Equals' exists in type 'Company'")] // Exception
+ [InlineData("MainCompany.Name", "company.MainCompany.Name")]
+ [InlineData("Name", "company.Name")]
+ [InlineData("it.DateTime", "company.DateTime")]
[InlineData("DateTime", "'.' or '(' or string literal expected")] // Exception
- [InlineData("Company.Name", "Static property requires null instance, non-static property requires non-null instance.")] // Exception
public void Parse_When_PrioritizePropertyOrFieldOverTheType_IsFalse(string expression, string result)
{
// Arrange
+ var config = new ParsingConfig
+ {
+ PrioritizePropertyOrFieldOverTheType = false
+ };
ParameterExpression[] parameters = { ParameterExpressionHelper.CreateParameterExpression(typeof(Company), "company") };
// Act
string parsedExpression;
try
{
- var sut = new ExpressionParser(parameters, expression, null, _parsingConfig);
+ var sut = new ExpressionParser(parameters, expression, null, config);
parsedExpression = sut.Parse(null).ToString();
}
catch (Exception e)
diff --git a/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Where.cs b/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Where.cs
index 5a340319..0e48885f 100644
--- a/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Where.cs
+++ b/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Where.cs
@@ -281,6 +281,10 @@ public void Where_Dynamic_ExpandoObject_As_AnonymousType()
public void Where_Dynamic_DateTimeConstructor_Issue662()
{
// Arrange
+ var config = new ParsingConfig
+ {
+ PrioritizePropertyOrFieldOverTheType = false
+ };
var date = new DateTime(2023, 1, 13, 12, 0, 0);
var queryable = new List
{
@@ -289,13 +293,13 @@ public void Where_Dynamic_DateTimeConstructor_Issue662()
}.AsQueryable();
// Act 1
- //var result1 = queryable.Where("DT > DateTime(2022, 1, 1, 0, 0, 0)").ToArray();
+ var result1 = queryable.Where(config, "DT > DateTime(2022, 1, 1, 0, 0, 0)").ToArray();
// Assert 1
- //result1.Should().HaveCount(1);
+ result1.Should().HaveCount(1);
// Act 2
- var result2 = queryable.Where("it.DateTime > DateTime(2022, 1, 1, 0, 0, 0)").ToArray();
+ var result2 = queryable.Where(config, "it.DateTime > DateTime(2022, 1, 1, 0, 0, 0)").ToArray();
// Assert 2
result2.Should().HaveCount(1);