diff --git a/src/EFCore.Design/Design/Internal/CSharpHelper.cs b/src/EFCore.Design/Design/Internal/CSharpHelper.cs
index 9ee6e914bc6..4a6947e01f2 100644
--- a/src/EFCore.Design/Design/Internal/CSharpHelper.cs
+++ b/src/EFCore.Design/Design/Internal/CSharpHelper.cs
@@ -737,6 +737,71 @@ public virtual string Literal(object?[,] values)
return builder.ToString();
}
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual string Literal(IList values, bool vertical = false)
+ => List(typeof(T), values, vertical);
+
+ private string List(Type type, IEnumerable values, bool vertical = false)
+ {
+ var builder = new IndentedStringBuilder();
+
+ builder.Append("new List<")
+ .Append(Reference(type))
+ .Append("> {");
+
+ var first = true;
+ foreach (var value in values)
+ {
+ if (first)
+ {
+ if (vertical)
+ {
+ builder.AppendLine();
+ builder.IncrementIndent();
+ }
+ else
+ {
+ builder.Append(" ");
+ }
+ first = false;
+ }
+ else
+ {
+ builder.Append(",");
+
+ if (vertical)
+ {
+ builder.AppendLine();
+ }
+ else
+ {
+ builder.Append(" ");
+ }
+ }
+
+ builder.Append(UnknownLiteral(value));
+ }
+
+ if (vertical)
+ {
+ builder.AppendLine();
+ builder.DecrementIndent();
+ }
+ else
+ {
+ builder.Append(" ");
+ }
+
+ builder.Append("}");
+
+ return builder.ToString();
+ }
+
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
@@ -844,6 +909,11 @@ public virtual string UnknownLiteral(object? value)
return Array(literalType.GetElementType()!, array);
}
+ if (value is IList list && value.GetType().IsGenericType && value.GetType().GetGenericTypeDefinition() == typeof(List<>))
+ {
+ return List(value.GetType().GetGenericArguments()[0], list);
+ }
+
var mapping = _typeMappingSource.FindMapping(literalType);
if (mapping != null)
{
@@ -878,6 +948,35 @@ private bool HandleExpression(Expression expression, StringBuilder builder, bool
HandleList(((NewArrayExpression)expression).Expressions, builder, simple: true);
+ builder
+ .Append(" }");
+
+ return true;
+ case ExpressionType.ListInit:
+ var listExpr = (ListInitExpression)expression;
+ if (listExpr.Initializers.Any(i => i.Arguments.Count != 1))
+ {
+ // If there is an initializer with more than one argument we can't make a literal cleanly
+ return false;
+ }
+
+ builder
+ .Append("new ")
+ .Append(Reference(expression.Type));
+
+ if (listExpr.NewExpression.Arguments.Count > 0 && !HandleArguments(listExpr.NewExpression.Arguments, builder))
+ {
+ return false;
+ }
+
+ builder
+ .Append(" { ");
+
+ if (!HandleList(listExpr.Initializers.Select(_ => _.Arguments.First()), builder, simple: true))
+ {
+ return false;
+ }
+
builder
.Append(" }");
diff --git a/test/EFCore.Design.Tests/Design/Internal/CSharpHelperTest.cs b/test/EFCore.Design.Tests/Design/Internal/CSharpHelperTest.cs
index d03c74c1aa9..db50e063185 100644
--- a/test/EFCore.Design.Tests/Design/Internal/CSharpHelperTest.cs
+++ b/test/EFCore.Design.Tests/Design/Internal/CSharpHelperTest.cs
@@ -121,6 +121,24 @@ public void Literal_works_when_many_ByteArray()
new byte[] { 1, 2 },
"new byte[] { 1, 2 }");
+ [ConditionalFact]
+ public void Literal_works_when_empty_list()
+ => Literal_works(
+ new List(),
+ @"new List { }");
+
+ [ConditionalFact]
+ public void Literal_works_when_list_without_ctor_arguments()
+ => Literal_works(
+ new List { "one", "two" },
+ @"new List { ""one"", ""two"" }");
+
+ [ConditionalFact]
+ public void Literal_works_when_list_with_ctor_arguments()
+ => Literal_works(
+ new List(new [] { "one" }) { "two", "three" },
+ @"new List { ""one"", ""two"", ""three"" }");
+
[ConditionalFact]
public void Literal_works_when_multiline_string()
=> Literal_works(
@@ -607,6 +625,33 @@ public void Literal_with_add()
new CSharpHelper(typeMapping).UnknownLiteral(new SimpleTestType()));
}
+ [ConditionalFact]
+ public void Literal_with_list_of_string_init_no_arguments()
+ {
+ var typeMapping = CreateTypeMappingSource(
+ v => Expression.ListInit(
+ Expression.New(typeof(List<>).MakeGenericType(typeof(string))),
+ Expression.Constant("one"), Expression.Constant("two")));
+
+ Assert.Equal(
+ @"new List { ""one"", ""two"" }",
+ new CSharpHelper(typeMapping).UnknownLiteral(new SimpleTestType()));
+ }
+
+ [ConditionalFact]
+ public void Literal_with_list_of_string_init_with_arguments()
+ {
+ var constructor = typeof(List<>).MakeGenericType((typeof(string))).GetConstructor(new[] { typeof(IEnumerable) })!;
+ var typeMapping = CreateTypeMappingSource(
+ v => Expression.ListInit(
+ Expression.New(constructor, Expression.NewArrayInit(typeof(string), Expression.Constant("one"))),
+ Expression.Constant("one"), Expression.Constant("two")));
+
+ Assert.Equal(
+ @"new List(new string[] { ""one"" }) { ""one"", ""two"" }",
+ new CSharpHelper(typeMapping).UnknownLiteral(new SimpleTestType()));
+ }
+
[ConditionalFact]
public void Literal_with_unsupported_node_throws()
{