Skip to content

Support string to enum conversion using in expression with substitution value #942

@stijnherreman

Description

@stijnherreman

When using an in expression without substitution values, e.g. qry.Where("it.TestEnum in (\"Var1\", \"Var2\")"), the parser checks whether the left side is an Enum and then converts the string values.
When using in @0 syntax, e.g. qry.Where("it.TestEnum in @0", new[] { "Var1", "Var2" }), there is no conversion and an exception is thrown.

[Fact]
public void ExpressionTests_In_Enum()
{
    // Arrange
    var model1 = new ModelWithEnum { TestEnum = TestEnum.Var1 };
    var model2 = new ModelWithEnum { TestEnum = TestEnum.Var2 };
    var model3 = new ModelWithEnum { TestEnum = TestEnum.Var3 };
    var qry = new[] { model1, model2, model3 }.AsQueryable();

    // Act
    var expected = qry.Where(x => new[] { TestEnum.Var1, TestEnum.Var2 }.Contains(x.TestEnum)).ToArray();
    var result1 = qry.Where("it.TestEnum in (\"Var1\", \"Var2\")").ToArray();
    var result2 = qry.Where("it.TestEnum in (0, 1)").ToArray();
+   var result3 = qry.Where("it.TestEnum in @0", new[] { TestEnum.Var1, TestEnum.Var2 }); // works
+   var result4 = qry.Where("it.TestEnum in @0", new[] { "Var1", "Var2" }); // fails

    // Assert
    Check.That(result1).ContainsExactly(expected);
    Check.That(result2).ContainsExactly(expected);
}

I'm not sure if the existing logic can be easily extended. Copying existing conversion logic breaks result3 and there is also a check on implementing IEnumerable that would clash.

// if the identifier is an Enum (or nullable Enum), try to convert the right-side also to an Enum.
if (TypeHelper.GetNonNullableType(left.Type).GetTypeInfo().IsEnum)
{
if (right is ConstantExpression constantExprRight)
{
right = ParseEnumToConstantExpression(token.Pos, left.Type, constantExprRight);
}
else if (_expressionHelper.TryUnwrapAsConstantExpression(right, out var unwrappedConstantExprRight))
{
right = ParseEnumToConstantExpression(token.Pos, left.Type, unwrappedConstantExprRight);
}
}

else if (_textParser.CurrentToken.Id == TokenId.Identifier) // a single argument
{
Expression right = ParsePrimary();
if (!typeof(IEnumerable).IsAssignableFrom(right.Type))
{
throw ParseError(_textParser.CurrentToken.Pos, Res.IdentifierImplementingInterfaceExpected, typeof(IEnumerable));
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions