Skip to content
Merged
9 changes: 7 additions & 2 deletions src/System.Linq.Dynamic.Core/Parser/ExpressionPromoter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,12 @@ public ExpressionPromoter(ParsingConfig config)
}
else
{
if (_constantExpressionHelper.TryGetText(ce, out var text))
if (!_constantExpressionHelper.TryGetText(ce, out var text))
{
text = ce.Value?.ToString();
}

if (text != null)
{
Type target = TypeHelper.GetNonNullableType(type);
object? value = null;
Expand All @@ -67,7 +72,7 @@ public ExpressionPromoter(ParsingConfig config)
// Make sure an enum value stays an enum value
if (target.IsEnum)
{
value = Enum.ToObject(target, value!);
TypeHelper.TryParseEnum(text, target, out value);
}
break;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
using Moq;
using FluentAssertions;
using Moq;
using System.Collections.Generic;
using System.Linq.Dynamic.Core.CustomTypeProviders;
using System.Linq.Dynamic.Core.Parser;
using System.Linq.Expressions;
using System.Threading.Tasks;
using System.Threading;
using Xunit;

namespace System.Linq.Dynamic.Core.Tests.Parser;
Expand Down Expand Up @@ -53,4 +56,49 @@ public void DynamicExpressionParser_ParseLambda_WithCustomExpressionPromoter()

_expressionPromoterMock.Verify(e => e.Promote(It.IsAny<ConstantExpression>(), typeof(Guid), true, true), Times.Once);
}

[Fact]
public async Task Promote_Should_Succeed_Even_When_LiteralsCache_Is_Cleaned()
{
// Arrange
var parsingConfig = new ParsingConfig()
{
ConstantExpressionCacheConfig = new Core.Util.Cache.CacheConfig
{
CleanupFrequency = TimeSpan.FromMilliseconds(500), // Run cleanup more often
TimeToLive = TimeSpan.FromMilliseconds(500), // Shorten TTL to force expiration
ReturnExpiredItems = false
}
};
var constantExpressionHelper = ConstantExpressionHelperFactory.GetInstance(parsingConfig);
var expressionPromoter = new ExpressionPromoter(parsingConfig);

double value = 0.40;
string text = "0.40";
Type targetType = typeof(decimal);

// Step 1: Add constant to cache
var literalExpression = constantExpressionHelper.CreateLiteral(value, text);
Assert.NotNull(literalExpression); // Ensure it was added

// Step 2: Manually trigger cleanup
var cts = new CancellationTokenSource(500);
await Task.Run(async () =>
{
while (!cts.IsCancellationRequested)
{
constantExpressionHelper.TryGetText(literalExpression, out _);
await Task.Delay(50); // Give some time for cleanup to be triggered
}
});

// Ensure some cleanup cycles have passed
await Task.Delay(500); // Allow cache cleanup to happen

// Step 3: Attempt to promote the expression after cleanup
var promotedExpression = expressionPromoter.Promote(literalExpression, targetType, exact: false, convertExpression: true);

// Assert: Promotion should still work even if the cache was cleaned
promotedExpression.Should().NotBeNull(); // Ensure `Promote()` still returns a valid expression
}
}