diff --git a/src/System.Linq.Dynamic.Core/CustomTypeProviders/DefaultDynamicLinqCustomTypeProvider.cs b/src/System.Linq.Dynamic.Core/CustomTypeProviders/DefaultDynamicLinqCustomTypeProvider.cs
index 78c8fea3..b312e729 100644
--- a/src/System.Linq.Dynamic.Core/CustomTypeProviders/DefaultDynamicLinqCustomTypeProvider.cs
+++ b/src/System.Linq.Dynamic.Core/CustomTypeProviders/DefaultDynamicLinqCustomTypeProvider.cs
@@ -31,7 +31,7 @@ public DefaultDynamicLinqCustomTypeProvider(bool cacheCustomTypes = true)
_cacheCustomTypes = cacheCustomTypes;
}
- ///
+ ///
public virtual HashSet GetCustomTypes()
{
if (_cacheCustomTypes)
@@ -47,7 +47,7 @@ public virtual HashSet GetCustomTypes()
return GetCustomTypesInternal();
}
- ///
+ ///
public Dictionary> GetExtensionMethods()
{
if (_cacheCustomTypes)
@@ -63,7 +63,7 @@ public Dictionary> GetExtensionMethods()
return GetExtensionMethodsInternal();
}
- ///
+ ///
public Type ResolveType(string typeName)
{
Check.NotEmpty(typeName, nameof(typeName));
@@ -72,7 +72,7 @@ public Type ResolveType(string typeName)
return ResolveType(assemblies, typeName);
}
- ///
+ ///
public Type ResolveTypeBySimpleName(string simpleTypeName)
{
Check.NotEmpty(simpleTypeName, nameof(simpleTypeName));
@@ -91,7 +91,7 @@ private Dictionary> GetExtensionMethodsInternal()
{
var types = GetCustomTypes();
- List> list= new List>();
+ List> list = new List>();
foreach (var type in types)
{
diff --git a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs
index d66abc0b..d6d6751f 100644
--- a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs
+++ b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs
@@ -1045,6 +1045,42 @@ Expression ParseIdentifier()
return expr;
}
+ //// This could be enum like "MyEnum.Value1"
+ //if (_textParser.CurrentToken.Id == TokenId.Identifier)
+ //{
+ // var parts = new List { _textParser.CurrentToken.Text };
+
+ // _textParser.NextToken();
+ // _textParser.ValidateToken(TokenId.Dot, Res.DotExpected);
+ // while (_textParser.CurrentToken.Id == TokenId.Dot || _textParser.CurrentToken.Id == TokenId.Plus)
+ // {
+ // parts.Add(_textParser.CurrentToken.Text);
+
+ // _textParser.NextToken();
+ // _textParser.ValidateToken(TokenId.Identifier, Res.IdentifierExpected);
+
+ // parts.Add(_textParser.CurrentToken.Text);
+
+ // _textParser.NextToken();
+ // }
+
+ // var enumTypeAsString = string.Join("", parts.Take(parts.Count - 2).ToArray());
+ // var enumType = _typeFinder.FindTypeByName(enumTypeAsString, null, true);
+ // if (enumType == null)
+ // {
+ // throw ParseError(_textParser.CurrentToken.Pos, Res.EnumTypeNotFound, enumTypeAsString);
+ // }
+
+ // string enumValue = parts.Last();
+ // var @enum = TypeHelper.ParseEnum(enumValue, enumType);
+ // if (@enum == null)
+ // {
+ // throw ParseError(_textParser.CurrentToken.Pos, Res.EnumValueNotDefined, enumValue, enumTypeAsString);
+ // }
+
+ // return Expression.Constant(@enum);
+ //}
+
if (_it != null)
{
return ParseMemberAccess(null, _it);
@@ -1682,10 +1718,9 @@ Expression ParseMemberAccess(Type type, Expression expression)
}
}
- if (type.GetTypeInfo().IsEnum)
+ var @enum = TypeHelper.ParseEnum(id, type);
+ if (@enum != null)
{
- var @enum = Enum.Parse(type, id, true);
-
return Expression.Constant(@enum);
}
@@ -1722,33 +1757,85 @@ Expression ParseMemberAccess(Type type, Expression expression)
return _expressionHelper.ConvertToExpandoObjectAndCreateDynamicExpression(expression, type, id);
}
#endif
-
+ // Parse as Lambda
if (_textParser.CurrentToken.Id == TokenId.Lambda && _it.Type == type)
{
- // This might be an internal variable for use within a lambda expression, so store it as such
- _internals.Add(id, _it);
- string _previousItName = ItName;
+ return ParseAsLambda(id);
+ }
+
+ // This could be enum like "A.B.C.MyEnum.Value1" or "A.B.C+MyEnum.Value1"
+ if (_textParser.CurrentToken.Id == TokenId.Dot || _textParser.CurrentToken.Id == TokenId.Plus)
+ {
+ return ParseAsEnum(id);
+ }
+
+ throw ParseError(errorPos, Res.UnknownPropertyOrField, id, TypeHelper.GetTypeName(type));
+ }
+
+ private Expression ParseAsLambda(string id)
+ {
+ // This might be an internal variable for use within a lambda expression, so store it as such
+ _internals.Add(id, _it);
+ string _previousItName = ItName;
+
+ // Also store ItName (only once)
+ if (string.Equals(ItName, KeywordsHelper.KEYWORD_IT))
+ {
+ ItName = id;
+ }
+
+ // next
+ _textParser.NextToken();
+
+ LastLambdaItName = ItName;
+ var exp = ParseConditionalOperator();
- // Also store ItName (only once)
- if (string.Equals(ItName, KeywordsHelper.KEYWORD_IT))
+ // Restore previous context and clear internals
+ _internals.Remove(id);
+ ItName = _previousItName;
+
+ return exp;
+ }
+
+ private Expression ParseAsEnum(string id)
+ {
+ var parts = new List { id };
+
+ while (_textParser.CurrentToken.Id == TokenId.Dot || _textParser.CurrentToken.Id == TokenId.Plus)
+ {
+ if (_textParser.CurrentToken.Id == TokenId.Dot || _textParser.CurrentToken.Id == TokenId.Plus)
{
- ItName = id;
+ parts.Add(_textParser.CurrentToken.Text);
+ _textParser.NextToken();
}
- // next
- _textParser.NextToken();
+ if (_textParser.CurrentToken.Id == TokenId.Identifier)
+ {
+ parts.Add(_textParser.CurrentToken.Text);
+ _textParser.NextToken();
+ }
+ }
- LastLambdaItName = ItName;
- var exp = ParseConditionalOperator();
+ var enumTypeAsString = string.Join("", parts.Take(parts.Count - 2).ToArray());
+ var enumType = _typeFinder.FindTypeByName(enumTypeAsString, null, true);
+ if (enumType == null)
+ {
+ throw ParseError(_textParser.CurrentToken.Pos, Res.EnumTypeNotFound, enumTypeAsString);
+ }
- // Restore previous context and clear internals
- _internals.Remove(id);
- ItName = _previousItName;
+ string enumValueAsString = parts.LastOrDefault();
+ if (enumValueAsString == null)
+ {
+ throw ParseError(_textParser.CurrentToken.Pos, Res.EnumValueExpected);
+ }
- return exp;
+ var enumValue = TypeHelper.ParseEnum(enumValueAsString, enumType);
+ if (enumValue == null)
+ {
+ throw ParseError(_textParser.CurrentToken.Pos, Res.EnumValueNotDefined, enumValueAsString, enumTypeAsString);
}
- throw ParseError(errorPos, Res.UnknownPropertyOrField, id, TypeHelper.GetTypeName(type));
+ return Expression.Constant(enumValue);
}
Expression ParseEnumerable(Expression instance, Type elementType, string methodName, int errorPos, Type type)
diff --git a/src/System.Linq.Dynamic.Core/Res.cs b/src/System.Linq.Dynamic.Core/Res.cs
index 63a66b91..d6b0e377 100644
--- a/src/System.Linq.Dynamic.Core/Res.cs
+++ b/src/System.Linq.Dynamic.Core/Res.cs
@@ -20,6 +20,9 @@ internal static class Res
public const string DotOrOpenParenOrStringLiteralExpected = "'.' or '(' or string literal expected";
public const string DynamicExpandoObjectIsNotSupported = "Dynamic / ExpandoObject is not supported in .NET 3.5, UAP and .NETStandard 1.3";
public const string DuplicateIdentifier = "The identifier '{0}' was defined more than once";
+ public const string EnumTypeNotFound = "Enum type '{0}' not found";
+ public const string EnumValueExpected = "Enum value expected";
+ public const string EnumValueNotDefined = "Enum value '{0}' is not defined in enum type '{1}'";
public const string ExpressionExpected = "Expression expected";
public const string ExpressionTypeMismatch = "Expression of type '{0}' expected";
public const string FirstExprMustBeBool = "The first expression must be of type 'Boolean'";
diff --git a/test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs b/test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs
index 9ffe41b0..ecbd1089 100644
--- a/test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs
+++ b/test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs
@@ -1,16 +1,26 @@
-using FluentAssertions;
-using Newtonsoft.Json.Linq;
-using NFluent;
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Dynamic;
using System.Globalization;
using System.Linq.Dynamic.Core.Exceptions;
using System.Linq.Dynamic.Core.Tests.Helpers;
using System.Linq.Dynamic.Core.Tests.Helpers.Models;
+using FluentAssertions;
+using Newtonsoft.Json.Linq;
+using NFluent;
using Xunit;
namespace System.Linq.Dynamic.Core.Tests
{
+ public enum TestEnumPublic : sbyte
+ {
+ Var1 = 0,
+ Var2 = 1,
+ Var3 = 2,
+ Var4 = 4,
+ Var5 = 8,
+ Var6 = 16
+ }
+
public class ExpressionTests
{
public enum TestEnum2 : sbyte
@@ -20,7 +30,7 @@ public enum TestEnum2 : sbyte
Var3 = 2,
Var4 = 4,
Var5 = 8,
- Var6 = 16,
+ Var6 = 16
}
public class TestEnumClass
@@ -31,6 +41,8 @@ public class TestEnumClass
public TestEnum2? C { get; set; }
+ public TestEnumPublic E { get; set; }
+
public int Id { get; set; }
}
@@ -677,7 +689,7 @@ public void ExpressionTests_Enum()
}
[Fact]
- public void ExpressionTests_Enum_Property()
+ public void ExpressionTests_Enum_Property_Equality_Using_Argument()
{
// Arrange
var qry = new List { new TestEnumClass { B = TestEnum2.Var2 } }.AsQueryable();
@@ -697,6 +709,129 @@ public void ExpressionTests_Enum_Property()
Check.That(resultEqualIntParamRight.Single()).Equals(TestEnum2.Var2);
}
+ [Fact]
+ public void ExpressionTests_Enum_Property_Equality_With_Integer_Inline()
+ {
+ // Arrange
+ var qry = new List { new TestEnumClass { E = TestEnumPublic.Var2 } }.AsQueryable();
+
+ // Act
+ var resultEqualIntParamLeft = qry.Where("1 == it.E").ToDynamicArray();
+ var resultEqualIntParamRight = qry.Where("it.E == 1").ToDynamicArray();
+
+ // Assert
+ Check.That(resultEqualIntParamLeft.Single()).Equals(TestEnumPublic.Var2);
+ Check.That(resultEqualIntParamRight.Single()).Equals(TestEnumPublic.Var2);
+ }
+
+ [Fact]
+ public void ExpressionTests_Enum_Property_Equality_Using_PublicEnum_And_FullName_Inline()
+ {
+ // Arrange
+ var qry = new List { new TestEnumClass { E = TestEnumPublic.Var2 } }.AsQueryable();
+ string enumType = typeof(TestEnumPublic).FullName;
+
+ // Act
+ var resultEqualEnumParamLeft = qry.Where($"{enumType}.Var2 == it.E").ToDynamicArray();
+ var resultEqualEnumParamRight = qry.Where($"it.E == {enumType}.Var2").ToDynamicArray();
+
+ // Assert
+ Check.That(resultEqualEnumParamLeft.Single()).Equals(TestEnumPublic.Var2);
+ Check.That(resultEqualEnumParamRight.Single()).Equals(TestEnumPublic.Var2);
+ }
+
+ [Fact]
+ public void ExpressionTests_Enum_Property_Equality_Using_Enum_And_FullName_Inline()
+ {
+ // Arrange
+ var qry = new List { new TestEnumClass { B = TestEnum2.Var2 } }.AsQueryable();
+ string enumType = typeof(TestEnum2).FullName;
+
+ // Act
+ var resultEqualEnumParamLeft = qry.Where($"{enumType}.Var2 == it.B").ToDynamicArray();
+ var resultEqualEnumParamRight = qry.Where($"it.B == {enumType}.Var2").ToDynamicArray();
+
+ // Assert
+ Check.That(resultEqualEnumParamLeft.Single()).Equals(TestEnum2.Var2);
+ Check.That(resultEqualEnumParamRight.Single()).Equals(TestEnum2.Var2);
+ }
+
+ [Fact]
+ public void ExpressionTests_Enum_Property_Equality_Using_PublicEnum_Name_Inline()
+ {
+ // Arrange
+ var qry = new List { new TestEnumClass { E = TestEnumPublic.Var2 } }.AsQueryable();
+ string enumType = typeof(TestEnumPublic).FullName;
+
+ // Act
+ var resultEqualEnumParamLeft = qry.Where($"{enumType}.Var2 == it.E").ToDynamicArray();
+ var resultEqualEnumParamRight = qry.Where($"it.E == {enumType}.Var2").ToDynamicArray();
+
+ // Assert
+ Check.That(resultEqualEnumParamLeft.Single()).Equals(TestEnumPublic.Var2);
+ Check.That(resultEqualEnumParamRight.Single()).Equals(TestEnumPublic.Var2);
+ }
+
+ [Fact]
+ public void ExpressionTests_Enum_Property_Equality_Using_Enum_Name_Inline_Should_Throw_Exception()
+ {
+ // Arrange
+ var config = new ParsingConfig
+ {
+ ResolveTypesBySimpleName = true
+ };
+ var qry = new List { new TestEnumClass { B = TestEnum2.Var2 } }.AsQueryable();
+ string enumType = typeof(TestEnum2).Name;
+
+ // Act
+ Action a = () => qry.Where(config, $"{enumType}.Var2 == it.B").ToDynamicArray();
+
+ // Assert
+ a.Should().Throw();
+ }
+
+ [Fact]
+ public void ExpressionTests_Enum_Property_Equality_Using_PublicEnum_Invalid_Inline_Should_Throw_ParseException()
+ {
+ // Arrange
+ var qry = new List { new TestEnumClass { E = TestEnumPublic.Var2 } }.AsQueryable();
+ string enumType = "a.b.c";
+
+ // Act
+ Action a = () => qry.Where($"{enumType}.Var2 == it.E").ToDynamicArray();
+
+ // Assert
+ a.Should().Throw().WithMessage("Enum type 'b.c' not found");
+ }
+
+ [Fact]
+ public void ExpressionTests_Enum_Property_Equality_Using_PublicEnum_InvalidValue_Inline_Should_Throw_ParseException()
+ {
+ // Arrange
+ var qry = new List { new TestEnumClass { E = TestEnumPublic.Var2 } }.AsQueryable();
+ string enumType = typeof(TestEnumPublic).FullName;
+
+ // Act
+ Action a = () => qry.Where($"{enumType}.VarInvalid == it.E").ToDynamicArray();
+
+ // Assert
+ a.Should().Throw().WithMessage("Enum value 'VarInvalid' is not defined in enum type 'System.Linq.Dynamic.Core.Tests.TestEnumPublic'");
+ }
+
+ [Fact]
+ public void ExpressionTests_Enum_Property_Equality_Using_PublicEnum_MissingValue_Inline_Should_Throw_ParseException()
+ {
+ // Arrange
+ var qry = new List { new TestEnumClass { E = TestEnumPublic.Var2 } }.AsQueryable();
+ string enumType = typeof(TestEnumPublic).FullName;
+
+ // Act
+ Action a = () => qry.Where($"{enumType}. == it.E").ToDynamicArray();
+
+ // Assert
+ a.Should().Throw().WithMessage("Enum type 'System.Linq.Dynamic.Core.Tests.' not found");
+ }
+
[Fact]
public void ExpressionTests_Enum_NullableProperty()
{