Skip to content

Commit

Permalink
Add complex property support to migration and relational model
Browse files Browse the repository at this point in the history
+ Target .NET 8
  • Loading branch information
lorcQc authored Sep 16, 2023
1 parent f677618 commit 537e9ad
Show file tree
Hide file tree
Showing 23 changed files with 2,199 additions and 684 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ Namespace <#= FileNamespaceIdentifier #>
if (!Options.SuppressConnectionStringWarning)
{
#>
'TODO /!\ To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see http://go.microsoft.com/fwlink/?LinkId=723263.
'TODO /!\ To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see https://go.microsoft.com/fwlink/?LinkId=723263.
<#
}
#>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,20 @@ Namespace Design
''' <param name="parameters">Additional parameters used during code generation.</param>
Sub Generate(entityType As IEntityType, parameters As VisualBasicRuntimeAnnotationCodeGeneratorParameters)

''' <summary>
''' Generates code to create the given annotations.
''' </summary>
''' <param name="complexProperty">The entity type to which the annotations are applied.</param>
''' <param name="parameters">Additional parameters used during code generation.</param>
Sub Generate(complexProperty As IComplexProperty, parameters As VisualBasicRuntimeAnnotationCodeGeneratorParameters)

''' <summary>
''' Generates code to create the given annotations.
''' </summary>
''' <param name="complexType">The entity type to which the annotations are applied.</param>
''' <param name="parameters">Additional parameters used during code generation.</param>
Sub Generate(complexType As IComplexType, parameters As VisualBasicRuntimeAnnotationCodeGeneratorParameters)

''' <summary>
''' Generates code to create the given annotations.
''' </summary>
Expand Down
6 changes: 4 additions & 2 deletions EFCore.VisualBasic/Design/Internal/VisualBasicHelper.vb
Original file line number Diff line number Diff line change
Expand Up @@ -871,7 +871,7 @@ Namespace Design.Internal
builder As StringBuilder,
Optional simple As Boolean = False) As Boolean

' Only handle trivially simple cases for `new` and factory methods
' Only handle trivially simple cases for `New` and factory methods

Select Case exp.NodeType
Case ExpressionType.NewArrayInit
Expand All @@ -887,7 +887,9 @@ Namespace Design.Internal

Return True

Case ExpressionType.Convert
Case ExpressionType.Convert,
ExpressionType.ConvertChecked

Dim unaryExpression = DirectCast(exp, UnaryExpression)

If unaryExpression.Method?.Name <> "op_Implicit" Then
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,43 @@ Namespace Design
GenerateSimpleAnnotations(parameters)
End Sub

''' <inheritdoc />
Public Overridable Sub Generate(complexProperty As IComplexProperty, parameters As VisualBasicRuntimeAnnotationCodeGeneratorParameters) _
Implements IVisualBasicRuntimeAnnotationCodeGenerator.Generate

Dim annotations = parameters.Annotations

If Not parameters.IsRuntime Then
For Each annotation In annotations
If CoreAnnotationNames.AllNames.Contains(annotation.Key) AndAlso
annotation.Key <> CoreAnnotationNames.DiscriminatorMappingComplete Then

annotations.Remove(annotation.Key)
End If
Next
End If

GenerateSimpleAnnotations(parameters)
End Sub

''' <inheritdoc />
Public Overridable Sub Generate(complexType As IComplexType, parameters As VisualBasicRuntimeAnnotationCodeGeneratorParameters) _
Implements IVisualBasicRuntimeAnnotationCodeGenerator.Generate

Dim annotations = parameters.Annotations
If Not parameters.IsRuntime Then
For Each annotation In annotations
If CoreAnnotationNames.AllNames.Contains(annotation.Key) AndAlso
annotation.Key <> CoreAnnotationNames.DiscriminatorMappingComplete Then

annotations.Remove(annotation.Key)
End If
Next
End If

GenerateSimpleAnnotations(parameters)
End Sub

''' <inheritdoc/>
Public Overridable Sub Generate(prop As IProperty, parameters As VisualBasicRuntimeAnnotationCodeGeneratorParameters) _
Implements IVisualBasicRuntimeAnnotationCodeGenerator.Generate
Expand Down
10 changes: 5 additions & 5 deletions EFCore.VisualBasic/EFCore.VisualBasic.vbproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<AssemblyName>EntityFrameworkCore.VisualBasic</AssemblyName>
<RootNamespace>EntityFrameworkCore.VisualBasic</RootNamespace>
<Authors>Brice Lambson, et al.</Authors>
Expand Down Expand Up @@ -47,16 +47,16 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Cosmos" Version="8.0.0-preview.6.23315.1">
<PackageReference Include="Microsoft.EntityFrameworkCore.Cosmos" Version="8.0.0-rc.1.23373.1">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.0-preview.6.23315.1">
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.0-rc.1.23373.1">
<IncludeAssets>all</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="8.0.0-preview.6.23315.1">
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="8.0.0-rc.1.23373.1">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.0-preview.6.23315.1">
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.0-rc.1.23373.1">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1">
Expand Down
144 changes: 119 additions & 25 deletions EFCore.VisualBasic/Migrations/Design/VisualBasicSnapshotGenerator.vb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,16 @@ Namespace Migrations.Design
Public Class VisualBasicSnapshotGenerator

Private Shared ReadOnly HasAnnotationMethodInfo As MethodInfo =
GetType(ModelBuilder).GetRuntimeMethod(NameOf(ModelBuilder.HasAnnotation), {GetType(String), GetType(String)})
GetType(ModelBuilder).GetRuntimeMethod(NameOf(ModelBuilder.HasAnnotation),
{GetType(String), GetType(String)})

Private Shared ReadOnly HasPropertyAnnotationMethodInfo As MethodInfo =
GetType(ComplexPropertyBuilder).GetRuntimeMethod(NameOf(ComplexPropertyBuilder.HasPropertyAnnotation),
{GetType(String), GetType(String)})

Private Shared ReadOnly HasTypeAnnotationMethodInfo As MethodInfo =
GetType(ComplexPropertyBuilder).GetRuntimeMethod(NameOf(ComplexPropertyBuilder.HasTypeAnnotation),
{GetType(String), GetType(String)})

''' <summary>
''' Initializes a New instance of the <see cref="VisualBasicSnapshotGenerator" /> class.
Expand Down Expand Up @@ -101,7 +110,6 @@ Namespace Migrations.Design
NotNull(entityTypes, NameOf(entityTypes))
NotNull(stringBuilder, NameOf(stringBuilder))


Dim nonOwnedTypes = entityTypes.Where(Function(e) e.FindOwnership() Is Nothing).ToList()
For Each entityType In nonOwnedTypes
stringBuilder.AppendLine()
Expand All @@ -128,14 +136,14 @@ Namespace Migrations.Design
''' <summary>
''' Generates code for an <see cref="IEntityType" />.
''' </summary>
''' <param name="modelBuilderName">The name of the builder variable.</param>
''' <param name="builderName">The name of the builder variable.</param>
''' <param name="entityType">The entity type.</param>
''' <param name="stringBuilder">The builder code Is added to.</param>
Protected Overridable Sub GenerateEntityType(modelBuilderName As String,
Protected Overridable Sub GenerateEntityType(builderName As String,
entityType As IEntityType,
stringBuilder As IndentedStringBuilder)

NotEmpty(modelBuilderName, NameOf(modelBuilderName))
NotEmpty(builderName, NameOf(builderName))
NotNull(entityType, NameOf(entityType))
NotNull(stringBuilder, NameOf(stringBuilder))

Expand All @@ -154,10 +162,10 @@ Namespace Migrations.Design
entityTypeName = entityType.ClrType.DisplayName()
End If

Dim entityTypeBuilderName = GenerateEntityTypeBuilderName(modelBuilderName)
Dim entityTypeBuilderName = GenerateNestedBuilderName(builderName)

stringBuilder.
Append(modelBuilderName).
Append(builderName).
Append(If(ownerNavigation IsNot Nothing, If(ownership.IsUnique, ".OwnsOne(", ".OwnsMany("), ".Entity(")).
Append(VBCode.Literal(entityTypeName))

Expand All @@ -181,6 +189,8 @@ Namespace Migrations.Design

GenerateProperties(entityTypeBuilderName, entityType.GetDeclaredProperties(), stringBuilder)

GenerateComplexProperties(entityTypeBuilderName, entityType.GetDeclaredComplexProperties(), stringBuilder)

GenerateKeys(
entityTypeBuilderName,
entityType.GetDeclaredKeys(),
Expand All @@ -206,20 +216,6 @@ Namespace Migrations.Design
End Using
End Sub

Private Function GenerateEntityTypeBuilderName(modelBuilderName As String) As String
If modelBuilderName.StartsWith("b", StringComparison.Ordinal) Then
Dim counter = 1
If modelBuilderName.Length > 1 AndAlso
Integer.TryParse(modelBuilderName.AsSpan(1, modelBuilderName.Length - 1), counter) Then
counter += 1
End If

Return "b" & If(counter = 0, "", counter.ToString())
End If

Return "b"
End Function

''' <summary>
''' Generates code for owned entity types.
''' </summary>
Expand Down Expand Up @@ -583,7 +579,7 @@ Namespace Migrations.Design

annotations(RelationalAnnotationNames.DefaultValue) = New Annotation(
RelationalAnnotationNames.DefaultValue,
ValueConverter.ConvertToProvider(defaultValue))
valueConverter.ConvertToProvider(defaultValue))
End If

GenerateAnnotations(propertyBuilderName, [property], stringBuilder, annotations, inChainedCall:=True)
Expand All @@ -594,6 +590,100 @@ Namespace Migrations.Design
Return If([property].GetValueConverter(), t?.Converter)
End Function

''' <summary>
''' Generates code for <see cref="IComplexProperty" /> objects.
''' </summary>
''' <param name="typeBuilderName">The name of the builder variable.</param>
''' <param name="properties">The properties.</param>
''' <param name="stringBuilder">The builder code Is added to.</param>
Protected Overridable Sub GenerateComplexProperties(typeBuilderName As String,
properties As IEnumerable(Of IComplexProperty),
stringBuilder As IndentedStringBuilder)
For Each [property] In properties
GenerateComplexProperty(typeBuilderName, [property], stringBuilder)
Next
End Sub

''' <summary>
''' Generates code for an <see cref="IComplexProperty" />.
''' </summary>
''' <param name="builderName">The name of the builder variable.</param>
''' <param name="complexProperty">The entity type.</param>
''' <param name="stringBuilder">The builder code Is added to.</param>
Protected Overridable Sub GenerateComplexProperty(builderName As String,
complexProperty As IComplexProperty,
stringBuilder As IndentedStringBuilder)

Dim ComplexType = complexProperty.ComplexType
Dim complexTypeBuilderName = GenerateNestedBuilderName(builderName)

stringBuilder.
AppendLine().
Append(builderName).
Append($".ComplexProperty(Of {VBCode.Reference(Model.DefaultPropertyBagType)})(").
AppendLine($"{VBCode.Literal(complexProperty.Name)}, {VBCode.Literal(ComplexType.Name)},")

Using stringBuilder.Indent()
stringBuilder.
AppendLine($"Sub({complexTypeBuilderName})")

Using stringBuilder.Indent()
If complexProperty.IsNullable <> complexProperty.ClrType.IsNullableType() Then
stringBuilder.
Append(complexTypeBuilderName).
AppendLine(".IsRequired()").
AppendLine()
End If

GenerateProperties(complexTypeBuilderName, ComplexType.GetDeclaredProperties(), stringBuilder)

GenerateComplexProperties(complexTypeBuilderName, ComplexType.GetDeclaredComplexProperties(), stringBuilder)

GenerateComplexPropertyAnnotations(complexTypeBuilderName, complexProperty, stringBuilder)
End Using

stringBuilder.
AppendLine("End Sub)")
End Using
End Sub

Private Shared Function GenerateNestedBuilderName(builderName As String) As String
If builderName.StartsWith("b", StringComparison.Ordinal) Then
Dim counter = 1
If builderName.Length > 1 AndAlso Integer.TryParse(builderName.AsSpan(1), counter) Then
counter += 1
End If

Return "b" & If(counter = 0, "", counter.ToString())
End If

Return "b"
End Function

''' <summary>
''' Generates code for the annotations on an <see cref="IProperty" />.
''' </summary>
''' <param name="propertyBuilderName">The name of the builder variable.</param>
''' <param name="property">The property.</param>
''' <param name="stringBuilder">The builder code Is added to.</param>
Protected Overridable Sub GenerateComplexPropertyAnnotations(propertyBuilderName As String,
[property] As IComplexProperty,
stringBuilder As IndentedStringBuilder)

Dim propertyAnnotations = AnnotationCodeGenerator.
FilterIgnoredAnnotations([property].GetAnnotations()).
ToDictionary(Function(a) a.Name, Function(a) a)

Dim typeAnnotations = AnnotationCodeGenerator.
FilterIgnoredAnnotations([property].ComplexType.GetAnnotations()).
ToDictionary(Function(a) a.Name, Function(a) a)

GenerateAnnotations(propertyBuilderName, [property], stringBuilder, propertyAnnotations,
inChainedCall:=False, hasAnnotationMethodInfo:=HasPropertyAnnotationMethodInfo)

GenerateAnnotations(propertyBuilderName, [property], stringBuilder, typeAnnotations,
inChainedCall:=False, hasAnnotationMethodInfo:=HasTypeAnnotationMethodInfo)
End Sub

''' <summary>
''' Generates code for <see cref="IKey"/> objects.
Expand Down Expand Up @@ -1035,7 +1125,7 @@ Namespace Migrations.Design
End Using
End If

stringBuilder.AppendLine(")"c)
stringBuilder.AppendLine(")"c)
End Sub

Private Sub GenerateSplitTableMapping(entityTypeBuilderName As String,
Expand Down Expand Up @@ -1828,7 +1918,8 @@ Namespace Migrations.Design
stringBuilder As IndentedStringBuilder,
annotations As Dictionary(Of String, IAnnotation),
inChainedCall As Boolean,
Optional leadingNewline As Boolean = True)
Optional leadingNewline As Boolean = True,
Optional hasAnnotationMethodInfo As MethodInfo = Nothing)

Dim fluentApiCalls = AnnotationCodeGenerator.GenerateFluentApiCalls(annotatable, annotations)

Expand All @@ -1849,7 +1940,10 @@ Namespace Migrations.Design

' Append remaining raw annotations which did Not get generated as Fluent API calls
For Each annotation In annotations.Values.OrderBy(Function(a) a.Name)
Dim c = New MethodCallCodeFragment(HasAnnotationMethodInfo, annotation.Name, annotation.Value)
Dim c As New MethodCallCodeFragment(If(hasAnnotationMethodInfo, VisualBasicSnapshotGenerator.HasAnnotationMethodInfo),
annotation.Name,
annotation.Value)

chainedCall = If(chainedCall Is Nothing, c, chainedCall.Chain(c))
Next

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ Namespace <#= FileNamespaceIdentifier #>
<#
If Not Options.SuppressConnectionStringWarning Then
#>
'TODO /!\ To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see http://go.microsoft.com/fwlink/?LinkId=723263.
'TODO /!\ To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see https://go.microsoft.com/fwlink/?LinkId=723263.
<#
End If
#>
Expand Down
Loading

0 comments on commit 537e9ad

Please sign in to comment.