Skip to content

Commit 695b5ad

Browse files
authored
Fully integrate JSON mapping into the relational model (#38038)
Change partial update JSON path to be structured instead of a string JSONPATH Fixes #36646 Fixes #32185
1 parent 6abde55 commit 695b5ad

58 files changed

Lines changed: 8713 additions & 367 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

eng/Tools/ApiChief/Model/ApiModel.cs

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,22 +42,16 @@ public static ApiModel LoadFromAssembly(string path)
4242
}
4343

4444
public static ApiModel LoadFromFile(string path)
45-
{
46-
return JsonSerializer.Deserialize<ApiModel>(File.ReadAllText(path), _serializerOptions)!;
47-
}
45+
=> JsonSerializer.Deserialize<ApiModel>(File.ReadAllText(path), _serializerOptions)!;
4846

4947
public void EvaluateDelta(ApiModel current)
50-
{
51-
current.Types = FindChanges(this, current);
52-
}
48+
=> current.Types = FindChanges(this, current);
5349

5450
public bool HasRemovals()
5551
=> Types.Any(static type => type.Removals != null);
5652

5753
public override string ToString()
58-
{
59-
return JsonSerializer.Serialize(this, _serializerOptions).ReplaceLineEndings("\n");
60-
}
54+
=> JsonSerializer.Serialize(this, _serializerOptions).ReplaceLineEndings(Environment.NewLine);
6155

6256
private static ISet<ApiType> FindChanges(ApiModel baseline, ApiModel current)
6357
{

src/EFCore.Relational/Design/AnnotationCodeGenerator.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public class AnnotationCodeGenerator : IAnnotationCodeGenerator
3535
RelationalAnnotationNames.UpdateStoredProcedure,
3636
RelationalAnnotationNames.MappingFragments,
3737
RelationalAnnotationNames.RelationalOverrides,
38+
RelationalAnnotationNames.JsonElementMappings,
3839
#pragma warning disable CS0618
3940
RelationalAnnotationNames.ContainerColumnTypeMapping
4041
#pragma warning restore CS0618

src/EFCore.Relational/Design/Internal/RelationalCSharpRuntimeAnnotationCodeGenerator.cs

Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,8 @@ private string GetOrCreate(
369369
Create(column, tableParameters);
370370
}
371371

372+
CreateJsonElements(table, tableParameters);
373+
372374
CreateAnnotations(
373375
table,
374376
Generate,
@@ -406,6 +408,8 @@ private string Create(
406408
Create(column, tableParameters);
407409
}
408410

411+
CreateJsonElements(table, tableParameters);
412+
409413
CreateAnnotations(
410414
table,
411415
Generate,
@@ -449,6 +453,8 @@ private string GetOrCreate(
449453
Create(column, viewParameters);
450454
}
451455

456+
CreateJsonElements(view, viewParameters);
457+
452458
CreateAnnotations(
453459
view,
454460
Generate,
@@ -469,6 +475,221 @@ private string GetOrCreate(
469475
public virtual void Generate(IView view, CSharpRuntimeAnnotationCodeGeneratorParameters parameters)
470476
=> GenerateSimpleAnnotations(parameters);
471477

478+
private void CreateJsonElements(
479+
ITableBase table,
480+
CSharpRuntimeAnnotationCodeGeneratorParameters parameters)
481+
{
482+
foreach (var column in table.Columns)
483+
{
484+
if (column.JsonElement == null)
485+
{
486+
continue;
487+
}
488+
489+
var code = Dependencies.CSharpHelper;
490+
var mainBuilder = parameters.MainBuilder;
491+
AddNamespace(typeof(RelationalJsonObject), parameters.Namespaces);
492+
493+
var columnVariable = parameters.ScopeVariables.TryGetValue(column, out var cv)
494+
? cv
495+
: $"{parameters.TargetName}.FindColumn({code.Literal(column.Name)})!";
496+
var elementVariable = CreateJsonElement(column.JsonElement, columnVariable, parameters);
497+
498+
mainBuilder.AppendLine($"{columnVariable}.JsonElement = {elementVariable};");
499+
}
500+
}
501+
502+
private void CreateJsonElementMappings(
503+
ITableMappingBase tableMapping,
504+
string tableMappingVariable,
505+
CSharpRuntimeAnnotationCodeGeneratorParameters parameters)
506+
{
507+
foreach (var column in tableMapping.Table.Columns)
508+
{
509+
if (column.JsonElement != null)
510+
{
511+
CreateJsonElementMappings(column.JsonElement, tableMapping, tableMappingVariable, parameters);
512+
}
513+
}
514+
}
515+
516+
private void CreateJsonElementMappings(
517+
IRelationalJsonElement element,
518+
ITableMappingBase tableMapping,
519+
string tableMappingVariable,
520+
CSharpRuntimeAnnotationCodeGeneratorParameters parameters)
521+
{
522+
if (parameters.ScopeVariables.TryGetValue(element, out var elementVariable))
523+
{
524+
foreach (var mapping in element.PropertyMappings.Where(m => ReferenceEquals(m.TableMapping, tableMapping)))
525+
{
526+
parameters.MainBuilder
527+
.Append("RelationalModel.CreateJsonElementMapping(")
528+
.Append(GetPropertyBaseAccess(mapping.Property, parameters))
529+
.Append(", ")
530+
.Append(elementVariable)
531+
.Append(", ")
532+
.Append(tableMappingVariable)
533+
.AppendLine(");");
534+
}
535+
}
536+
537+
switch (element)
538+
{
539+
case IRelationalJsonObject jsonObject:
540+
foreach (var child in jsonObject.Properties)
541+
{
542+
CreateJsonElementMappings(child, tableMapping, tableMappingVariable, parameters);
543+
}
544+
545+
break;
546+
case IRelationalJsonArray jsonArray:
547+
CreateJsonElementMappings(jsonArray.ElementType, tableMapping, tableMappingVariable, parameters);
548+
break;
549+
}
550+
}
551+
552+
private string GetPropertyBaseAccess(
553+
IPropertyBase propertyBase,
554+
CSharpRuntimeAnnotationCodeGeneratorParameters parameters)
555+
{
556+
var code = Dependencies.CSharpHelper;
557+
var declaringTypeAccess = GetTypeBaseAccess(propertyBase.DeclaringType, parameters);
558+
559+
return propertyBase switch
560+
{
561+
IProperty property => $"{declaringTypeAccess}.FindProperty({code.Literal(property.Name)})!",
562+
IComplexProperty complexProperty => $"{declaringTypeAccess}.FindComplexProperty({code.Literal(complexProperty.Name)})!",
563+
INavigation navigation => $"{declaringTypeAccess}.FindNavigation({code.Literal(navigation.Name)})!",
564+
_ => throw new UnreachableException()
565+
};
566+
}
567+
568+
private string GetTypeBaseAccess(
569+
ITypeBase typeBase,
570+
CSharpRuntimeAnnotationCodeGeneratorParameters parameters)
571+
{
572+
var code = Dependencies.CSharpHelper;
573+
if (parameters.ScopeVariables.TryGetValue(typeBase, out var variable))
574+
{
575+
return variable;
576+
}
577+
578+
return typeBase switch
579+
{
580+
IEntityType entityType => $"FindEntityType({code.Literal(entityType.Name)})!",
581+
IComplexType complexType
582+
=> $"{GetTypeBaseAccess(complexType.ComplexProperty.DeclaringType, parameters)}.FindComplexProperty({code.Literal(complexType.ComplexProperty.Name)})!.ComplexType",
583+
_ => throw new UnreachableException()
584+
};
585+
}
586+
587+
private string CreateJsonElement(
588+
IRelationalJsonElement element,
589+
string columnVariable,
590+
CSharpRuntimeAnnotationCodeGeneratorParameters parameters)
591+
{
592+
var parentLiteral = element.ParentElement != null && parameters.ScopeVariables.TryGetValue(element.ParentElement, out var pv)
593+
? pv
594+
: "null";
595+
596+
return element switch
597+
{
598+
IRelationalJsonObject jsonObject => CreateJsonObject(jsonObject, columnVariable, parentLiteral, parameters),
599+
IRelationalJsonArray jsonArray => CreateJsonArray(jsonArray, columnVariable, parentLiteral, parameters),
600+
RelationalJsonScalar jsonScalar => CreateJsonProperty(jsonScalar, columnVariable, parentLiteral, parameters),
601+
_ => throw new UnreachableException()
602+
};
603+
}
604+
605+
private string CreateJsonObject(
606+
IRelationalJsonObject jsonObject,
607+
string columnVariable,
608+
string parentLiteral,
609+
CSharpRuntimeAnnotationCodeGeneratorParameters parameters)
610+
{
611+
var code = Dependencies.CSharpHelper;
612+
var mainBuilder = parameters.MainBuilder;
613+
var variable = code.Identifier((jsonObject.PropertyName ?? "element") + "JsonObject", jsonObject, parameters.ScopeObjects, capitalize: false);
614+
parameters.ScopeVariables[jsonObject] = variable;
615+
616+
mainBuilder.Append($"var {variable} = new RelationalJsonObject(");
617+
AppendJsonConstructorArgs(jsonObject, columnVariable, parentLiteral, mainBuilder, code);
618+
mainBuilder.AppendLine(");");
619+
620+
foreach (var child in jsonObject.Properties)
621+
{
622+
var childVariable = CreateJsonElement(child, columnVariable, parameters);
623+
mainBuilder.AppendLine($"{variable}.AddProperty({childVariable});");
624+
}
625+
626+
return variable;
627+
}
628+
629+
private string CreateJsonArray(
630+
IRelationalJsonArray jsonArray,
631+
string columnVariable,
632+
string parentLiteral,
633+
CSharpRuntimeAnnotationCodeGeneratorParameters parameters)
634+
{
635+
var code = Dependencies.CSharpHelper;
636+
var mainBuilder = parameters.MainBuilder;
637+
638+
var variable = code.Identifier((jsonArray.PropertyName ?? "array") + "JsonArray", jsonArray, parameters.ScopeObjects, capitalize: false);
639+
parameters.ScopeVariables[jsonArray] = variable;
640+
641+
mainBuilder.Append($"var {variable} = new RelationalJsonArray(");
642+
AppendJsonConstructorArgs(jsonArray, columnVariable, parentLiteral, mainBuilder, code);
643+
mainBuilder.AppendLine(");");
644+
645+
var elementTypeVariable = CreateJsonElement(jsonArray.ElementType, columnVariable, parameters);
646+
mainBuilder.AppendLine($"{variable}.ElementType = {elementTypeVariable};");
647+
648+
return variable;
649+
}
650+
651+
private string CreateJsonProperty(
652+
RelationalJsonScalar jsonScalar,
653+
string columnVariable,
654+
string parentLiteral,
655+
CSharpRuntimeAnnotationCodeGeneratorParameters parameters)
656+
{
657+
var code = Dependencies.CSharpHelper;
658+
var mainBuilder = parameters.MainBuilder;
659+
var variable = code.Identifier((jsonScalar.PropertyName ?? "scalar") + "JsonScalar", jsonScalar, parameters.ScopeObjects, capitalize: false);
660+
parameters.ScopeVariables[jsonScalar] = variable;
661+
662+
mainBuilder.Append($"var {variable} = new RelationalJsonScalar(");
663+
AppendJsonConstructorArgs(jsonScalar, columnVariable, parentLiteral, mainBuilder, code);
664+
mainBuilder.AppendLine(");");
665+
666+
return variable;
667+
}
668+
669+
private static void AppendJsonConstructorArgs(
670+
IRelationalJsonElement element,
671+
string columnVariable,
672+
string parentLiteral,
673+
IndentedStringBuilder builder,
674+
ICSharpHelper code)
675+
{
676+
if (element.ParentElement is IRelationalJsonArray)
677+
{
678+
// (parent, isNullable) — array type
679+
builder.Append($"{parentLiteral}, {code.Literal(element.IsNullable)}");
680+
}
681+
else if (element.ParentElement is IRelationalJsonObject)
682+
{
683+
// (name, parent, isNullable) — object property
684+
builder.Append($"{code.Literal(element.PropertyName!)}, {parentLiteral}, {code.Literal(element.IsNullable)}");
685+
}
686+
else
687+
{
688+
// (column, isNullable) — root element
689+
builder.Append($"{columnVariable}, {code.Literal(element.IsNullable)}");
690+
}
691+
}
692+
472693
private string GetOrCreate(
473694
ISqlQuery sqlQuery,
474695
CSharpRuntimeAnnotationCodeGeneratorParameters parameters)
@@ -1182,6 +1403,8 @@ private void Create(
11821403
.Append($"{typeBaseVariable}.FindProperty({code.Literal(columnMapping.Property.Name)})!, ")
11831404
.Append(tableMappingVariable).AppendLine(");");
11841405
}
1406+
1407+
CreateJsonElementMappings(tableMapping, tableMappingVariable, parameters);
11851408
}
11861409

11871410
/// <summary>
@@ -1234,6 +1457,8 @@ private void Create(
12341457
.Append(tableMappingVariable).AppendLine(");");
12351458
}
12361459

1460+
CreateJsonElementMappings(tableMapping, tableMappingVariable, parameters);
1461+
12371462
if (tableMapping == table.EntityTypeMappings.Last())
12381463
{
12391464
foreach (var uniqueConstraint in table.UniqueConstraints)
@@ -1302,6 +1527,8 @@ private void Create(
13021527
.Append($"{typeBaseVariable}.FindProperty({code.Literal(columnMapping.Property.Name)})!, ")
13031528
.Append(viewMappingVariable).AppendLine(");");
13041529
}
1530+
1531+
CreateJsonElementMappings(viewMapping, viewMappingVariable, parameters);
13051532
}
13061533

13071534
/// <summary>
@@ -1355,6 +1582,8 @@ private void Create(
13551582
.Append($"{typeBaseVariable}.FindProperty({code.Literal(columnMapping.Property.Name)})!, ")
13561583
.Append(sqlQueryMappingVariable).AppendLine(");");
13571584
}
1585+
1586+
CreateJsonElementMappings(sqlQueryMapping, sqlQueryMappingVariable, parameters);
13581587
}
13591588

13601589
/// <summary>
@@ -1410,6 +1639,8 @@ private void Create(
14101639
.Append($"{typeBaseVariable}.FindProperty({code.Literal(columnMapping.Property.Name)})!, ")
14111640
.Append(functionMappingVariable).AppendLine(");");
14121641
}
1642+
1643+
CreateJsonElementMappings(functionMapping, functionMappingVariable, parameters);
14131644
}
14141645

14151646
/// <summary>
@@ -2078,6 +2309,7 @@ public override void Generate(IProperty property, CSharpRuntimeAnnotationCodeGen
20782309
annotations.Remove(RelationalAnnotationNames.UpdateStoredProcedureParameterMappings);
20792310
annotations.Remove(RelationalAnnotationNames.UpdateStoredProcedureResultColumnMappings);
20802311
annotations.Remove(RelationalAnnotationNames.DefaultColumnMappings);
2312+
annotations.Remove(RelationalAnnotationNames.JsonElementMappings);
20812313
}
20822314
else
20832315
{
@@ -2110,6 +2342,40 @@ public override void Generate(IProperty property, CSharpRuntimeAnnotationCodeGen
21102342
base.Generate(property, parameters);
21112343
}
21122344

2345+
/// <inheritdoc />
2346+
public override void Generate(INavigation navigation, CSharpRuntimeAnnotationCodeGeneratorParameters parameters)
2347+
{
2348+
if (parameters.IsRuntime)
2349+
{
2350+
var annotations = parameters.Annotations;
2351+
annotations.Remove(RelationalAnnotationNames.TableColumnMappings);
2352+
annotations.Remove(RelationalAnnotationNames.ViewColumnMappings);
2353+
annotations.Remove(RelationalAnnotationNames.SqlQueryColumnMappings);
2354+
annotations.Remove(RelationalAnnotationNames.FunctionColumnMappings);
2355+
annotations.Remove(RelationalAnnotationNames.DefaultColumnMappings);
2356+
annotations.Remove(RelationalAnnotationNames.JsonElementMappings);
2357+
}
2358+
2359+
base.Generate(navigation, parameters);
2360+
}
2361+
2362+
/// <inheritdoc />
2363+
public override void Generate(IComplexProperty complexProperty, CSharpRuntimeAnnotationCodeGeneratorParameters parameters)
2364+
{
2365+
if (parameters.IsRuntime)
2366+
{
2367+
var annotations = parameters.Annotations;
2368+
annotations.Remove(RelationalAnnotationNames.TableColumnMappings);
2369+
annotations.Remove(RelationalAnnotationNames.ViewColumnMappings);
2370+
annotations.Remove(RelationalAnnotationNames.SqlQueryColumnMappings);
2371+
annotations.Remove(RelationalAnnotationNames.FunctionColumnMappings);
2372+
annotations.Remove(RelationalAnnotationNames.DefaultColumnMappings);
2373+
annotations.Remove(RelationalAnnotationNames.JsonElementMappings);
2374+
}
2375+
2376+
base.Generate(complexProperty, parameters);
2377+
}
2378+
21132379
private void Create(
21142380
IRelationalPropertyOverrides overrides,
21152381
string overridesVariable,

0 commit comments

Comments
 (0)