Skip to content

Commit

Permalink
Add support for CSharpHelper for List literals
Browse files Browse the repository at this point in the history
Fixes #19274

Also relates to npgsql/efcore.pg#2402
  • Loading branch information
yinzara committed Jun 13, 2022
1 parent 27a83b9 commit 05969a0
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 0 deletions.
99 changes: 99 additions & 0 deletions src/EFCore.Design/Design/Internal/CSharpHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,71 @@ public virtual string Literal(object?[,] values)
return builder.ToString();
}

/// <summary>
/// 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.
/// </summary>
public virtual string Literal<T>(IList<T> 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();
}

/// <summary>
/// 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
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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(i => i.Arguments.First()), builder, simple: true))
{
return false;
}

builder
.Append(" }");

Expand Down
45 changes: 45 additions & 0 deletions test/EFCore.Design.Tests/Design/Internal/CSharpHelperTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<string>(),
@"new List<string> { }");

[ConditionalFact]
public void Literal_works_when_list_without_ctor_arguments()
=> Literal_works(
new List<string> { "one", "two" },
@"new List<string> { ""one"", ""two"" }");

[ConditionalFact]
public void Literal_works_when_list_with_ctor_arguments()
=> Literal_works(
new List<string>(new [] { "one" }) { "two", "three" },
@"new List<string> { ""one"", ""two"", ""three"" }");

[ConditionalFact]
public void Literal_works_when_multiline_string()
=> Literal_works(
Expand Down Expand Up @@ -607,6 +625,33 @@ public void Literal_with_add()
new CSharpHelper(typeMapping).UnknownLiteral(new SimpleTestType()));
}

[ConditionalFact]
public void Literal_with_list_init_without_ctor_arguments()
{
var typeMapping = CreateTypeMappingSource<SimpleTestType>(
v => Expression.ListInit(
Expression.New(typeof(List<>).MakeGenericType(typeof(string))),
Expression.Constant("one"), Expression.Constant("two")));

Assert.Equal(
@"new List<string> { ""one"", ""two"" }",
new CSharpHelper(typeMapping).UnknownLiteral(new SimpleTestType()));
}

[ConditionalFact]
public void Literal_with_list_init_with_ctor_arguments()
{
var constructor = typeof(List<>).MakeGenericType((typeof(string))).GetConstructor(new[] { typeof(IEnumerable<string>) })!;
var typeMapping = CreateTypeMappingSource<SimpleTestType>(
v => Expression.ListInit(
Expression.New(constructor, Expression.NewArrayInit(typeof(string), Expression.Constant("one"))),
Expression.Constant("one"), Expression.Constant("two")));

Assert.Equal(
@"new List<string>(new string[] { ""one"" }) { ""one"", ""two"" }",
new CSharpHelper(typeMapping).UnknownLiteral(new SimpleTestType()));
}

[ConditionalFact]
public void Literal_with_unsupported_node_throws()
{
Expand Down

0 comments on commit 05969a0

Please sign in to comment.