Skip to content

Commit

Permalink
Merge pull request #399 from ascott18/UseParameterizedNamesInDynamicQ…
Browse files Browse the repository at this point in the history
…uery

Use parameterized names in dynamic query
  • Loading branch information
JonathanMagnan committed Aug 7, 2020
2 parents 2821de8 + 069311c commit 02910bd
Show file tree
Hide file tree
Showing 7 changed files with 210 additions and 24 deletions.
80 changes: 80 additions & 0 deletions src/System.Linq.Dynamic.Core/Parser/ConstantExpressionWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,26 @@ public void Wrap(ref Expression expression)
{
expression = WrappedConstant((bool)constantExpression.Value);
}
else if (constantExpression.Type == typeof(bool?))
{
expression = WrappedConstant((bool?)constantExpression.Value);
}
else if (constantExpression.Type == typeof(char))
{
expression = WrappedConstant((char)constantExpression.Value);
}
else if (constantExpression.Type == typeof(char?))
{
expression = WrappedConstant((char?)constantExpression.Value);
}
else if (constantExpression.Type == typeof(byte))
{
expression = WrappedConstant((byte)constantExpression.Value);
}
else if (constantExpression.Type == typeof(byte?))
{
expression = WrappedConstant((byte?)constantExpression.Value);
}
else if (constantExpression.Type == typeof(sbyte))
{
expression = WrappedConstant((sbyte)constantExpression.Value);
Expand All @@ -36,54 +48,106 @@ public void Wrap(ref Expression expression)
{
expression = WrappedConstant((float)constantExpression.Value);
}
else if (constantExpression.Type == typeof(float?))
{
expression = WrappedConstant((float?)constantExpression.Value);
}
else if (constantExpression.Type == typeof(decimal))
{
expression = WrappedConstant((decimal)constantExpression.Value);
}
else if (constantExpression.Type == typeof(decimal?))
{
expression = WrappedConstant((decimal?)constantExpression.Value);
}
else if (constantExpression.Type == typeof(double))
{
expression = WrappedConstant((double)constantExpression.Value);
}
else if (constantExpression.Type == typeof(double?))
{
expression = WrappedConstant((double?)constantExpression.Value);
}
else if (constantExpression.Type == typeof(long))
{
expression = WrappedConstant((long)constantExpression.Value);
}
else if (constantExpression.Type == typeof(long?))
{
expression = WrappedConstant((long?)constantExpression.Value);
}
else if (constantExpression.Type == typeof(ulong))
{
expression = WrappedConstant((ulong)constantExpression.Value);
}
else if (constantExpression.Type == typeof(ulong?))
{
expression = WrappedConstant((ulong?)constantExpression.Value);
}
else if (constantExpression.Type == typeof(int))
{
expression = WrappedConstant((int)constantExpression.Value);
}
else if (constantExpression.Type == typeof(int?))
{
expression = WrappedConstant((int?)constantExpression.Value);
}
else if (constantExpression.Type == typeof(uint))
{
expression = WrappedConstant((uint)constantExpression.Value);
}
else if (constantExpression.Type == typeof(uint?))
{
expression = WrappedConstant((uint?)constantExpression.Value);
}
else if (constantExpression.Type == typeof(short))
{
expression = WrappedConstant((short)constantExpression.Value);
}
else if (constantExpression.Type == typeof(short?))
{
expression = WrappedConstant((short?)constantExpression.Value);
}
else if (constantExpression.Type == typeof(ushort))
{
expression = WrappedConstant((ushort)constantExpression.Value);
}
else if (constantExpression.Type == typeof(ushort?))
{
expression = WrappedConstant((ushort?)constantExpression.Value);
}
else if (constantExpression.Type == typeof(Guid))
{
expression = WrappedConstant((Guid)constantExpression.Value);
}
else if (constantExpression.Type == typeof(Guid?))
{
expression = WrappedConstant((Guid?)constantExpression.Value);
}
else if (constantExpression.Type == typeof(DateTime))
{
expression = WrappedConstant((DateTime)constantExpression.Value);
}
else if (constantExpression.Type == typeof(DateTime?))
{
expression = WrappedConstant((DateTime?)constantExpression.Value);
}
else if (constantExpression.Type == typeof(DateTimeOffset))
{
expression = WrappedConstant((DateTimeOffset)constantExpression.Value);
}
else if (constantExpression.Type == typeof(DateTimeOffset?))
{
expression = WrappedConstant((DateTimeOffset?)constantExpression.Value);
}
else if (constantExpression.Type == typeof(TimeSpan))
{
expression = WrappedConstant((TimeSpan)constantExpression.Value);
}
else if (constantExpression.Type == typeof(TimeSpan?))
{
expression = WrappedConstant((TimeSpan?)constantExpression.Value);
}

return;
}
Expand All @@ -94,18 +158,34 @@ public void Wrap(ref Expression expression)
{
expression = WrappedConstant(Expression.Lambda<Func<Guid>>(newExpression).Compile()());
}
else if (newExpression.Type == typeof(Guid?))
{
expression = WrappedConstant(Expression.Lambda<Func<Guid?>>(newExpression).Compile()());
}
else if (newExpression.Type == typeof(DateTime))
{
expression = WrappedConstant(Expression.Lambda<Func<DateTime>>(newExpression).Compile()());
}
else if (newExpression.Type == typeof(DateTime?))
{
expression = WrappedConstant(Expression.Lambda<Func<DateTime?>>(newExpression).Compile()());
}
else if (newExpression.Type == typeof(DateTimeOffset))
{
expression = WrappedConstant(Expression.Lambda<Func<DateTimeOffset>>(newExpression).Compile()());
}
else if (newExpression.Type == typeof(DateTimeOffset?))
{
expression = WrappedConstant(Expression.Lambda<Func<DateTimeOffset?>>(newExpression).Compile()());
}
else if (newExpression.Type == typeof(TimeSpan))
{
expression = WrappedConstant(Expression.Lambda<Func<TimeSpan>>(newExpression).Compile()());
}
else if (newExpression.Type == typeof(TimeSpan?))
{
expression = WrappedConstant(Expression.Lambda<Func<TimeSpan?>>(newExpression).Compile()());
}
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,8 @@ Expression ParseUnary()
Expression ParsePrimary()
{
Expression expr = ParsePrimaryStart();
_expressionHelper.WrapConstantExpression(ref expr);

while (true)
{
if (_textParser.CurrentToken.Id == TokenId.Dot)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,9 @@
<Compile Include="..\System.Linq.Dynamic.Core.Tests\Helpers\TestEnum.cs">
<Link>Helpers\TestEnum.cs</Link>
</Compile>
<Compile Include="..\System.Linq.Dynamic.Core.Tests\TestHelpers\ExpressionString.cs">
<Link>TestHelpers\ExpressionString.cs</Link>
</Compile>
<Compile Include="..\System.Linq.Dynamic.Core.Tests\OperatorTests.cs">
<Link>OperatorTests.cs</Link>
</Compile>
Expand Down Expand Up @@ -380,4 +383,4 @@
<Error Condition="!Exists('..\..\packages\xunit.runner.visualstudio.2.4.1\build\net20\xunit.runner.visualstudio.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\xunit.runner.visualstudio.2.4.1\build\net20\xunit.runner.visualstudio.props'))" />
</Target>
<Import Project="..\..\packages\xunit.core.2.4.1\build\xunit.core.targets" Condition="Exists('..\..\packages\xunit.core.2.4.1\build\xunit.core.targets')" />
</Project>
</Project>
78 changes: 56 additions & 22 deletions test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using FluentAssertions;
using Xunit;
using User = System.Linq.Dynamic.Core.Tests.Helpers.Models.User;
using System.Linq.Dynamic.Core.Tests.TestHelpers;

namespace System.Linq.Dynamic.Core.Tests
{
Expand Down Expand Up @@ -251,6 +252,25 @@ public Type ResolveTypeBySimpleName(string typeName)
}
}

[Fact]
public void DynamicExpressionParser_ParseLambda_UseParameterizedNamesInDynamicQuery_false()
{
// Assign
var config = new ParsingConfig
{
UseParameterizedNamesInDynamicQuery = false
};

// Act
var expression = DynamicExpressionParser.ParseLambda<string, bool>(config, true, "s => s == \"x\"");

// Assert
dynamic constantExpression = (ConstantExpression)(expression.Body as BinaryExpression).Right;
string value = constantExpression.Value;

Check.That(value).IsEqualTo("x");
}

[Fact]
public void DynamicExpressionParser_ParseLambda_UseParameterizedNamesInDynamicQuery_true()
{
Expand All @@ -261,16 +281,49 @@ public void DynamicExpressionParser_ParseLambda_UseParameterizedNamesInDynamicQu
};

// Act
var expression = DynamicExpressionParser.ParseLambda<string, bool>(config, true, "s => s == \"x\"");
var expression = DynamicExpressionParser.ParseLambda<Person, bool>(config, false, "Id = 42");
string expressionAsString = expression.ToString();

// Assert
Check.That(expressionAsString).IsEqualTo("Param_0 => (Param_0.Id == value(System.Linq.Dynamic.Core.Parser.WrappedValue`1[System.Int32]).Value)");

dynamic constantExpression = ((MemberExpression)(expression.Body as BinaryExpression).Right).Expression as ConstantExpression;
dynamic wrappedObj = constantExpression.Value;

var propertyInfo = wrappedObj.GetType().GetProperty("Value", BindingFlags.Instance | BindingFlags.Public);
string value = propertyInfo.GetValue(wrappedObj) as string;
int value = (int) propertyInfo.GetValue(wrappedObj);

Check.That(value).IsEqualTo("x");
Check.That(value).IsEqualTo(42);
}

[Theory]
[InlineData("NullableIntValue", "42")]
[InlineData("NullableDoubleValue", "42.23")]
public void DynamicExpressionParser_ParseLambda_UseParameterizedNamesInDynamicQuery_ForNullableProperty_true(string propName, string valueString)
{
// Assign
var config = new ParsingConfig
{
UseParameterizedNamesInDynamicQuery = true
};

// Act
var expression = DynamicExpressionParser.ParseLambda<SimpleValuesModel, bool>(config, false, $"{propName} = {valueString}");
string expressionAsString = expression.ToString();

// Assert
var queriedProp = typeof(SimpleValuesModel).GetProperty(propName, BindingFlags.Instance | BindingFlags.Public);
var queriedPropType = queriedProp.PropertyType;
var queriedPropUnderlyingType = Nullable.GetUnderlyingType(queriedPropType);

Check.That(expressionAsString).IsEqualTo($"Param_0 => (Param_0.{propName} == {ExpressionString.NullableConversion($"value(System.Linq.Dynamic.Core.Parser.WrappedValue`1[{queriedPropUnderlyingType}]).Value")})");
dynamic constantExpression = ((MemberExpression)(((expression.Body as BinaryExpression).Right as UnaryExpression).Operand)).Expression as ConstantExpression;
object wrapperObj = constantExpression.Value;

var propertyInfo = wrapperObj.GetType().GetProperty("Value", BindingFlags.Instance | BindingFlags.Public);
object value = propertyInfo.GetValue(wrapperObj);

Check.That(value).IsEqualTo(Convert.ChangeType(valueString, Nullable.GetUnderlyingType(queriedPropType) ?? queriedPropType));
}

[Theory]
Expand Down Expand Up @@ -298,25 +351,6 @@ public void DynamicExpressionParser_ParseLambda_WithStructWithEquality(string qu
Check.That(result.ToArray()[0]).Equals(expected[0]);
}

[Fact]
public void DynamicExpressionParser_ParseLambda_UseParameterizedNamesInDynamicQuery_false()
{
// Assign
var config = new ParsingConfig
{
UseParameterizedNamesInDynamicQuery = false
};

// Act
var expression = DynamicExpressionParser.ParseLambda<string, bool>(config, true, "s => s == \"x\"");

// Assert
dynamic constantExpression = (ConstantExpression)(expression.Body as BinaryExpression).Right;
string value = constantExpression.Value;

Check.That(value).IsEqualTo("x");
}

[Fact]
public void DynamicExpressionParser_ParseLambda_ToList()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,9 @@ public class SimpleValuesModel
public decimal DecimalValue { get; set; }

public double DoubleValue { get; set; }

public int? NullableIntValue { get; set; }

public double? NullableDoubleValue { get; set; }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,27 @@ public void ExpressionHelper_WrapConstantExpression_false()
Check.That(expression.ToString()).Equals("\"test\"");
}

[Fact]
public void ExpressionHelper_WrapNullableConstantExpression_false()
{
// Assign
var config = new ParsingConfig
{
UseParameterizedNamesInDynamicQuery = false
};
var expressionHelper = new ExpressionHelper(config);

int? value = 42;
Expression expression = Expression.Constant(value);

// Act
expressionHelper.WrapConstantExpression(ref expression);

// Assert
Check.That(expression).IsInstanceOf<ConstantExpression>();
Check.That(expression.ToString()).Equals("42");
}

[Fact]
public void ExpressionHelper_WrapConstantExpression_true()
{
Expand All @@ -57,6 +78,28 @@ public void ExpressionHelper_WrapConstantExpression_true()
Check.That(expression.ToString()).Equals("value(System.Linq.Dynamic.Core.Parser.WrappedValue`1[System.String]).Value");
}

[Fact]
public void ExpressionHelper_WrapNullableConstantExpression_true()
{
// Assign
var config = new ParsingConfig
{
UseParameterizedNamesInDynamicQuery = true
};
var expressionHelper = new ExpressionHelper(config);

int? value = 42;
Expression expression = Expression.Constant(value);

// Act
expressionHelper.WrapConstantExpression(ref expression);
expressionHelper.WrapConstantExpression(ref expression);

// Assert
Check.That(expression.GetType().FullName).Equals("System.Linq.Expressions.PropertyExpression");
Check.That(expression.ToString()).Equals("value(System.Linq.Dynamic.Core.Parser.WrappedValue`1[System.Int32]).Value");
}

[Fact]
public void ExpressionHelper_OptimizeStringForEqualityIfPossible_Guid()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace System.Linq.Dynamic.Core.Tests.TestHelpers
{
public static class ExpressionString
{
public static string NullableConversion(string convertedExpr)
{
#if NET452
return $"Convert({convertedExpr})";
#else
return $"Convert({convertedExpr}, Nullable`1)";
#endif
}
}
}

0 comments on commit 02910bd

Please sign in to comment.