Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/System.Linq.Dynamic.Core/Parser/ExpressionPromoter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ public ExpressionPromoter(ParsingConfig config)

if (TypeHelper.IsCompatibleWith(returnType, type))
{
if (type == typeof(decimal) && TypeHelper.IsEnumType(sourceExpression.Type))
if (TypeHelper.TypesAreEqual(type, typeof(decimal)) && TypeHelper.IsEnumType(sourceExpression.Type))
{
return Expression.Convert(Expression.Convert(sourceExpression, Enum.GetUnderlyingType(sourceExpression.Type)), type);
}
Expand Down
37 changes: 21 additions & 16 deletions src/System.Linq.Dynamic.Core/Parser/TypeHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,20 +83,20 @@ public static bool IsCompatibleWith(Type source, Type target)
return target.IsAssignableFrom(source);
}

Type st = GetNonNullableType(source);
Type tt = GetNonNullableType(target);
var sourceType = GetNonNullableType(source);
var targetType = GetNonNullableType(target);

if (st != source && tt == target)
if (sourceType != source && targetType == target)
{
return false;
}

TypeCode sc = st.GetTypeInfo().IsEnum ? TypeCode.Int64 : Type.GetTypeCode(st);
TypeCode tc = tt.GetTypeInfo().IsEnum ? TypeCode.Int64 : Type.GetTypeCode(tt);
switch (sc)
var sourceTypeCode = sourceType.GetTypeInfo().IsEnum ? TypeCode.Int32 : Type.GetTypeCode(sourceType);
var targetTypeCode = targetType.GetTypeInfo().IsEnum ? TypeCode.Int32 : Type.GetTypeCode(targetType);
switch (sourceTypeCode)
{
case TypeCode.SByte:
switch (tc)
switch (targetTypeCode)
{
case TypeCode.SByte:
case TypeCode.Int16:
Expand All @@ -110,7 +110,7 @@ public static bool IsCompatibleWith(Type source, Type target)
break;

case TypeCode.Byte:
switch (tc)
switch (targetTypeCode)
{
case TypeCode.Byte:
case TypeCode.Int16:
Expand All @@ -127,7 +127,7 @@ public static bool IsCompatibleWith(Type source, Type target)
break;

case TypeCode.Int16:
switch (tc)
switch (targetTypeCode)
{
case TypeCode.Int16:
case TypeCode.Int32:
Expand All @@ -140,7 +140,7 @@ public static bool IsCompatibleWith(Type source, Type target)
break;

case TypeCode.UInt16:
switch (tc)
switch (targetTypeCode)
{
case TypeCode.UInt16:
case TypeCode.Int32:
Expand All @@ -155,7 +155,7 @@ public static bool IsCompatibleWith(Type source, Type target)
break;

case TypeCode.Int32:
switch (tc)
switch (targetTypeCode)
{
case TypeCode.Int32:
case TypeCode.Int64:
Expand All @@ -167,7 +167,7 @@ public static bool IsCompatibleWith(Type source, Type target)
break;

case TypeCode.UInt32:
switch (tc)
switch (targetTypeCode)
{
case TypeCode.UInt32:
case TypeCode.Int64:
Expand All @@ -180,7 +180,7 @@ public static bool IsCompatibleWith(Type source, Type target)
break;

case TypeCode.Int64:
switch (tc)
switch (targetTypeCode)
{
case TypeCode.Int64:
case TypeCode.Single:
Expand All @@ -191,7 +191,7 @@ public static bool IsCompatibleWith(Type source, Type target)
break;

case TypeCode.UInt64:
switch (tc)
switch (targetTypeCode)
{
case TypeCode.UInt64:
case TypeCode.Single:
Expand All @@ -202,7 +202,7 @@ public static bool IsCompatibleWith(Type source, Type target)
break;

case TypeCode.Single:
switch (tc)
switch (targetTypeCode)
{
case TypeCode.Single:
case TypeCode.Double:
Expand All @@ -211,7 +211,7 @@ public static bool IsCompatibleWith(Type source, Type target)
break;

default:
if (st == tt)
if (sourceType == targetType)
{
return true;
}
Expand Down Expand Up @@ -471,6 +471,11 @@ public static Type GetUnderlyingType(Type type)
return type;
}

public static bool TypesAreEqual(Type type, Type typeToCheck)
{
return GetNullableType(type) == GetNullableType(typeToCheck);
}

public static IList<Type> GetSelfAndBaseTypes(Type type, bool excludeObject = false)
{
if (type.GetTypeInfo().IsInterface)
Expand Down
48 changes: 46 additions & 2 deletions test/System.Linq.Dynamic.Core.Tests/Parser/TypeHelperTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public void TypeHelper_IsCompatibleWith_SameTypes_True()
}

[Fact]
public void TypeHelper_IsCompatibleWith_True()
public void TypeHelper_IsCompatibleWith_Int_And_Long_Returns_True()
{
// Assign + Act
var result = TypeHelper.IsCompatibleWith(typeof(int), typeof(long));
Expand All @@ -54,8 +54,52 @@ public void TypeHelper_IsCompatibleWith_True()
Check.That(result).IsTrue();
}

[Theory]

// True (enum underlying Int32 compatible targets)
[InlineData(typeof(DayOfWeek), true)]
[InlineData(typeof(DayOfWeek?), true)]
[InlineData(typeof(int), true)]
[InlineData(typeof(int?), true)]
[InlineData(typeof(long), true)]
[InlineData(typeof(long?), true)]
[InlineData(typeof(float), true)]
[InlineData(typeof(float?), true)]
[InlineData(typeof(double), true)]
[InlineData(typeof(double?), true)]
[InlineData(typeof(decimal), true)]
[InlineData(typeof(decimal?), true)]
[InlineData(typeof(object), true)]

// False (not compatible with enum's Int32 widening rules or reference types)
[InlineData(typeof(char), false)]
[InlineData(typeof(char?), false)]
[InlineData(typeof(short), false)]
[InlineData(typeof(short?), false)]
[InlineData(typeof(byte), false)]
[InlineData(typeof(byte?), false)]
[InlineData(typeof(sbyte), false)]
[InlineData(typeof(sbyte?), false)]
[InlineData(typeof(ushort), false)]
[InlineData(typeof(ushort?), false)]
[InlineData(typeof(uint), false)]
[InlineData(typeof(uint?), false)]
[InlineData(typeof(ulong), false)]
[InlineData(typeof(ulong?), false)]
[InlineData(typeof(bool), false)]
[InlineData(typeof(bool?), false)]
[InlineData(typeof(string), false)]
public void TypeHelper_IsCompatibleWith_Enum(Type targetType, bool expected)
{
// Assign + Act
var result = TypeHelper.IsCompatibleWith(typeof(DayOfWeek), targetType);

// Assert
result.Should().Be(expected);
}

[Fact]
public void TypeHelper_IsCompatibleWith_False()
public void TypeHelper_IsCompatibleWith_Long_And_Int_Returns_False()
{
// Assign + Act
var result = TypeHelper.IsCompatibleWith(typeof(long), typeof(int));
Expand Down
26 changes: 26 additions & 0 deletions test/System.Linq.Dynamic.Core.Tests/QueryableTests.Select.cs
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,32 @@ public void Select_Dynamic_Add_Strings()
Assert.Equal(range.Select(x => x + "c").ToArray(), rangeResult.Cast<string>().ToArray());
}

[Fact]
public void Select_Dynamic_Add_DayOfWeekEnum_And_Integer()
{
// Arrange
var range = new DayOfWeek[] { DayOfWeek.Monday };

// Act
var rangeResult = range.AsQueryable().Select("it + 1");

// Assert
Assert.Equal(range.Select(x => x + 1).ToArray(), rangeResult.Cast<DayOfWeek>().ToArray());
}

[Fact]
public void Select_Dynamic_Add_Integer_And_DayOfWeekEnum()
{
// Arrange
var range = new int[] { 1 };

// Act
var rangeResult = range.AsQueryable().Select("it + DayOfWeek.Monday");

// Assert
Assert.Equal(range.Select(x => x + DayOfWeek.Monday).Cast<int>().ToArray(), rangeResult.Cast<int>().ToArray());
}

[Fact]
public void Select_Dynamic_WithIncludes()
{
Expand Down
Loading