Skip to content

Commit e8093b2

Browse files
committed
Add a pubternal way to namespace-qualify ambiguous types in compiled model.
Fixes #25523
1 parent 9c7aa58 commit e8093b2

File tree

8 files changed

+495
-35
lines changed

8 files changed

+495
-35
lines changed

src/EFCore.Design/Design/Internal/CSharpHelper.cs

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -191,16 +191,33 @@ public virtual string Lambda(IReadOnlyList<string> properties, string? lambdaIde
191191
/// any release. You should only use it directly in your code with extreme caution and knowing that
192192
/// doing so can result in application failures when updating to a new Entity Framework Core release.
193193
/// </summary>
194-
public virtual string Reference(Type type)
195-
=> Reference(type, useFullName: false);
196-
197-
private string Reference(Type type, bool useFullName)
194+
public virtual string Reference(Type type, bool? fullName = null)
198195
{
199196
Check.NotNull(type, nameof(type));
200197

201-
return type.DisplayName(fullName: useFullName, compilable: true);
198+
fullName ??= type.IsNested ? ShouldUseFullName(type.DeclaringType!) : ShouldUseFullName(type);
199+
200+
return type.DisplayName(fullName.Value, compilable: true);
202201
}
203202

203+
/// <summary>
204+
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
205+
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
206+
/// any release. You should only use it directly in your code with extreme caution and knowing that
207+
/// doing so can result in application failures when updating to a new Entity Framework Core release.
208+
/// </summary>
209+
public virtual bool ShouldUseFullName(Type type)
210+
=> ShouldUseFullName(type.Name);
211+
212+
/// <summary>
213+
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
214+
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
215+
/// any release. You should only use it directly in your code with extreme caution and knowing that
216+
/// doing so can result in application failures when updating to a new Entity Framework Core release.
217+
/// </summary>
218+
public virtual bool ShouldUseFullName(string shortTypeName)
219+
=> false;
220+
204221
/// <summary>
205222
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
206223
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
@@ -536,8 +553,8 @@ public virtual string Literal(BigInteger value)
536553
/// any release. You should only use it directly in your code with extreme caution and knowing that
537554
/// doing so can result in application failures when updating to a new Entity Framework Core release.
538555
/// </summary>
539-
public virtual string Literal(Type value)
540-
=> $"typeof({Reference(value)})";
556+
public virtual string Literal(Type value, bool? useFullName = null)
557+
=> $"typeof({Reference(value, useFullName)})";
541558

542559
/// <summary>
543560
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -845,14 +862,14 @@ private bool HandleExpression(Expression expression, StringBuilder builder, bool
845862
case ExpressionType.Convert:
846863
builder
847864
.Append('(')
848-
.Append(Reference(expression.Type, useFullName: true))
865+
.Append(Reference(expression.Type, fullName: true))
849866
.Append(')');
850867

851868
return HandleExpression(((UnaryExpression)expression).Operand, builder);
852869
case ExpressionType.New:
853870
builder
854871
.Append("new ")
855-
.Append(Reference(expression.Type, useFullName: true));
872+
.Append(Reference(expression.Type, fullName: true));
856873

857874
return HandleArguments(((NewExpression)expression).Arguments, builder);
858875
case ExpressionType.Call:
@@ -861,7 +878,7 @@ private bool HandleExpression(Expression expression, StringBuilder builder, bool
861878
if (callExpression.Method.IsStatic)
862879
{
863880
builder
864-
.Append(Reference(callExpression.Method.DeclaringType!, useFullName: true));
881+
.Append(Reference(callExpression.Method.DeclaringType!, fullName: true));
865882
}
866883
else
867884
{
@@ -895,7 +912,7 @@ private bool HandleExpression(Expression expression, StringBuilder builder, bool
895912
if (memberExpression.Expression == null)
896913
{
897914
builder
898-
.Append(Reference(memberExpression.Member.DeclaringType!, useFullName: true));
915+
.Append(Reference(memberExpression.Member.DeclaringType!, fullName: true));
899916
}
900917
else
901918
{

src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ public virtual void Generate(string modelBuilderName, IModel model, IndentedStri
8282
/// <param name="stringBuilder"> The builder code is added to. </param>
8383
protected virtual void GenerateEntityTypes(
8484
string modelBuilderName,
85-
IReadOnlyList<IEntityType> entityTypes,
85+
IEnumerable<IEntityType> entityTypes,
8686
IndentedStringBuilder stringBuilder)
8787
{
8888
Check.NotEmpty(modelBuilderName, nameof(modelBuilderName));

src/EFCore.Design/Scaffolding/Internal/CSharpRuntimeModelCodeGenerator.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,8 +236,10 @@ private string CreateModelBuilder(
236236
var entityTypes = model.GetEntityTypesInHierarchicalOrder();
237237
var variables = new HashSet<string>();
238238

239+
var anyEntityTypes = false;
239240
foreach (var entityType in entityTypes)
240241
{
242+
anyEntityTypes = true;
241243
var variableName = _code.Identifier(entityType.ShortName(), variables, capitalize: false);
242244

243245
var firstChar = variableName[0] == '@' ? variableName[1] : variableName[0];
@@ -265,7 +267,7 @@ private string CreateModelBuilder(
265267
.AppendLine(");");
266268
}
267269

268-
if (entityTypes.Count > 0)
270+
if (anyEntityTypes)
269271
{
270272
mainBuilder.AppendLine();
271273
}
@@ -339,7 +341,7 @@ private string CreateModelBuilder(
339341
.AppendLine(");");
340342
}
341343

342-
if (entityTypes.Count > 0)
344+
if (anyEntityTypes)
343345
{
344346
mainBuilder.AppendLine();
345347
}

src/EFCore/Design/ICSharpHelper.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,8 +206,9 @@ string Literal<T>(T? value)
206206
/// Generates a <see cref="Type"/> literal.
207207
/// </summary>
208208
/// <param name="value"> The value. </param>
209+
/// <param name="fullName"> Whether the type should be namespace-qualified. </param>
209210
/// <returns> The literal. </returns>
210-
string Literal(Type value);
211+
string Literal(Type value, bool? fullName = null);
211212

212213
/// <summary>
213214
/// Generates an object array literal.
@@ -228,8 +229,9 @@ string Literal<T>(T? value)
228229
/// Generates a C# type reference.
229230
/// </summary>
230231
/// <param name="type"> The type to reference. </param>
232+
/// <param name="fullName"> Whether the type should be namespace-qualified. </param>
231233
/// <returns> The reference. </returns>
232-
string Reference(Type type);
234+
string Reference(Type type, bool? fullName = null);
233235

234236
/// <summary>
235237
/// Generates a literal for a type not known at compile time.

src/EFCore/Metadata/Conventions/RuntimeModelConvention.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ protected virtual RuntimeModel Create(IModel model)
5353
((IModel)runtimeModel).ModelDependencies = model.ModelDependencies!;
5454

5555
var entityTypes = model.GetEntityTypesInHierarchicalOrder();
56-
var entityTypePairs = new List<(IEntityType Source, RuntimeEntityType Target)>(entityTypes.Count);
56+
var entityTypePairs = new List<(IEntityType Source, RuntimeEntityType Target)>();
5757

5858
foreach (var entityType in entityTypes)
5959
{

src/EFCore/Metadata/Internal/ModelExtensions.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,10 @@ public static IEnumerable<IEntityType> GetRootEntityTypes(this IModel model)
4040
/// any release. You should only use it directly in your code with extreme caution and knowing that
4141
/// doing so can result in application failures when updating to a new Entity Framework Core release.
4242
/// </summary>
43-
public static IReadOnlyList<IEntityType> GetEntityTypesInHierarchicalOrder(this IModel model)
43+
public static IEnumerable<IEntityType> GetEntityTypesInHierarchicalOrder(this IModel model)
4444
=> Sort(model.GetEntityTypes());
4545

46-
private static IReadOnlyList<IEntityType> Sort(IEnumerable<IEntityType> entityTypes)
46+
private static IEnumerable<IEntityType> Sort(IEnumerable<IEntityType> entityTypes)
4747
{
4848
var entityTypeGraph = new Multigraph<IEntityType, int>();
4949
entityTypeGraph.AddVertices(entityTypes);
@@ -52,7 +52,7 @@ private static IReadOnlyList<IEntityType> Sort(IEnumerable<IEntityType> entityTy
5252
entityTypeGraph.AddEdge(entityType.BaseType!, entityType, 0);
5353
}
5454

55-
return entityTypeGraph.TopologicalSort();
55+
return entityTypeGraph.BatchingTopologicalSort().SelectMany(b => b.OrderBy(et => et.Name));
5656
}
5757

5858
/// <summary>

src/Shared/SharedTypeExtensions.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,8 +420,10 @@ public static IEnumerable<MemberInfo> GetMembersInHierarchy(this Type type, stri
420420
#pragma warning disable IDE0034 // Simplify 'default' expression - default causes default(object)
421421
{ typeof(int), default(int) },
422422
{ typeof(Guid), default(Guid) },
423+
{ typeof(DateOnly), default(DateOnly) },
423424
{ typeof(DateTime), default(DateTime) },
424425
{ typeof(DateTimeOffset), default(DateTimeOffset) },
426+
{ typeof(TimeOnly), default(TimeOnly) },
425427
{ typeof(long), default(long) },
426428
{ typeof(bool), default(bool) },
427429
{ typeof(double), default(double) },

0 commit comments

Comments
 (0)