diff --git a/src/Aspire.Hosting/ApplicationModel/ReferenceExpression.cs b/src/Aspire.Hosting/ApplicationModel/ReferenceExpression.cs index dcf032ddef..c9ffc41ba6 100644 --- a/src/Aspire.Hosting/ApplicationModel/ReferenceExpression.cs +++ b/src/Aspire.Hosting/ApplicationModel/ReferenceExpression.cs @@ -112,7 +112,9 @@ public readonly void AppendLiteral(string value) /// The formatted string to be appended to the interpolated string. public readonly void AppendFormatted(string? value) { - _builder.Append(value); + // The value that comes in is a literal string that is not meant to be interpreted. + // But the _builder later gets treated as a format string, so we just need to escape the braces. + _builder.Append(value?.Replace("{", "{{").Replace("}", "}}")); } /// diff --git a/tests/Aspire.Hosting.Tests/ReferenceExpressionTests.cs b/tests/Aspire.Hosting.Tests/ReferenceExpressionTests.cs new file mode 100644 index 0000000000..1d2e84d93a --- /dev/null +++ b/tests/Aspire.Hosting.Tests/ReferenceExpressionTests.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit; + +namespace Aspire.Hosting.Tests; +public class ReferenceExpressionTests +{ + [Theory] + [InlineData("world", "Hello world", "Hello world")] + [InlineData("{", "Hello {{", "Hello {")] + [InlineData("}", "Hello }}", "Hello }")] + [InlineData("{1}", "Hello {{1}}", "Hello {1}")] + [InlineData("{x}", "Hello {{x}}", "Hello {x}")] + [InlineData("{{x}}", "Hello {{{{x}}}}", "Hello {{x}}")] + public void TestReferenceExpressionCreateInputStringTreatedAsLiteral(string input, string expectedFormat, string expectedExpression) + { + var refExpression = ReferenceExpression.Create($"Hello {input}"); + Assert.Equal(expectedFormat, refExpression.Format); + + // Generally, the input string should end up unchanged in the expression, since it's a literal + var expr = refExpression.ValueExpression; + Assert.Equal(expectedExpression, expr); + } +}