From f72888f3ae390e38f4a6ca4ad1acc8f0659e2661 Mon Sep 17 00:00:00 2001 From: Matthew Morrissette Date: Mon, 27 Jun 2022 10:14:21 -0700 Subject: [PATCH] Add support for CSharpHelper for List literals (#28212) * Add support for CSharpHelper for List literals Fixes #19274 Also relates to https://github.com/npgsql/efcore.pg/pull/2402 * Fixes from review comments --- .../Design/Internal/CSharpHelper.cs | 78 +++++++++++++++++++ .../Design/Internal/CSharpHelperTest.cs | 24 ++++++ 2 files changed, 102 insertions(+) diff --git a/src/EFCore.Design/Design/Internal/CSharpHelper.cs b/src/EFCore.Design/Design/Internal/CSharpHelper.cs index 9ee6e914bc6..562fb412e2b 100644 --- a/src/EFCore.Design/Design/Internal/CSharpHelper.cs +++ b/src/EFCore.Design/Design/Internal/CSharpHelper.cs @@ -737,6 +737,79 @@ 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(List 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) + { + builder.Append(" {"); + 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 (first) + { + builder.Append("()"); + } + else + { + 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 +917,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) { diff --git a/test/EFCore.Design.Tests/Design/Internal/CSharpHelperTest.cs b/test/EFCore.Design.Tests/Design/Internal/CSharpHelperTest.cs index d03c74c1aa9..2fc765dab8f 100644 --- a/test/EFCore.Design.Tests/Design/Internal/CSharpHelperTest.cs +++ b/test/EFCore.Design.Tests/Design/Internal/CSharpHelperTest.cs @@ -121,6 +121,30 @@ 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_with_single_element() + => Literal_works( + new List { "one" }, + @"new List { ""one"" }"); + + [ConditionalFact] + public void Literal_works_when_list_of_mixed_objects() + => Literal_works( + new List { 1, "two" }, + @"new List { 1, ""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(