Skip to content

Commit

Permalink
Add complex types to Metadata
Browse files Browse the repository at this point in the history
Fixes #13947
  • Loading branch information
AndriySvyryd committed Jun 23, 2023
1 parent da6e296 commit ed851d9
Show file tree
Hide file tree
Showing 281 changed files with 25,619 additions and 5,670 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,10 @@ public virtual void ProcessEntityTypeAnnotationChanged(
/// <returns>The store value generation strategy to set for the given property.</returns>
protected override ValueGenerated? GetValueGenerated(IConventionProperty property)
{
var entityType = property.DeclaringEntityType;
var entityType = property.DeclaringType as IConventionEntityType;
var propertyType = property.ClrType.UnwrapNullableType();
if (propertyType == typeof(int))
if (propertyType == typeof(int)
&& entityType != null)
{
var ownership = entityType.FindOwnership();
if (ownership is { IsUnique: false }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Text;
using Microsoft.EntityFrameworkCore.Design.Internal;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Internal;

namespace Microsoft.EntityFrameworkCore.Scaffolding.Internal;
Expand Down Expand Up @@ -459,6 +460,11 @@ private string GenerateEntityType(IEntityType entityType, string @namespace, str
{
CreateEntityType(entityType, mainBuilder, methodBuilder, namespaces, className, nullable);

foreach (var complexProperty in entityType.GetDeclaredComplexProperties())
{
CreateComplexProperty(complexProperty, mainBuilder, methodBuilder, namespaces, className, nullable);
}

var foreignKeyNumber = 1;
foreach (var foreignKey in entityType.GetDeclaredForeignKeys())
{
Expand Down Expand Up @@ -538,6 +544,17 @@ private void CreateEntityType(
Create(property, parameters);
}

foreach (var complexProperty in entityType.GetDeclaredComplexProperties())
{
mainBuilder
.Append(_code.Identifier(complexProperty.Name, capitalize: true))
.Append("ComplexProperty")
.Append(".Create")
.Append("(")
.Append(entityTypeVariable)
.AppendLine(");");
}

foreach (var key in entityType.GetDeclaredKeys())
{
Create(key, propertyVariables, parameters, nullable);
Expand Down Expand Up @@ -665,14 +682,33 @@ private void Create(
IProperty property,
Dictionary<IProperty, string> propertyVariables,
CSharpRuntimeAnnotationCodeGeneratorParameters parameters)
{
var variableName = _code.Identifier(property.Name, parameters.ScopeVariables, capitalize: false);
propertyVariables[property] = variableName;

Create(property, variableName, propertyVariables, parameters);

CreateAnnotations(
property,
_annotationCodeGenerator.Generate,
parameters with { TargetName = variableName });

parameters.MainBuilder.AppendLine();
}

private void Create(
IProperty property,
string variableName,
Dictionary<IProperty, string> propertyVariables,
CSharpRuntimeAnnotationCodeGeneratorParameters parameters)
{
var valueGeneratorFactoryType = (Type?)property[CoreAnnotationNames.ValueGeneratorFactoryType];
if (valueGeneratorFactoryType == null
&& property.GetValueGeneratorFactory() != null)
{
throw new InvalidOperationException(
DesignStrings.CompiledModelValueGenerator(
property.DeclaringEntityType.ShortName(), property.Name, nameof(PropertyBuilder.HasValueGeneratorFactory)));
property.DeclaringType.ShortName(), property.Name, nameof(PropertyBuilder.HasValueGeneratorFactory)));
}

var valueComparerType = (Type?)property[CoreAnnotationNames.ValueComparerType];
Expand All @@ -681,7 +717,7 @@ private void Create(
{
throw new InvalidOperationException(
DesignStrings.CompiledModelValueComparer(
property.DeclaringEntityType.ShortName(), property.Name, nameof(PropertyBuilder.HasConversion)));
property.DeclaringType.ShortName(), property.Name, nameof(PropertyBuilder.HasConversion)));
}

var providerValueComparerType = (Type?)property[CoreAnnotationNames.ProviderValueComparerType];
Expand All @@ -690,7 +726,7 @@ private void Create(
{
throw new InvalidOperationException(
DesignStrings.CompiledModelValueComparer(
property.DeclaringEntityType.ShortName(), property.Name, nameof(PropertyBuilder.HasConversion)));
property.DeclaringType.ShortName(), property.Name, nameof(PropertyBuilder.HasConversion)));
}

var valueConverterType = GetValueConverterType(property);
Expand All @@ -699,20 +735,17 @@ private void Create(
{
throw new InvalidOperationException(
DesignStrings.CompiledModelValueConverter(
property.DeclaringEntityType.ShortName(), property.Name, nameof(PropertyBuilder.HasConversion)));
property.DeclaringType.ShortName(), property.Name, nameof(PropertyBuilder.HasConversion)));
}

if (property is IConventionProperty conventionProperty
&& conventionProperty.GetTypeMappingConfigurationSource() != null)
{
throw new InvalidOperationException(
DesignStrings.CompiledModelTypeMapping(
property.DeclaringEntityType.ShortName(), property.Name, "Customize()", parameters.ClassName));
property.DeclaringType.ShortName(), property.Name, "Customize()", parameters.ClassName));
}

var variableName = _code.Identifier(property.Name, parameters.ScopeVariables, capitalize: false);
propertyVariables[property] = variableName;

var mainBuilder = parameters.MainBuilder;
mainBuilder
.Append("var ").Append(variableName).Append(" = ").Append(parameters.TargetName).AppendLine(".AddProperty(")
Expand Down Expand Up @@ -870,13 +903,6 @@ private void Create(
mainBuilder
.AppendLine(");")
.DecrementIndent();

CreateAnnotations(
property,
_annotationCodeGenerator.Generate,
parameters with { TargetName = variableName });

mainBuilder.AppendLine();
}

private static Type? GetValueConverterType(IProperty property)
Expand Down Expand Up @@ -925,9 +951,8 @@ private void Create(
}

return i == ForeignKey.LongestFkChainAllowedLength
? throw new InvalidOperationException(
CoreStrings.RelationshipCycle(
property.DeclaringEntityType.DisplayName(), property.Name, "ValueConverterType"))
? throw new InvalidOperationException(CoreStrings.RelationshipCycle(
property.DeclaringType.DisplayName(), property.Name, "ValueConverterType"))
: null;
}

Expand Down Expand Up @@ -1057,9 +1082,10 @@ private void Create(

PropertyBaseParameters(property, parameters, skipType: true);

AddNamespace(property.ClrType, parameters.Namespaces);
mainBuilder
.AppendLine(",")
.Append("serviceType: typeof(" + property.ClrType.DisplayName(fullName: true, compilable: true) + ")");
.Append("serviceType: typeof(" + _code.Reference(property.ClrType) + ")");

mainBuilder
.AppendLine(");")
Expand Down Expand Up @@ -1148,6 +1174,166 @@ private void Create(
mainBuilder.AppendLine();
}

private void CreateComplexProperty(
IComplexProperty complexProperty,
IndentedStringBuilder mainBuilder,
IndentedStringBuilder methodBuilder,
SortedSet<string> namespaces,
string topClassName,
bool nullable)
{
mainBuilder
.AppendLine()
.Append("private static class ")
.Append(_code.Identifier(complexProperty.Name, capitalize: true))
.AppendLine("ComplexProperty")
.AppendLine("{");

var complexType = complexProperty.ComplexType;
using (mainBuilder.Indent())
{
var declaringTypeVariable = "declaringType";
mainBuilder
.Append("public static RuntimeComplexProperty Create(")
.Append(complexProperty.DeclaringType is IEntityType ? "RuntimeEntityType " : "RuntimeComplexType ")
.Append(declaringTypeVariable)
.AppendLine(")")
.AppendLine("{");

using (mainBuilder.Indent())
{
const string complexPropertyVariable = "complexProperty";
const string complexTypeVariable = "complexType";
var variables = new HashSet<string>
{
declaringTypeVariable,
complexPropertyVariable,
complexTypeVariable
};

mainBuilder
.Append("var ").Append(complexPropertyVariable).Append(" = ")
.Append(declaringTypeVariable).Append(".AddComplexProperty(")
.IncrementIndent()
.Append(_code.Literal(complexProperty.Name))
.AppendLine(",")
.Append(_code.Literal(complexProperty.ClrType))
.AppendLine(",")
.Append(_code.Literal(complexType.Name))
.AppendLine(",")
.Append(_code.Literal(complexType.ClrType));

AddNamespace(complexProperty.ClrType, namespaces);
AddNamespace(complexType.ClrType, namespaces);

var parameters = new CSharpRuntimeAnnotationCodeGeneratorParameters(
declaringTypeVariable,
topClassName,
mainBuilder,
methodBuilder,
namespaces,
variables,
nullable);

PropertyBaseParameters(complexProperty, parameters, skipType: true);

if (complexProperty.IsNullable)
{
mainBuilder.AppendLine(",")
.Append("nullable: ")
.Append(_code.Literal(true));
}

if (complexProperty.IsCollection)
{
mainBuilder.AppendLine(",")
.Append("collection: ")
.Append(_code.Literal(true));
}

var changeTrackingStrategy = complexType.GetChangeTrackingStrategy();
if (changeTrackingStrategy != ChangeTrackingStrategy.Snapshot)
{
namespaces.Add(typeof(ChangeTrackingStrategy).Namespace!);

mainBuilder.AppendLine(",")
.Append("changeTrackingStrategy: ")
.Append(_code.Literal(changeTrackingStrategy));
}

var indexerPropertyInfo = complexType.FindIndexerPropertyInfo();
if (indexerPropertyInfo != null)
{
mainBuilder.AppendLine(",")
.Append("indexerPropertyInfo: RuntimeEntityType.FindIndexerProperty(")
.Append(_code.Literal(complexType.ClrType))
.Append(")");
}

if (complexType.IsPropertyBag)
{
mainBuilder.AppendLine(",")
.Append("propertyBag: ")
.Append(_code.Literal(true));
}

mainBuilder
.AppendLine(");")
.AppendLine()
.DecrementIndent();

mainBuilder
.Append("var ").Append(complexTypeVariable).Append(" = ")
.Append(complexPropertyVariable).AppendLine(".ComplexType;");

var complexTypeParameters = parameters with { TargetName = complexTypeVariable };
var propertyVariables = new Dictionary<IProperty, string>();
foreach (var property in complexType.GetProperties())
{
Create(property, propertyVariables, complexTypeParameters);
}

foreach (var nestedComplexProperty in complexType.GetComplexProperties())
{
mainBuilder
.Append(_code.Identifier(nestedComplexProperty.Name, capitalize: true))
.Append("ComplexProperty")
.Append(".Create")
.Append("(")
.Append(complexTypeVariable)
.AppendLine(");");
}

CreateAnnotations(
complexType,
_annotationCodeGenerator.Generate,
complexTypeParameters);

CreateAnnotations(
complexProperty,
_annotationCodeGenerator.Generate,
parameters with { TargetName = complexPropertyVariable });

mainBuilder
.Append("return ")
.Append(complexPropertyVariable)
.AppendLine(";");
}

mainBuilder.AppendLine("}");
}

using (mainBuilder.Indent())
{
foreach (var nestedComplexProperty in complexType.GetComplexProperties())
{
CreateComplexProperty(nestedComplexProperty, mainBuilder, methodBuilder, namespaces, topClassName, nullable);
}
}

mainBuilder.AppendLine("}");
}

private void CreateForeignKey(
IForeignKey foreignKey,
int foreignKeyNumber,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -714,10 +714,10 @@ protected virtual ModelBuilder VisitForeignKeys(
uniquifier: NavigationUniquifier);

var leftSkipNavigation = leftEntityType.AddSkipNavigation(
leftNavigationPropertyName, null, rightEntityType, collection: true, onDependent: false);
leftNavigationPropertyName, memberInfo: null, targetEntityType: rightEntityType, collection: true, onDependent: false);
leftSkipNavigation.SetForeignKey(fks[0]);
var rightSkipNavigation = rightEntityType.AddSkipNavigation(
rightNavigationPropertyName, null, leftEntityType, collection: true, onDependent: false);
rightNavigationPropertyName, memberInfo: null, targetEntityType: leftEntityType, collection: true, onDependent: false);
rightSkipNavigation.SetForeignKey(fks[1]);
leftSkipNavigation.SetInverse(rightSkipNavigation);
rightSkipNavigation.SetInverse(leftSkipNavigation);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ public InMemoryValueGeneratorSelector(
/// 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 override ValueGenerator Select(IProperty property, IEntityType entityType)
public override ValueGenerator Select(IProperty property, ITypeBase typeBase)
=> property.GetValueGeneratorFactory() == null
&& property.ClrType.IsInteger()
&& property.ClrType.UnwrapNullableType() != typeof(char)
? GetOrCreate(property)
: base.Select(property, entityType);
: base.Select(property, typeBase);

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand All @@ -58,7 +58,7 @@ private ValueGenerator GetOrCreate(IProperty property)

throw new ArgumentException(
CoreStrings.InvalidValueGeneratorFactoryProperty(
"InMemoryIntegerValueGeneratorFactory", property.Name, property.DeclaringEntityType.DisplayName()));
"InMemoryIntegerValueGeneratorFactory", property.Name, property.DeclaringType.DisplayName()));
}

private bool FindGenerator(IProperty property, Type type, out ValueGenerator? valueGenerator)
Expand Down Expand Up @@ -132,8 +132,8 @@ private bool FindGenerator(IProperty property, Type type, out ValueGenerator? va
}

/// <inheritdoc />
protected override ValueGenerator? FindForType(IProperty property, IEntityType entityType, Type clrType)
protected override ValueGenerator? FindForType(IProperty property, ITypeBase typeBase, Type clrType)
=> property.ValueGenerated != ValueGenerated.Never && FindGenerator(property, clrType, out var valueGenerator)
? valueGenerator!
: base.FindForType(property, entityType, clrType);
: base.FindForType(property, typeBase, clrType);
}
Loading

0 comments on commit ed851d9

Please sign in to comment.