diff --git a/Directory.Build.props b/Directory.Build.props index 36432f73130..4ea29042ac6 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -42,11 +42,6 @@ https://docs.microsoft.com/ef/core/ - - - - - $(WarningsNotAsErrors);CS1591;SA1636 diff --git a/Directory.Build.targets b/Directory.Build.targets index 58605e9ceb0..16f8a0c019e 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -9,7 +9,7 @@ diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 033722b5eb7..aa704ddcde6 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -292,15 +292,16 @@ stages: arguments: -ConfigFile $(Build.SourcesDirectory)/NuGet.config -Password $Env:Token env: Token: $(dn-bot-dnceng-artifact-feeds-rw) - - script: restore.cmd -ci /p:configuration=$(_BuildConfig) + - script: restore.cmd -ci /p:configuration=$(_BuildConfig) $(_InternalRuntimeDownloadArgs) displayName: Restore packages - - script: .dotnet\dotnet build eng\helix.proj /restore /t:Test /p:configuration=$(_BuildConfig) /bl:$(Build.SourcesDirectory)/artifacts/log/$(_BuildConfig)/SendToHelix.binlog + - script: .dotnet\dotnet build eng\helix.proj /restore /t:Test /p:configuration=$(_BuildConfig) /bl:$(Build.SourcesDirectory)/artifacts/log/$(_BuildConfig)/SendToHelix.binlog $(_InternalRuntimeDownloadArgs) displayName: Send job to helix env: HelixAccessToken: $(_HelixAccessToken) SYSTEM_ACCESSTOKEN: $(System.AccessToken) # We need to set this env var to publish helix results to Azure Dev Ops MSSQL_SA_PASSWORD: "Password12!" COMPlus_EnableWriteXorExecute: 0 # Work-around for https://github.com/dotnet/runtime/issues/70758 + DotNetBuildsInternalReadSasToken: $(dotnetbuilds-internal-container-read-token) - ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest'), ne(variables.runCodeQL3000, 'true')) }}: - template: eng\common\templates\post-build\post-build.yml diff --git a/benchmark/EFCore.Sqlite.Benchmarks/EFCore.Sqlite.Benchmarks.csproj b/benchmark/EFCore.Sqlite.Benchmarks/EFCore.Sqlite.Benchmarks.csproj index 0656a32498b..b693456dfed 100644 --- a/benchmark/EFCore.Sqlite.Benchmarks/EFCore.Sqlite.Benchmarks.csproj +++ b/benchmark/EFCore.Sqlite.Benchmarks/EFCore.Sqlite.Benchmarks.csproj @@ -16,7 +16,7 @@ - + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index b98f6d8eebc..1f2996a49ec 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,72 +1,72 @@ - + https://github.com/dotnet/runtime - f8c110b8003d68cc635add4ca791d6cf2e645561 + 9f4c5f7766ba6e6899bc998e17ca91a4fcc2d8d5 - + https://github.com/dotnet/runtime - f8c110b8003d68cc635add4ca791d6cf2e645561 + 9f4c5f7766ba6e6899bc998e17ca91a4fcc2d8d5 - + https://github.com/dotnet/runtime - f8c110b8003d68cc635add4ca791d6cf2e645561 + 9f4c5f7766ba6e6899bc998e17ca91a4fcc2d8d5 - + https://github.com/dotnet/runtime - f8c110b8003d68cc635add4ca791d6cf2e645561 + 9f4c5f7766ba6e6899bc998e17ca91a4fcc2d8d5 - + https://github.com/dotnet/runtime - f8c110b8003d68cc635add4ca791d6cf2e645561 + 9f4c5f7766ba6e6899bc998e17ca91a4fcc2d8d5 - + https://github.com/dotnet/runtime - f8c110b8003d68cc635add4ca791d6cf2e645561 + 9f4c5f7766ba6e6899bc998e17ca91a4fcc2d8d5 - + https://github.com/dotnet/runtime - f8c110b8003d68cc635add4ca791d6cf2e645561 + 9f4c5f7766ba6e6899bc998e17ca91a4fcc2d8d5 - + https://github.com/dotnet/runtime - f8c110b8003d68cc635add4ca791d6cf2e645561 + 9f4c5f7766ba6e6899bc998e17ca91a4fcc2d8d5 - + https://github.com/dotnet/runtime - f8c110b8003d68cc635add4ca791d6cf2e645561 + 9f4c5f7766ba6e6899bc998e17ca91a4fcc2d8d5 - + https://github.com/dotnet/runtime - f8c110b8003d68cc635add4ca791d6cf2e645561 + 9f4c5f7766ba6e6899bc998e17ca91a4fcc2d8d5 - + https://github.com/dotnet/runtime - f8c110b8003d68cc635add4ca791d6cf2e645561 + 9f4c5f7766ba6e6899bc998e17ca91a4fcc2d8d5 - + https://github.com/dotnet/runtime - f8c110b8003d68cc635add4ca791d6cf2e645561 + 9f4c5f7766ba6e6899bc998e17ca91a4fcc2d8d5 - + https://github.com/dotnet/runtime - f8c110b8003d68cc635add4ca791d6cf2e645561 + 9f4c5f7766ba6e6899bc998e17ca91a4fcc2d8d5 - + https://github.com/dotnet/arcade - 385129cbc980a515ddee2fa56f6b16f3183ed9bc + 4665b3d04e1da3796b965c3c3e3b97f55c449a6e - + https://github.com/dotnet/arcade - 385129cbc980a515ddee2fa56f6b16f3183ed9bc + 4665b3d04e1da3796b965c3c3e3b97f55c449a6e - + https://github.com/dotnet/arcade - 385129cbc980a515ddee2fa56f6b16f3183ed9bc + 4665b3d04e1da3796b965c3c3e3b97f55c449a6e diff --git a/eng/Versions.props b/eng/Versions.props index b472ecee542..7981ccc2942 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -16,22 +16,22 @@ False - 8.0.0-rc.1.23421.3 - 8.0.0-rc.1.23421.3 - 8.0.0-rc.1.23421.3 - 8.0.0-rc.1.23421.3 - 8.0.0-rc.1.23421.3 - 8.0.0-rc.1.23421.3 - 8.0.0-rc.1.23421.3 - 8.0.0-rc.1.23421.3 - 8.0.0-rc.1.23421.3 - 8.0.0-rc.1.23421.3 - 8.0.0-rc.1.23421.3 - 8.0.0-rc.1.23421.3 - 8.0.0-rc.1.23421.3 + 9.0.0-alpha.1.23465.19 + 9.0.0-alpha.1.23465.19 + 9.0.0-alpha.1.23465.19 + 9.0.0-alpha.1.23465.19 + 9.0.0-alpha.1.23465.19 + 9.0.0-alpha.1.23465.19 + 9.0.0-alpha.1.23465.19 + 9.0.0-alpha.1.23465.19 + 9.0.0-alpha.1.23465.19 + 9.0.0-alpha.1.23465.19 + 9.0.0-alpha.1.23465.19 + 9.0.0-alpha.1.23465.19 + 9.0.0-alpha.1.23465.19 - 8.0.0-beta.23419.1 + 8.0.0-beta.23451.1 diff --git a/eng/common/cross/toolchain.cmake b/eng/common/cross/toolchain.cmake index a88d643c8a7..0998e875e5f 100644 --- a/eng/common/cross/toolchain.cmake +++ b/eng/common/cross/toolchain.cmake @@ -207,6 +207,7 @@ elseif(ILLUMOS) set(CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_CXX_STANDARD_LIBRARIES} -lssp") elseif(HAIKU) set(CMAKE_SYSROOT "${CROSS_ROOTFS}") + set(CMAKE_PROGRAM_PATH "${CMAKE_PROGRAM_PATH};${CROSS_ROOTFS}/cross-tools-x86_64/bin") set(TOOLSET_PREFIX ${TOOLCHAIN}-) function(locate_toolchain_exec exec var) @@ -217,7 +218,6 @@ elseif(HAIKU) endif() find_program(EXEC_LOCATION_${exec} - PATHS "${CROSS_ROOTFS}/cross-tools-x86_64/bin" NAMES "${TOOLSET_PREFIX}${exec}${CLR_CMAKE_COMPILER_FILE_NAME_VERSION}" "${TOOLSET_PREFIX}${exec}") diff --git a/eng/common/loc/P22DotNetHtmlLocalization.lss b/eng/common/loc/P22DotNetHtmlLocalization.lss index 858a0b237c6..5d892d61939 100644 Binary files a/eng/common/loc/P22DotNetHtmlLocalization.lss and b/eng/common/loc/P22DotNetHtmlLocalization.lss differ diff --git a/eng/helix.proj b/eng/helix.proj index cb785fbeab2..fce3e5c0614 100644 --- a/eng/helix.proj +++ b/eng/helix.proj @@ -28,6 +28,11 @@ runtime + + + $([System.Environment]::GetEnvironmentVariable('DotNetBuildsInternalReadSasToken')) + diff --git a/global.json b/global.json index 3ce448dab21..c0684a3f40e 100644 --- a/global.json +++ b/global.json @@ -1,11 +1,11 @@ { "sdk": { - "version": "8.0.100-rc.1.23407.2", + "version": "8.0.100-rc.2.23461.1", "allowPrerelease": true, "rollForward": "latestMajor" }, "tools": { - "dotnet": "8.0.100-rc.1.23407.2", + "dotnet": "8.0.100-rc.2.23461.1", "runtimes": { "dotnet": [ "$(MicrosoftNETCoreBrowserDebugHostTransportVersion)" @@ -13,7 +13,7 @@ } }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "8.0.0-beta.23419.1", - "Microsoft.DotNet.Helix.Sdk": "8.0.0-beta.23419.1" + "Microsoft.DotNet.Arcade.Sdk": "8.0.0-beta.23451.1", + "Microsoft.DotNet.Helix.Sdk": "8.0.0-beta.23451.1" } } diff --git a/src/EFCore.Cosmos/Infrastructure/CosmosDbContextOptionsBuilder.cs b/src/EFCore.Cosmos/Infrastructure/CosmosDbContextOptionsBuilder.cs index b396ce16b2f..e26e454ba6d 100644 --- a/src/EFCore.Cosmos/Infrastructure/CosmosDbContextOptionsBuilder.cs +++ b/src/EFCore.Cosmos/Infrastructure/CosmosDbContextOptionsBuilder.cs @@ -65,6 +65,17 @@ public virtual CosmosDbContextOptionsBuilder ExecutionStrategy( public virtual CosmosDbContextOptionsBuilder Region(string region) => WithOption(e => e.WithRegion(Check.NotNull(region, nameof(region)))); + /// + /// Configures the context to use the provided preferred regions for geo-replicated database accounts. + /// + /// + /// See Using DbContextOptions, and + /// Accessing Azure Cosmos DB with EF Core for more information and examples. + /// + /// A list of Azure Cosmos DB region names. + public virtual CosmosDbContextOptionsBuilder PreferredRegions(IReadOnlyList regions) + => WithOption(e => e.WithPreferredRegions(Check.NotNull(regions, nameof(regions)))); + /// /// Limits the operations to the provided endpoint. /// diff --git a/src/EFCore.Cosmos/Infrastructure/Internal/CosmosDbOptionExtension.cs b/src/EFCore.Cosmos/Infrastructure/Internal/CosmosDbOptionExtension.cs index 33c4c7588f0..9a7792b57e5 100644 --- a/src/EFCore.Cosmos/Infrastructure/Internal/CosmosDbOptionExtension.cs +++ b/src/EFCore.Cosmos/Infrastructure/Internal/CosmosDbOptionExtension.cs @@ -23,6 +23,7 @@ public class CosmosOptionsExtension : IDbContextOptionsExtension private string? _connectionString; private string? _databaseName; private string? _region; + private IReadOnlyList? _preferredRegions; private ConnectionMode? _connectionMode; private bool? _limitToEndpoint; private Func? _executionStrategyFactory; @@ -61,6 +62,7 @@ protected CosmosOptionsExtension(CosmosOptionsExtension copyFrom) _databaseName = copyFrom._databaseName; _connectionString = copyFrom._connectionString; _region = copyFrom._region; + _preferredRegions = copyFrom._preferredRegions; _connectionMode = copyFrom._connectionMode; _limitToEndpoint = copyFrom._limitToEndpoint; _executionStrategyFactory = copyFrom._executionStrategyFactory; @@ -247,6 +249,30 @@ public virtual CosmosOptionsExtension WithRegion(string? region) return clone; } + /// + /// 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. + /// + public virtual IReadOnlyList? PreferredRegions + => _preferredRegions; + + /// + /// 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. + /// + public virtual CosmosOptionsExtension WithPreferredRegions(IReadOnlyList? regions) + { + var clone = Clone(); + + clone._preferredRegions = regions; + + return clone; + } + /// /// 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 diff --git a/src/EFCore.Cosmos/Infrastructure/Internal/CosmosSingletonOptions.cs b/src/EFCore.Cosmos/Infrastructure/Internal/CosmosSingletonOptions.cs index 22f657166c6..4426c1ecc9c 100644 --- a/src/EFCore.Cosmos/Infrastructure/Internal/CosmosSingletonOptions.cs +++ b/src/EFCore.Cosmos/Infrastructure/Internal/CosmosSingletonOptions.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections; using System.Net; using Azure.Core; @@ -54,6 +55,14 @@ public class CosmosSingletonOptions : ICosmosSingletonOptions /// public virtual string? Region { get; private set; } + /// + /// 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. + /// + public virtual IReadOnlyList? PreferredRegions { get; private set; } + /// /// 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 @@ -158,6 +167,7 @@ public virtual void Initialize(IDbContextOptions options) TokenCredential = cosmosOptions.TokenCredential; ConnectionString = cosmosOptions.ConnectionString; Region = cosmosOptions.Region; + PreferredRegions = cosmosOptions.PreferredRegions; LimitToEndpoint = cosmosOptions.LimitToEndpoint; EnableContentResponseOnWrite = cosmosOptions.EnableContentResponseOnWrite; ConnectionMode = cosmosOptions.ConnectionMode; @@ -188,6 +198,7 @@ public virtual void Validate(IDbContextOptions options) || TokenCredential != cosmosOptions.TokenCredential || ConnectionString != cosmosOptions.ConnectionString || Region != cosmosOptions.Region + || !StructuralComparisons.StructuralEqualityComparer.Equals(PreferredRegions, cosmosOptions.PreferredRegions) || LimitToEndpoint != cosmosOptions.LimitToEndpoint || ConnectionMode != cosmosOptions.ConnectionMode || WebProxy != cosmosOptions.WebProxy diff --git a/src/EFCore.Cosmos/Infrastructure/Internal/ICosmosSingletonOptions.cs b/src/EFCore.Cosmos/Infrastructure/Internal/ICosmosSingletonOptions.cs index b38e4f40993..a26b79a82b3 100644 --- a/src/EFCore.Cosmos/Infrastructure/Internal/ICosmosSingletonOptions.cs +++ b/src/EFCore.Cosmos/Infrastructure/Internal/ICosmosSingletonOptions.cs @@ -60,6 +60,14 @@ public interface ICosmosSingletonOptions : ISingletonOptions /// string? Region { get; } + /// + /// 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. + /// + IReadOnlyList? PreferredRegions { get; } + /// /// 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 diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Cosmos/Query/Internal/CosmosQueryableMethodTranslatingExpressionVisitor.cs index eb8c3c01766..b5e7fb2c6bc 100644 --- a/src/EFCore.Cosmos/Query/Internal/CosmosQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.Cosmos/Query/Internal/CosmosQueryableMethodTranslatingExpressionVisitor.cs @@ -154,6 +154,24 @@ static bool ExtractPartitionKeyFromPredicate( return true; } } + else if (joinCondition is MethodCallExpression + { + Method.Name: "Equals", + Object: null, + Arguments: [MethodCallExpression equalsMethodCallExpression, ParameterExpression parameterExpresion] + } + && equalsMethodCallExpression.TryGetEFPropertyArguments(out _, out var propertyName)) + { + var property = entityType.FindProperty(propertyName); + if (property == null) + { + return false; + } + + properties.Add(property); + parameterNames.Add(parameterExpresion.Name); + return true; + } return false; } diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosSqlTranslatingExpressionVisitor.cs b/src/EFCore.Cosmos/Query/Internal/CosmosSqlTranslatingExpressionVisitor.cs index 7079cec7b45..f34e5b1c1cb 100644 --- a/src/EFCore.Cosmos/Query/Internal/CosmosSqlTranslatingExpressionVisitor.cs +++ b/src/EFCore.Cosmos/Query/Internal/CosmosSqlTranslatingExpressionVisitor.cs @@ -840,7 +840,7 @@ private bool TryRewriteContainsEntity(Expression source, Expression item, out Ex var propertyGetter = property.GetGetter(); foreach (var value in values) { - propertyValueList.Add(propertyGetter.GetStructuralTypeClrValue(value)); + propertyValueList.Add(propertyGetter.GetClrValue(value)); } rewrittenSource = Expression.Constant(propertyValueList); @@ -971,7 +971,7 @@ private Expression CreatePropertyAccessExpression(Expression target, IProperty p { case SqlConstantExpression sqlConstantExpression: return Expression.Constant( - property.GetGetter().GetStructuralTypeClrValue(sqlConstantExpression.Value!), property.ClrType.MakeNullable()); + property.GetGetter().GetClrValue(sqlConstantExpression.Value!), property.ClrType.MakeNullable()); case SqlParameterExpression sqlParameterExpression when sqlParameterExpression.Name.StartsWith(QueryCompilationContext.QueryParameterPrefix, StringComparison.Ordinal): @@ -1002,7 +1002,7 @@ when memberInitExpression.Bindings.SingleOrDefault( private static T ParameterValueExtractor(QueryContext context, string baseParameterName, IProperty property) { var baseParameter = context.ParameterValues[baseParameterName]; - return baseParameter == null ? (T)(object)null : (T)property.GetGetter().GetStructuralTypeClrValue(baseParameter); + return baseParameter == null ? (T)(object)null : (T)property.GetGetter().GetClrValue(baseParameter); } private static List ParameterListValueExtractor( @@ -1016,7 +1016,7 @@ private static List ParameterListValueExtractor( } var getter = property.GetGetter(); - return baseListParameter.Select(e => e != null ? (TProperty)getter.GetStructuralTypeClrValue(e) : (TProperty)(object)null).ToList(); + return baseListParameter.Select(e => e != null ? (TProperty)getter.GetClrValue(e) : (TProperty)(object)null).ToList(); } private static bool IsNullSqlConstantExpression(Expression expression) diff --git a/src/EFCore.Cosmos/Storage/Internal/CosmosTypeMapping.cs b/src/EFCore.Cosmos/Storage/Internal/CosmosTypeMapping.cs index cb62bd9a9e5..726e907c271 100644 --- a/src/EFCore.Cosmos/Storage/Internal/CosmosTypeMapping.cs +++ b/src/EFCore.Cosmos/Storage/Internal/CosmosTypeMapping.cs @@ -60,12 +60,13 @@ protected CosmosTypeMapping(CoreTypeMappingParameters parameters) /// 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. /// - public override CoreTypeMapping Clone( + public override CoreTypeMapping WithComposedConverter( ValueConverter? converter, ValueComparer? comparer = null, + ValueComparer? keyComparer = null, CoreTypeMapping? elementMapping = null, JsonValueReaderWriter? jsonValueReaderWriter = null) - => new CosmosTypeMapping(Parameters.WithComposedConverter(converter, comparer, elementMapping, jsonValueReaderWriter)); + => new CosmosTypeMapping(Parameters.WithComposedConverter(converter, comparer, keyComparer, elementMapping, jsonValueReaderWriter)); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to diff --git a/src/EFCore.Cosmos/Storage/Internal/CosmosTypeMappingSource.cs b/src/EFCore.Cosmos/Storage/Internal/CosmosTypeMappingSource.cs index ce9dd461aaf..d01c7aedd18 100644 --- a/src/EFCore.Cosmos/Storage/Internal/CosmosTypeMappingSource.cs +++ b/src/EFCore.Cosmos/Storage/Internal/CosmosTypeMappingSource.cs @@ -71,6 +71,12 @@ public CosmosTypeMappingSource(TypeMappingSourceDependencies dependencies) private CoreTypeMapping? FindCollectionMapping(in TypeMappingInfo mappingInfo) { var clrType = mappingInfo.ClrType!; + + if (mappingInfo.ElementTypeMapping != null) + { + return null; + } + var elementType = clrType.TryGetSequenceType(); if (elementType == null) { diff --git a/src/EFCore.Cosmos/Storage/Internal/SingletonCosmosClientWrapper.cs b/src/EFCore.Cosmos/Storage/Internal/SingletonCosmosClientWrapper.cs index bc4f620c922..4e99f2e0b00 100644 --- a/src/EFCore.Cosmos/Storage/Internal/SingletonCosmosClientWrapper.cs +++ b/src/EFCore.Cosmos/Storage/Internal/SingletonCosmosClientWrapper.cs @@ -41,6 +41,11 @@ public SingletonCosmosClientWrapper(ICosmosSingletonOptions options) configuration.ApplicationRegion = options.Region; } + if (options.PreferredRegions != null) + { + configuration.ApplicationPreferredRegions = options.PreferredRegions; + } + if (options.LimitToEndpoint != null) { configuration.LimitToEndpoint = options.LimitToEndpoint.Value; diff --git a/src/EFCore.Design/Extensions/MethodCallCodeFragmentExtensions.cs b/src/EFCore.Design/Extensions/MethodCallCodeFragmentExtensions.cs new file mode 100644 index 00000000000..e1c3e4d54bd --- /dev/null +++ b/src/EFCore.Design/Extensions/MethodCallCodeFragmentExtensions.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Design; + +/// +/// Design-time extensions. +/// +public static class MethodCallCodeFragmentExtensions +{ + /// + /// Gets the using statements required for this method call. + /// + /// The method call. + /// The usings. + public static IEnumerable GetRequiredUsings(this MethodCallCodeFragment methodCall) + { + var method = methodCall.MethodInfo; + if (method?.IsStatic == true) + { + yield return method.DeclaringType!.Namespace!; + } + + foreach (var argument in methodCall.Arguments) + { + if (argument is NestedClosureCodeFragment nestedClosure) + { + foreach (var nestedUsing in nestedClosure.MethodCalls.SelectMany(GetRequiredUsings)) + { + yield return nestedUsing; + } + } + else if (argument is not null) + { + yield return argument.GetType().Namespace!; + } + } + } +} diff --git a/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs b/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs index 7092724682c..365a06a3f97 100644 --- a/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs +++ b/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs @@ -576,7 +576,8 @@ protected virtual void GenerateComplexProperty( { stringBuilder .AppendLine() - .Append(".IsRequired()"); + .Append(complexTypeBuilderName) + .AppendLine(".IsRequired();"); } GenerateProperties(complexTypeBuilderName, complexType.GetDeclaredProperties(), stringBuilder); diff --git a/src/EFCore.Design/Query/Internal/LinqToCSharpSyntaxTranslator.cs b/src/EFCore.Design/Query/Internal/LinqToCSharpSyntaxTranslator.cs index c29f44c1e4e..32e50b6be63 100644 --- a/src/EFCore.Design/Query/Internal/LinqToCSharpSyntaxTranslator.cs +++ b/src/EFCore.Design/Query/Internal/LinqToCSharpSyntaxTranslator.cs @@ -882,7 +882,7 @@ protected override Expression VisitConstant(ConstantExpression constant) ExpressionSyntax GenerateValue(object? value) => value switch { - int or long or uint or ulong or short or sbyte or ushort or byte or double or float or decimal + int or long or uint or ulong or short or sbyte or ushort or byte or double or float or decimal or char => (ExpressionSyntax)_g.LiteralExpression(constant.Value), string or bool or null => (ExpressionSyntax)_g.LiteralExpression(constant.Value), diff --git a/src/EFCore.Design/README.md b/src/EFCore.Design/README.md new file mode 100644 index 00000000000..ba2b49b132b --- /dev/null +++ b/src/EFCore.Design/README.md @@ -0,0 +1,45 @@ +The Entity Framework Core tools help with design-time development tasks. They're primarily used to manage Migrations and to scaffold a DbContext and entity types by reverse engineering the schema of a database. +Microsoft.EntityFrameworkCore.Design is for cross-platform command line tooling. + +## Getting started + +`Microsoft.EntityFrameworkCore.Design` contains all the design-time logic for Entity Framework Core. It's the code that all of the various tools (PMC cmdlets like `Add-Migration`, `dotnet ef` & `ef.exe`) call into. + +If you don't use Migrations or Reverse Engineering, you don't need it. + +And when you do need it, we encourage `PrivateAssets="All" `so it doesn't get published to the server where you almost certainly won't need it. + +### Prerequisites + +Before using the tools: + +- [Understand the difference between target and startup project](https://learn.microsoft.com/en-us/ef/core/cli/powershell#target-and-startup-project). +- [Learn how to use the tools with .NET Standard class libraries](https://learn.microsoft.com/en-us/ef/core/cli/powershell#other-target-frameworks). +- [For ASP.NET Core projects, set the environment](https://learn.microsoft.com/en-us/ef/core/cli/powershell#aspnet-core-environment). + +## Usage + +PMC Command | Usage +-- | -- +Get-Help entityframework |Displays information about entity framework commands. +[Add-Migration](https://learn.microsoft.com/en-us/ef/core/cli/powershell#add-migration) | Creates a migration by adding a migration snapshot. +[Bundle-Migration](https://learn.microsoft.com/en-us/ef/core/cli/powershell#bundle-migration) | Creates an executable to update the database. +[Get-DbContext](https://learn.microsoft.com/en-us/ef/core/cli/powershell#get-dbcontext) | Gets information about a DbContext type. +[Drop-Database](https://learn.microsoft.com/en-us/ef/core/cli/powershell#drop-database) | Drops the database. +[Get-Migration](https://learn.microsoft.com/en-us/ef/core/cli/powershell#get-migration) | Lists available migrations. +[Optimize-DbContext](https://learn.microsoft.com/en-us/ef/core/cli/powershell#optimize-dbcontext) | Generates a compiled version of the model used by the `DbContext`. +[Remove-Migration](https://learn.microsoft.com/en-us/ef/core/cli/powershell#remove-migration) | Removes the last migration snapshot. +[Scaffold-DbContext](https://learn.microsoft.com/en-us/ef/core/cli/powershell#scaffold-dbcontext) | Generates a DbContext and entity type classes for a specified database. This is called reverse engineering. +[Script-DbContext](https://learn.microsoft.com/en-us/ef/core/cli/powershell#script-dbcontext) | Generates a SQL script from the DbContext. Bypasses any migrations. +[Script-Migration](https://learn.microsoft.com/en-us/ef/core/cli/powershell#script-migration) | Generates a SQL script using all the migration snapshots. +[Update-Database](https://learn.microsoft.com/en-us/ef/core/cli/powershell#update-database) | Updates the database schema based on the last migration snapshot. + +## Additional documentation + +- [Migrations](https://learn.microsoft.com/en-us/ef/core/managing-schemas/migrations/) +- [Reverse Engineering](https://learn.microsoft.com/en-us/ef/core/managing-schemas/scaffolding/?tabs=dotnet-core-cli) +- [Compiled models](https://learn.microsoft.com/en-us/ef/core/performance/advanced-performance-topics?tabs=with-di%2Cwith-constant#compiled-models) + +## Feedback + +If you encounter a bug or issues with this package,you can [open an Github issue](https://github.com/dotnet/efcore/issues/new/choose). For more details, see [getting support](https://github.com/dotnet/efcore/blob/main/.github/SUPPORT.md). \ No newline at end of file diff --git a/src/EFCore.Design/Scaffolding/Internal/CSharpDbContextGenerator.cs b/src/EFCore.Design/Scaffolding/Internal/CSharpDbContextGenerator.cs index 79dd3634758..c28d92bf0a0 100644 --- a/src/EFCore.Design/Scaffolding/Internal/CSharpDbContextGenerator.cs +++ b/src/EFCore.Design/Scaffolding/Internal/CSharpDbContextGenerator.cs @@ -101,8 +101,11 @@ public virtual string TransformText() } + var useProviderCall = providerCode.GenerateUseProvider(Options.ConnectionString); + usings.AddRange(useProviderCall.GetRequiredUsings()); + this.Write(" => optionsBuilder"); - this.Write(this.ToStringHelper.ToStringWithCulture(code.Fragment(providerCode.GenerateUseProvider(Options.ConnectionString), indent: 3))); + this.Write(this.ToStringHelper.ToStringWithCulture(code.Fragment(useProviderCall, indent: 3))); this.Write(";\r\n\r\n"); } @@ -617,7 +620,7 @@ public class CSharpDbContextGeneratorBase /// /// The string builder that generation-time code is using to assemble generated output /// - protected System.Text.StringBuilder GenerationEnvironment + public System.Text.StringBuilder GenerationEnvironment { get { diff --git a/src/EFCore.Design/Scaffolding/Internal/CSharpDbContextGenerator.tt b/src/EFCore.Design/Scaffolding/Internal/CSharpDbContextGenerator.tt index 80e3c9eca95..0741e40e6b9 100644 --- a/src/EFCore.Design/Scaffolding/Internal/CSharpDbContextGenerator.tt +++ b/src/EFCore.Design/Scaffolding/Internal/CSharpDbContextGenerator.tt @@ -78,8 +78,11 @@ public partial class <#= Options.ContextName #> : DbContext #warning 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. <# } + + var useProviderCall = providerCode.GenerateUseProvider(Options.ConnectionString); + usings.AddRange(useProviderCall.GetRequiredUsings()); #> - => optionsBuilder<#= code.Fragment(providerCode.GenerateUseProvider(Options.ConnectionString), indent: 3) #>; + => optionsBuilder<#= code.Fragment(useProviderCall, indent: 3) #>; <# } diff --git a/src/EFCore.InMemory/Query/Internal/InMemoryExpressionTranslatingExpressionVisitor.cs b/src/EFCore.InMemory/Query/Internal/InMemoryExpressionTranslatingExpressionVisitor.cs index b6c4f0131db..f649b462a92 100644 --- a/src/EFCore.InMemory/Query/Internal/InMemoryExpressionTranslatingExpressionVisitor.cs +++ b/src/EFCore.InMemory/Query/Internal/InMemoryExpressionTranslatingExpressionVisitor.cs @@ -1288,7 +1288,7 @@ private bool TryRewriteContainsEntity(Expression? source, Expression item, [NotN var propertyGetter = property.GetGetter(); foreach (var value in values) { - propertyValueList.Add(propertyGetter.GetStructuralTypeClrValue(value)); + propertyValueList.Add(propertyGetter.GetClrValue(value)); } rewrittenSource = Expression.Constant(propertyValueList); @@ -1436,7 +1436,7 @@ private Expression CreatePropertyAccessExpression(Expression target, IProperty p return Expression.Constant( constantExpression.Value is null ? null - : property.GetGetter().GetStructuralTypeClrValue(constantExpression.Value), + : property.GetGetter().GetClrValue(constantExpression.Value), property.ClrType.MakeNullable()); case MethodCallExpression { Method.IsGenericMethod: true } methodCallExpression @@ -1479,7 +1479,7 @@ when CanEvaluate(memberInitExpression): private static T? ParameterValueExtractor(QueryContext context, string baseParameterName, IProperty property) { var baseParameter = context.ParameterValues[baseParameterName]; - return baseParameter == null ? (T?)(object?)null : (T?)property.GetGetter().GetStructuralTypeClrValue(baseParameter); + return baseParameter == null ? (T?)(object?)null : (T?)property.GetGetter().GetClrValue(baseParameter); } private static List? ParameterListValueExtractor( @@ -1493,7 +1493,7 @@ when CanEvaluate(memberInitExpression): } var getter = property.GetGetter(); - return baseListParameter.Select(e => e != null ? (TProperty?)getter.GetStructuralTypeClrValue(e) : (TProperty?)(object?)null).ToList(); + return baseListParameter.Select(e => e != null ? (TProperty?)getter.GetClrValue(e) : (TProperty?)(object?)null).ToList(); } private static ConstantExpression GetValue(Expression expression) diff --git a/src/EFCore.InMemory/Storage/Internal/InMemoryTypeMapping.cs b/src/EFCore.InMemory/Storage/Internal/InMemoryTypeMapping.cs index bfd8126ac82..34bd3b51288 100644 --- a/src/EFCore.InMemory/Storage/Internal/InMemoryTypeMapping.cs +++ b/src/EFCore.InMemory/Storage/Internal/InMemoryTypeMapping.cs @@ -53,12 +53,13 @@ private InMemoryTypeMapping(CoreTypeMappingParameters parameters) /// 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. /// - public override CoreTypeMapping Clone( + public override CoreTypeMapping WithComposedConverter( ValueConverter? converter, ValueComparer? comparer = null, + ValueComparer? keyComparer = null, CoreTypeMapping? elementMapping = null, JsonValueReaderWriter? jsonValueReaderWriter = null) - => new InMemoryTypeMapping(Parameters.WithComposedConverter(converter, comparer, elementMapping, jsonValueReaderWriter)); + => new InMemoryTypeMapping(Parameters.WithComposedConverter(converter, comparer, keyComparer, elementMapping, jsonValueReaderWriter)); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to diff --git a/src/EFCore.InMemory/Storage/Internal/InMemoryTypeMappingSource.cs b/src/EFCore.InMemory/Storage/Internal/InMemoryTypeMappingSource.cs index cff9a52809d..f5e83539fb6 100644 --- a/src/EFCore.InMemory/Storage/Internal/InMemoryTypeMappingSource.cs +++ b/src/EFCore.InMemory/Storage/Internal/InMemoryTypeMappingSource.cs @@ -37,7 +37,7 @@ public InMemoryTypeMappingSource(TypeMappingSourceDependencies dependencies) if (clrType.IsValueType || clrType == typeof(string) - || clrType == typeof(byte[])) + || (clrType == typeof(byte[]) && mappingInfo.ElementTypeMapping == null)) { return new InMemoryTypeMapping( clrType, jsonValueReaderWriter: jsonValueReaderWriter); diff --git a/src/EFCore.Proxies/Proxies/Internal/PropertyChangedInterceptor.cs b/src/EFCore.Proxies/Proxies/Internal/PropertyChangedInterceptor.cs index f67e5fc52a0..2637f9e07c8 100644 --- a/src/EFCore.Proxies/Proxies/Internal/PropertyChangedInterceptor.cs +++ b/src/EFCore.Proxies/Proxies/Internal/PropertyChangedInterceptor.cs @@ -94,7 +94,7 @@ private void HandleChanged(IInvocation invocation, IPropertyBase property, IEqua if (_checkEquality) { - var oldValue = property.GetGetter().GetClrValue(invocation.Proxy); + var oldValue = property.GetGetter().GetClrValueUsingContainingEntity(invocation.Proxy); invocation.Proceed(); diff --git a/src/EFCore.Proxies/Proxies/Internal/PropertyChangingInterceptor.cs b/src/EFCore.Proxies/Proxies/Internal/PropertyChangingInterceptor.cs index 670639d5369..ed1cfe38e51 100644 --- a/src/EFCore.Proxies/Proxies/Internal/PropertyChangingInterceptor.cs +++ b/src/EFCore.Proxies/Proxies/Internal/PropertyChangingInterceptor.cs @@ -92,7 +92,7 @@ private void HandleChanging(IInvocation invocation, IPropertyBase property, IEqu { if (_checkEquality) { - var oldValue = property.GetGetter().GetClrValue(invocation.Proxy); + var oldValue = property.GetGetter().GetClrValueUsingContainingEntity(invocation.Proxy); var newValue = invocation.Arguments[^1]; if (!(comparer?.Equals(oldValue, newValue) ?? Equals(oldValue, newValue))) diff --git a/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs b/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs index fe28ee86dc2..aafeac6f7f8 100644 --- a/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs +++ b/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs @@ -61,6 +61,12 @@ public static class RelationalEntityTypeExtensions return entityType.GetRootType().GetTableName(); } + if (entityType.GetMappingStrategy() == RelationalAnnotationNames.TpcMappingStrategy + && !entityType.ClrType.IsInstantiable()) + { + return null; + } + var ownership = entityType.FindOwnership(); if (ownership != null && (ownership.IsUnique || entityType.IsMappedToJson())) @@ -81,12 +87,6 @@ public static class RelationalEntityTypeExtensions : $"{ownership.PrincipalToDependent.Name}_{name}"; } - if (entityType.GetMappingStrategy() == RelationalAnnotationNames.TpcMappingStrategy - && !entityType.ClrType.IsInstantiable()) - { - return null; - } - return truncate ? Uniquifier.Truncate(name, entityType.Model.GetMaxIdentifierLength()) : name; @@ -1379,13 +1379,16 @@ public static bool IsTableExcludedFromMigrations(this IReadOnlyEntityType entity return excluded.Value; } - if (entityType.BaseType != null) + if (entityType.BaseType != null + && entityType.GetMappingStrategy() == RelationalAnnotationNames.TphMappingStrategy) { return entityType.GetRootType().IsTableExcludedFromMigrations(); } var ownership = entityType.FindOwnership(); - if (ownership is { IsUnique: true }) + if (ownership is { IsUnique: true } + && ownership.DeclaringEntityType.GetTableName() == entityType.GetTableName() + && ownership.DeclaringEntityType.GetSchema() == entityType.GetSchema()) { return ownership.PrincipalEntityType.IsTableExcludedFromMigrations(); } diff --git a/src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs b/src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs index 187f3297208..157dc23d827 100644 --- a/src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs +++ b/src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs @@ -1145,7 +1145,7 @@ public static void SetIsFixedLength(this IMutableProperty property, bool? fixedL /// if the mapped column is nullable; otherwise. public static bool IsColumnNullable(this IReadOnlyProperty property) => property.IsNullable - || (property.DeclaringType is IReadOnlyEntityType entityType + || (property.DeclaringType.ContainingEntityType is IReadOnlyEntityType entityType && entityType.BaseType != null && entityType.GetMappingStrategy() == RelationalAnnotationNames.TphMappingStrategy); diff --git a/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs b/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs index 47251be8f88..a14998c9fd4 100644 --- a/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs +++ b/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs @@ -558,7 +558,7 @@ protected virtual IEnumerable Diff( DiffContext diffContext) { if (source.IsExcludedFromMigrations - && target.IsExcludedFromMigrations) + || target.IsExcludedFromMigrations) { // Populate column mapping foreach (var _ in Diff(source.Columns, target.Columns, diffContext)) diff --git a/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs b/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs index 2bc8f88fe1c..2b9713407ab 100644 --- a/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs +++ b/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs @@ -53,6 +53,12 @@ public static string BadSequenceType public static string CannotChangeWhenOpen => GetString("CannotChangeWhenOpen"); + /// + /// Comparing complex types to null is not supported. + /// + public static string CannotCompareComplexTypeToNull + => GetString("CannotCompareComplexTypeToNull"); + /// /// Can't configure a trigger on entity type '{entityType}', which is in a TPH hierarchy and isn't the root. Configure the trigger on the TPH root entity type '{rootEntityType}' instead. /// diff --git a/src/EFCore.Relational/Properties/RelationalStrings.resx b/src/EFCore.Relational/Properties/RelationalStrings.resx index 9f1db59cbb4..f426a92d9d0 100644 --- a/src/EFCore.Relational/Properties/RelationalStrings.resx +++ b/src/EFCore.Relational/Properties/RelationalStrings.resx @@ -130,6 +130,9 @@ The instance of DbConnection is currently in use. The connection can only be changed when the existing connection is not being used. + + Comparing complex types to null is not supported. + Can't configure a trigger on entity type '{entityType}', which is in a TPH hierarchy and isn't the root. Configure the trigger on the TPH root entity type '{rootEntityType}' instead. diff --git a/src/EFCore.Relational/Query/Internal/QueryableJsonProjectionInfo.cs b/src/EFCore.Relational/Query/Internal/QueryableJsonProjectionInfo.cs new file mode 100644 index 00000000000..12b168b43c3 --- /dev/null +++ b/src/EFCore.Relational/Query/Internal/QueryableJsonProjectionInfo.cs @@ -0,0 +1,51 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Internal; + +/// +/// 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. +/// +public readonly struct QueryableJsonProjectionInfo +{ + /// + /// 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. + /// + public QueryableJsonProjectionInfo( + Dictionary propertyIndexMap, + List<(JsonProjectionInfo, INavigation)> childrenProjectionInfo) + { + PropertyIndexMap = propertyIndexMap; + ChildrenProjectionInfo = childrenProjectionInfo; + } + + /// + /// Map between entity properties and corresponding column indexes. + /// + /// + /// 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. + /// + public IDictionary PropertyIndexMap { get; } + + /// + /// Information needed to construct each child JSON entity. + /// - JsonProjection info (same one we use for simple JSON projection), + /// - navigation between parent and the child JSON entity. + /// + /// + /// 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. + /// + public IList<(JsonProjectionInfo JsonProjectionInfo, INavigation Navigation)> ChildrenProjectionInfo { get; } +} diff --git a/src/EFCore.Relational/Query/Internal/RelationalProjectionBindingExpressionVisitor.cs b/src/EFCore.Relational/Query/Internal/RelationalProjectionBindingExpressionVisitor.cs index e5e261e048c..e12b70645f4 100644 --- a/src/EFCore.Relational/Query/Internal/RelationalProjectionBindingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/Internal/RelationalProjectionBindingExpressionVisitor.cs @@ -182,15 +182,17 @@ public virtual Expression Translate(SelectExpression selectExpression, Expressio if (expression is MethodCallExpression methodCallExpression) { - if (methodCallExpression.Method.IsGenericMethod - && methodCallExpression.Method.DeclaringType == typeof(Enumerable) - && methodCallExpression.Method.Name == nameof(Enumerable.ToList) - && methodCallExpression.Arguments.Count == 1 - && methodCallExpression.Arguments[0].Type.TryGetElementType(typeof(IQueryable<>)) != null) + if (methodCallExpression is + { + Method.IsGenericMethod: true, + Method.Name: nameof(Enumerable.ToList), + Method: var method, + Arguments: [var argument] + } + && method.DeclaringType == typeof(Enumerable) + && argument.Type.TryGetElementType(typeof(IQueryable<>)) != null) { - var subquery = _queryableMethodTranslatingExpressionVisitor.TranslateSubquery( - methodCallExpression.Arguments[0]); - if (subquery != null) + if (_queryableMethodTranslatingExpressionVisitor.TranslateSubquery(argument) is ShapedQueryExpression subquery) { _clientProjections!.Add(subquery); // expression.Type here will be List diff --git a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs index c2717e60443..837b777070b 100644 --- a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs @@ -266,14 +266,9 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp { // Attempt to translate access into a primitive collection property (i.e. array column) - // TODO: We should be detecting primitive collections by looking at GetElementType() of the property and not at its type - // mapping; but #31469 is blocking that for shadow properties. if (_sqlTranslator.TryTranslatePropertyAccess(methodCallExpression, out var translatedExpression, out var property) - && property is IProperty regularProperty - && translatedExpression is SqlExpression - { - TypeMapping.ElementTypeMapping: RelationalTypeMapping - } sqlExpression) + && property is IProperty { IsPrimitiveCollection: true } regularProperty + && translatedExpression is SqlExpression sqlExpression) { var tableAlias = sqlExpression switch { @@ -2309,10 +2304,8 @@ static TableExpressionBase FindRootTableExpressionForColumn(ColumnExpression col } } - // TODO: Check that the property is a primitive collection property directly once we have that in metadata, rather than - // looking at the type mapping. var property = type.FindProperty(memberName); - if (property?.GetRelationalTypeMapping().ElementTypeMapping is null) + if (property?.IsPrimitiveCollection != true) { return null; } diff --git a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.cs index 8d250d96c18..c5b89fc588f 100644 --- a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.cs @@ -433,7 +433,11 @@ protected override Expression VisitBinary(BinaryExpression binaryExpression) if (newExpression.Arguments[0] is ProjectionBindingExpression projectionBindingExpression) { - var propertyMap = (IDictionary)GetProjectionIndex(projectionBindingExpression); + var projectionIndex = GetProjectionIndex(projectionBindingExpression); + var propertyMap = projectionIndex is IDictionary + ? (IDictionary)projectionIndex + : ((QueryableJsonProjectionInfo)projectionIndex).PropertyIndexMap; + _materializationContextBindings[parameterExpression] = propertyMap; _entityTypeIdentifyingExpressionInfo[parameterExpression] = // If single entity type is being selected in hierarchy then we use the value directly else we store the offset @@ -535,6 +539,50 @@ protected override Expression VisitExtension(Expression extensionExpression) visitedShaperResultParameter, shaper.Type); } + else if (GetProjectionIndex(projectionBindingExpression) is QueryableJsonProjectionInfo queryableJsonEntityProjectionInfo) + { + if (_isTracking) + { + throw new InvalidOperationException( + RelationalStrings.JsonEntityOrCollectionProjectedAtRootLevelInTrackingQuery(nameof(EntityFrameworkQueryableExtensions.AsNoTracking))); + } + + // json entity converted to query root and projected + var entityParameter = Parameter(shaper.Type); + _variables.Add(entityParameter); + var entityMaterializationExpression = (BlockExpression)_parentVisitor.InjectEntityMaterializers(shaper); + + var mappedProperties = queryableJsonEntityProjectionInfo.PropertyIndexMap.Keys.ToList(); + var rewrittenEntityMaterializationExpression = new QueryableJsonEntityMaterializerRewriter(mappedProperties) + .Rewrite(entityMaterializationExpression); + + var visitedEntityMaterializationExpression = Visit(rewrittenEntityMaterializationExpression); + _expressions.Add(Assign(entityParameter, visitedEntityMaterializationExpression)); + + foreach (var childProjectionInfo in queryableJsonEntityProjectionInfo.ChildrenProjectionInfo) + { + var (jsonReaderDataVariable, keyValuesParameter) = JsonShapingPreProcess( + childProjectionInfo.JsonProjectionInfo, + childProjectionInfo.Navigation.TargetEntityType, + childProjectionInfo.Navigation.IsCollection); + + var shaperResult = CreateJsonShapers( + childProjectionInfo.Navigation.TargetEntityType, + nullable: true, + jsonReaderDataVariable, + keyValuesParameter, + parentEntityExpression: entityParameter, + navigation: childProjectionInfo.Navigation); + + var visitedShaperResult = Visit(shaperResult); + + _includeExpressions.Add(visitedShaperResult); + } + + accessor = CompensateForCollectionMaterialization( + entityParameter, + shaper.Type); + } else { var entityParameter = Parameter(shaper.Type); @@ -1473,6 +1521,39 @@ protected override Expression VisitSwitch(SwitchExpression switchExpression) //sometimes we have shadow value buffer and sometimes not, but type initializer always comes last switch (body.Expressions[^1]) { + case UnaryExpression { Operand: BlockExpression innerBlock } jsonEntityTypeInitializerUnary + when jsonEntityTypeInitializerUnary.NodeType is ExpressionType.Convert or ExpressionType.ConvertChecked: + { + // in case of proxies, the entity initializer block is wrapped around Convert node + // that converts from the proxy type to the actual entity type. + // We normalize that into a block by pushing the convert inside the inner block. Rather than: + // + // return (MyEntity) + // { + // ProxyEntity instance; + // (...) + // return instance; + // } + // + // we produce: + // return + // { + // ProxyEntity instance; + // MyEntity actualInstance; + // (...) + // actualInstance = (MyEntity)instance; + // return actualInstance; + // } + var newVariables = innerBlock.Variables.ToList(); + var proxyConversionVariable = Variable(jsonEntityTypeInitializerUnary.Type); + newVariables.Add(proxyConversionVariable); + var newExpressions = innerBlock.Expressions.ToList()[..^1]; + newExpressions.Add(Assign(proxyConversionVariable, jsonEntityTypeInitializerUnary.Update(innerBlock.Expressions[^1]))); + newExpressions.Add(proxyConversionVariable); + jsonEntityTypeInitializerBlock = Block(newVariables, newExpressions); + break; + } + case BlockExpression b: jsonEntityTypeInitializerBlock = b; break; @@ -1922,15 +2003,14 @@ protected override Expression VisitBinary(BinaryExpression node) if (node.Right is MethodCallExpression methodCallExpression && IsPropertyAssignment(methodCallExpression, out var property, out var parameter)) { - if (property!.GetTypeMapping().ElementTypeMapping != null + if (property!.IsPrimitiveCollection && !property.ClrType.IsArray) { var currentVariable = Variable(parameter!.Type); return Block( new[] { currentVariable }, - Assign( - currentVariable, - MakeMemberAccess(_instance, property.GetMemberInfo(forMaterialization: true, forSet: false))), + MakeMemberAccess(_instance, property.GetMemberInfo(forMaterialization: true, forSet: false)) + .Assign(currentVariable), IfThenElse( OrElse( ReferenceEqual(currentVariable, Constant(null)), @@ -1943,7 +2023,11 @@ protected override Expression VisitBinary(BinaryExpression node) )); } - return MakeBinary(node.NodeType, Visit(node.Left), parameter!); + var visitedLeft = Visit(node.Left); + return node.NodeType == ExpressionType.Assign + && visitedLeft is MemberExpression memberExpression + ? memberExpression.Assign(parameter!) + : MakeBinary(node.NodeType, visitedLeft, parameter!); } return base.VisitBinary(node); @@ -2141,6 +2225,62 @@ ParameterExpression ExtractAndCacheNonConstantJsonArrayElementAccessValue(int in } } + private sealed class QueryableJsonEntityMaterializerRewriter : ExpressionVisitor + { + private readonly List _mappedProperties; + + public QueryableJsonEntityMaterializerRewriter(List mappedProperties) + { + _mappedProperties = mappedProperties; + } + + public BlockExpression Rewrite(BlockExpression jsonEntityShaperMaterializer) + => (BlockExpression)VisitBlock(jsonEntityShaperMaterializer); + + protected override Expression VisitBinary(BinaryExpression binaryExpression) + { + // here we try to pattern match part of the shaper code that checks if key values are null + // if they are all non-null then we generate the entity + // problem for JSON entities is that some of the keys are synthesized and should be omitted + // if the key is one of the mapped ones, we leave the expression as is, otherwise replace with Constant(true) + // i.e. removing it + if (binaryExpression is + { + NodeType: ExpressionType.NotEqual, + Left: MethodCallExpression + { + Method: { IsGenericMethod: true } method, + Arguments: [_, _, ConstantExpression { Value: IProperty property }] + }, + Right: ConstantExpression { Value: null } + } + && method.GetGenericMethodDefinition() == Infrastructure.ExpressionExtensions.ValueBufferTryReadValueMethod) + { + return _mappedProperties.Contains(property) + ? binaryExpression + : Constant(true); + } + + return base.VisitBinary(binaryExpression); + } + + protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression) + { + if (methodCallExpression is + { + Method: { IsGenericMethod: true } method, + Arguments: [_, _, ConstantExpression { Value: IProperty property }] + } + && method.GetGenericMethodDefinition() == Infrastructure.ExpressionExtensions.ValueBufferTryReadValueMethod + && !_mappedProperties.Contains(property)) + { + return Default(methodCallExpression.Type); + } + + return base.VisitMethodCall(methodCallExpression); + } + } + private static LambdaExpression GenerateFixup( Type entityType, Type relatedEntityType, diff --git a/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs index 46309019c68..d9e35874574 100644 --- a/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs @@ -1684,7 +1684,7 @@ private bool TryRewriteContainsEntity(Expression source, Expression item, [NotNu var propertyGetter = property.GetGetter(); foreach (var value in values) { - propertyValueList.Add(propertyGetter.GetStructuralTypeClrValue(value)); + propertyValueList.Add(propertyGetter.GetClrValue(value)); } rewrittenSource = Expression.Constant(propertyValueList); @@ -1791,19 +1791,23 @@ bool TryRewriteEntityEquality([NotNullWhen(true)] out Expression? result) if (allNonPrincipalSharedNonPkProperties.Count != 0 && allNonPrincipalSharedNonPkProperties.All(p => p.IsNullable)) { - var atLeastOneNonNullValueInNullablePropertyCondition = allNonPrincipalSharedNonPkProperties + // if we don't have any required properties to properly check the nullability, + // we rely on optional ones (somewhat unreliably) + // - if entity is to be null, all the properties must be null + // - if the entity is to be not null, at least one property must be not null + var optionalPropertiesCondition = allNonPrincipalSharedNonPkProperties .Select( p => Infrastructure.ExpressionExtensions.CreateEqualsExpression( CreatePropertyAccessExpression(nonNullEntityReference, p), Expression.Constant(null, p.ClrType.MakeNullable()), nodeType != ExpressionType.Equal)) - .Aggregate((l, r) => nodeType == ExpressionType.Equal ? Expression.OrElse(l, r) : Expression.AndAlso(l, r)); + .Aggregate((l, r) => nodeType == ExpressionType.Equal ? Expression.AndAlso(l, r) : Expression.OrElse(l, r)); condition = condition == null - ? atLeastOneNonNullValueInNullablePropertyCondition + ? optionalPropertiesCondition : nodeType == ExpressionType.Equal - ? Expression.OrElse(condition, atLeastOneNonNullValueInNullablePropertyCondition) - : Expression.AndAlso(condition, atLeastOneNonNullValueInNullablePropertyCondition); + ? Expression.OrElse(condition, optionalPropertiesCondition) + : Expression.AndAlso(condition, optionalPropertiesCondition); } if (condition != null) @@ -1890,7 +1894,7 @@ bool TryRewriteComplexTypeEquality([NotNullWhen(true)] out Expression? result) { // TODO: when we support optional complex types - or projecting required complex types via optional navigations - we'll // be able to translate this. - throw new InvalidOperationException(); // TODO: Message + throw new InvalidOperationException(RelationalStrings.CannotCompareComplexTypeToNull); } var leftComplexType = leftReference?.StructuralType as IComplexType; @@ -1974,14 +1978,14 @@ private Expression CreatePropertyAccessExpression(Expression target, IProperty p return Expression.Constant( sqlConstantExpression.Value is null ? null - : property.GetGetter().GetStructuralTypeClrValue(sqlConstantExpression.Value), + : property.GetGetter().GetClrValue(sqlConstantExpression.Value), property.ClrType.MakeNullable()); case ConstantExpression sqlConstantExpression: return Expression.Constant( sqlConstantExpression.Value is null ? null - : property.GetGetter().GetStructuralTypeClrValue(sqlConstantExpression.Value), + : property.GetGetter().GetClrValue(sqlConstantExpression.Value), property.ClrType.MakeNullable()); case SqlParameterExpression sqlParameterExpression @@ -2035,7 +2039,7 @@ private Expression CreateComplexPropertyAccessExpression(Expression target, ICom => target switch { SqlConstantExpression constant => Expression.Constant( - constant.Value is null ? null : complexProperty.GetGetter().GetStructuralTypeClrValue(constant.Value), + constant.Value is null ? null : complexProperty.GetGetter().GetClrValue(constant.Value), complexProperty.ClrType.MakeNullable()), SqlParameterExpression sqlParameterExpression @@ -2070,11 +2074,11 @@ when memberInitExpression.Bindings.SingleOrDefault(mb => mb.Member.Name == compl break; } - baseValue = complexProperty.GetGetter().GetStructuralTypeClrValue(baseValue); + baseValue = complexProperty.GetGetter().GetClrValue(baseValue); } } - return baseValue == null ? (T?)(object?)null : (T?)property.GetGetter().GetStructuralTypeClrValue(baseValue); + return baseValue == null ? (T?)(object?)null : (T?)property.GetGetter().GetClrValue(baseValue); } private static List? ParameterListValueExtractor( @@ -2088,7 +2092,7 @@ when memberInitExpression.Bindings.SingleOrDefault(mb => mb.Member.Name == compl } var getter = property.GetGetter(); - return baseListParameter.Select(e => e != null ? (TProperty?)getter.GetStructuralTypeClrValue(e) : (TProperty?)(object?)null).ToList(); + return baseListParameter.Select(e => e != null ? (TProperty?)getter.GetClrValue(e) : (TProperty?)(object?)null).ToList(); } private sealed class ParameterBasedComplexPropertyChainExpression : Expression diff --git a/src/EFCore.Relational/Query/SqlExpressions/CrossApplyExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/CrossApplyExpression.cs index 9f58ec09b4b..c1ba6d964eb 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/CrossApplyExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/CrossApplyExpression.cs @@ -38,7 +38,7 @@ protected override Expression VisitChildren(ExpressionVisitor visitor) /// /// The property of the result. /// This expression if no children changed, or an expression with the updated children. - public virtual CrossApplyExpression Update(TableExpressionBase table) + public override CrossApplyExpression Update(TableExpressionBase table) => table != Table ? new CrossApplyExpression(table, GetAnnotations()) : this; diff --git a/src/EFCore.Relational/Query/SqlExpressions/CrossJoinExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/CrossJoinExpression.cs index c5e6c4eef09..89f87da89dc 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/CrossJoinExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/CrossJoinExpression.cs @@ -38,7 +38,7 @@ protected override Expression VisitChildren(ExpressionVisitor visitor) /// /// The property of the result. /// This expression if no children changed, or an expression with the updated children. - public virtual CrossJoinExpression Update(TableExpressionBase table) + public override CrossJoinExpression Update(TableExpressionBase table) => table != Table ? new CrossJoinExpression(table, GetAnnotations()) : this; diff --git a/src/EFCore.Relational/Query/SqlExpressions/InnerJoinExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/InnerJoinExpression.cs index dc4fc43fa47..f3287684c79 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/InnerJoinExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/InnerJoinExpression.cs @@ -48,11 +48,22 @@ protected override Expression VisitChildren(ExpressionVisitor visitor) /// The property of the result. /// The property of the result. /// This expression if no children changed, or an expression with the updated children. - public virtual InnerJoinExpression Update(TableExpressionBase table, SqlExpression joinPredicate) + public override InnerJoinExpression Update(TableExpressionBase table, SqlExpression joinPredicate) => table != Table || joinPredicate != JoinPredicate ? new InnerJoinExpression(table, joinPredicate, GetAnnotations()) : this; + /// + /// Creates a new expression that is like this one, but using the supplied children. If all of the children are the same, it will + /// return this expression. + /// + /// The property of the result. + /// This expression if no children changed, or an expression with the updated children. + public override InnerJoinExpression Update(TableExpressionBase table) + => table != Table + ? new InnerJoinExpression(table, JoinPredicate, GetAnnotations()) + : this; + /// protected override TableExpressionBase CreateWithAnnotations(IEnumerable annotations) => new InnerJoinExpression(Table, JoinPredicate, annotations); diff --git a/src/EFCore.Relational/Query/SqlExpressions/JoinExpressionBase.cs b/src/EFCore.Relational/Query/SqlExpressions/JoinExpressionBase.cs index 7e5653e041f..15fae8a45db 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/JoinExpressionBase.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/JoinExpressionBase.cs @@ -39,6 +39,14 @@ protected JoinExpressionBase(TableExpressionBase table, IEnumerable /// public virtual TableExpressionBase Table { get; } + /// + /// Creates a new expression that is like this one, but using the supplied children. If all of the children are the same, it will + /// return this expression. + /// + /// The property of the result. + /// This expression if no children changed, or an expression with the updated children. + public abstract JoinExpressionBase Update(TableExpressionBase table); + /// public override bool Equals(object? obj) => obj != null diff --git a/src/EFCore.Relational/Query/SqlExpressions/LeftJoinExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/LeftJoinExpression.cs index ca8e42132ec..b0eceb1e0a2 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/LeftJoinExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/LeftJoinExpression.cs @@ -48,11 +48,22 @@ protected override Expression VisitChildren(ExpressionVisitor visitor) /// The property of the result. /// The property of the result. /// This expression if no children changed, or an expression with the updated children. - public virtual LeftJoinExpression Update(TableExpressionBase table, SqlExpression joinPredicate) + public override LeftJoinExpression Update(TableExpressionBase table, SqlExpression joinPredicate) => table != Table || joinPredicate != JoinPredicate ? new LeftJoinExpression(table, joinPredicate, GetAnnotations()) : this; + /// + /// Creates a new expression that is like this one, but using the supplied children. If all of the children are the same, it will + /// return this expression. + /// + /// The property of the result. + /// This expression if no children changed, or an expression with the updated children. + public override LeftJoinExpression Update(TableExpressionBase table) + => table != Table + ? new LeftJoinExpression(table, JoinPredicate, GetAnnotations()) + : this; + /// protected override TableExpressionBase CreateWithAnnotations(IEnumerable annotations) => new LeftJoinExpression(Table, JoinPredicate, annotations); diff --git a/src/EFCore.Relational/Query/SqlExpressions/OuterApplyExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/OuterApplyExpression.cs index 2cc30f8dda0..7f20fd74494 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/OuterApplyExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/OuterApplyExpression.cs @@ -38,7 +38,7 @@ protected override Expression VisitChildren(ExpressionVisitor visitor) /// /// The property of the result. /// This expression if no children changed, or an expression with the updated children. - public virtual OuterApplyExpression Update(TableExpressionBase table) + public override OuterApplyExpression Update(TableExpressionBase table) => table != Table ? new OuterApplyExpression(table, GetAnnotations()) : this; diff --git a/src/EFCore.Relational/Query/SqlExpressions/PredicateJoinExpressionBase.cs b/src/EFCore.Relational/Query/SqlExpressions/PredicateJoinExpressionBase.cs index 8c40d3043a0..a1df3330052 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/PredicateJoinExpressionBase.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/PredicateJoinExpressionBase.cs @@ -44,6 +44,15 @@ protected PredicateJoinExpressionBase( /// public virtual SqlExpression JoinPredicate { get; } + /// + /// Creates a new expression that is like this one, but using the supplied children. If all of the children are the same, it will + /// return this expression. + /// + /// The property of the result. + /// The property of the result. + /// This expression if no children changed, or an expression with the updated children. + public abstract PredicateJoinExpressionBase Update(TableExpressionBase table, SqlExpression joinPredicate); + /// public override bool Equals(object? obj) => obj != null diff --git a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs index 856a485ec2a..0ad1dfc7ec8 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs @@ -807,6 +807,7 @@ when entityType.IsMappedToJson(): // For JSON entities, the identifier is the key that was generated when we convert from json to query root // (OPENJSON, json_each, etc), but we can't use it for distinct, as it would warp the results. // Instead, we will treat every non-key property as identifier. + foreach (var property in entityType.GetDeclaredProperties().Where(p => !p.IsPrimaryKey())) { typeProjectionIdentifiers.Add(entityProjection.BindProperty(property)); @@ -823,6 +824,7 @@ when entityType.IsMappedToJson(): // We know that there are existing identifiers (see condition above); we know we must have a key since a keyless // entity type would have wiped the identifiers when generating the join. Check.DebugAssert(primaryKey != null, "primary key is null."); + foreach (var property in primaryKey.Properties) { typeProjectionIdentifiers.Add(entityProjection.BindProperty(property)); @@ -1297,15 +1299,7 @@ static void UpdateLimit(SelectExpression selectExpression) case JsonQueryExpression jsonQueryExpression: { - var jsonProjectionResult = AddJsonProjection( - jsonQueryExpression, - jsonScalarToAdd: new JsonScalarExpression( - jsonQueryExpression.JsonColumn, - jsonQueryExpression.Path, - jsonQueryExpression.JsonColumn.Type, - jsonQueryExpression.JsonColumn.TypeMapping!, - jsonQueryExpression.IsNullable)); - + var jsonProjectionResult = AddJsonProjection(jsonQueryExpression); newClientProjections.Add(jsonProjectionResult); clientProjectionIndexMap.Add(newClientProjections.Count - 1); @@ -1765,6 +1759,33 @@ Expression CopyProjectionToOuter(SelectExpression innerSelectExpression, Express projectionIndexMap[jsonProjectionInfo.JsonColumnIndex], newKeyAccessInfo)); } + else if (constantValue is QueryableJsonProjectionInfo queryableJsonProjectionInfo) + { + var newPropertyIndexMap = new Dictionary(queryableJsonProjectionInfo.PropertyIndexMap.Count); + foreach (var (property, value) in queryableJsonProjectionInfo.PropertyIndexMap) + { + newPropertyIndexMap[property] = projectionIndexMap[value]; + } + + var newChildrenProjectionInfo = new List<(JsonProjectionInfo, INavigation)>(); + foreach (var childProjectionInfo in queryableJsonProjectionInfo.ChildrenProjectionInfo) + { + var newKeyAccessInfo = new List<(IProperty?, int?, int?)>(); + foreach (var (keyProperty, constantKeyValue, keyProjectionIndex) in childProjectionInfo.JsonProjectionInfo.KeyAccessInfo) + { + newKeyAccessInfo.Add((keyProperty, constantKeyValue, keyProjectionIndex != null ? projectionIndexMap[keyProjectionIndex.Value] : null)); + } + + newChildrenProjectionInfo.Add( + (new JsonProjectionInfo( + projectionIndexMap[childProjectionInfo.JsonProjectionInfo.JsonColumnIndex], + newKeyAccessInfo), + childProjectionInfo.Navigation)); + } + + remappedConstant = Constant( + new QueryableJsonProjectionInfo(newPropertyIndexMap, newChildrenProjectionInfo)); + } else { remappedConstant = Constant(projectionIndexMap[(int)constantValue]); @@ -1792,14 +1813,7 @@ Expression CopyProjectionToOuter(SelectExpression innerSelectExpression, Express result[projectionMember] = expression switch { StructuralTypeProjectionExpression projection => AddStructuralTypeProjection(projection), - JsonQueryExpression jsonQueryExpression => AddJsonProjection( - jsonQueryExpression, - new JsonScalarExpression( - jsonQueryExpression.JsonColumn, - jsonQueryExpression.Path, - jsonQueryExpression.JsonColumn.Type, - jsonQueryExpression.JsonColumn.TypeMapping!, - jsonQueryExpression.IsNullable)), + JsonQueryExpression jsonQueryExpression => AddJsonProjection(jsonQueryExpression), _ => Constant(AddToProjection((SqlExpression)expression, projectionMember.Last?.Name)) }; } @@ -1817,41 +1831,90 @@ ConstantExpression AddStructuralTypeProjection(StructuralTypeProjectionExpressio throw new InvalidOperationException(RelationalStrings.CannotProjectNullableComplexType(complexType.DisplayName())); } - var projections = new Dictionary(); + // JSON entity that had some query operations applied on it - it has been converted to a query root via OPENJSON/json_each + // so it requires different materialization path than regular entity + // e.g. we need to also add all the child navigations, JSON entity builds all the includes as part of it's own materializer + // rather than relying on IncludeExpressions in the shaper query + // also, we don't want to add projection map for synthesized keys, whereas regular entity needs to project every single property it has + if (projection is { StructuralType: IEntityType entityType } + && entityType.IsMappedToJson()) + { + var propertyIndexMap = new Dictionary(); + var ownerEntity = entityType; - ProcessType(projection); + do + { + var ownership = ownerEntity.FindOwnership(); + if (ownership != null) + { + ownerEntity = ownership.PrincipalEntityType; + } + } + while (ownerEntity.IsMappedToJson()); + + var keyPropertyCount = ownerEntity.FindPrimaryKey()!.Properties.Count; + foreach (var property in entityType.FindPrimaryKey()!.Properties.Take(keyPropertyCount) + .Concat(entityType.GetDeclaredProperties().Where(p => p.GetJsonPropertyName() is not null))) + { + propertyIndexMap[property] = AddToProjection(projection.BindProperty(property), null); + } + + var childrenProjectionInfo = new List<(JsonProjectionInfo, INavigation)>(); + foreach (var ownedNavigation in entityType.GetNavigations().Where( + n => n.TargetEntityType.IsMappedToJson() && n.ForeignKey.IsOwnership && n == n.ForeignKey.PrincipalToDependent)) + { + var jsonQueryExpression = (JsonQueryExpression)projection.BindNavigation(ownedNavigation)!.ValueBufferExpression; + var jsonProjectionInfo = (JsonProjectionInfo)AddJsonProjection(jsonQueryExpression).Value!; + childrenProjectionInfo.Add((jsonProjectionInfo, ownedNavigation)); + } - void ProcessType(StructuralTypeProjectionExpression typeProjection) + return Constant(new QueryableJsonProjectionInfo(propertyIndexMap, childrenProjectionInfo)); + } + else { - foreach (var property in GetAllPropertiesInHierarchy(typeProjection.StructuralType)) + var projections = new Dictionary(); + + ProcessType(projection); + + void ProcessType(StructuralTypeProjectionExpression typeProjection) { - if (typeProjection is { StructuralType: IEntityType entityType } - && entityType.IsMappedToJson() - && property.IsOrdinalKeyProperty()) + foreach (var property in GetAllPropertiesInHierarchy(typeProjection.StructuralType)) { - continue; + if (typeProjection is { StructuralType: IEntityType entityType } + && entityType.IsMappedToJson() + && property.IsOrdinalKeyProperty()) + { + continue; + } + + projections[property] = AddToProjection(typeProjection.BindProperty(property), alias: null); } - projections[property] = AddToProjection(typeProjection.BindProperty(property), alias: null); + foreach (var complexProperty in GetAllComplexPropertiesInHierarchy(typeProjection.StructuralType)) + { + ProcessType((StructuralTypeProjectionExpression)typeProjection.BindComplexProperty(complexProperty).ValueBufferExpression); + } } - foreach (var complexProperty in GetAllComplexPropertiesInHierarchy(typeProjection.StructuralType)) + if (projection.DiscriminatorExpression is not null) { - ProcessType((StructuralTypeProjectionExpression)typeProjection.BindComplexProperty(complexProperty).ValueBufferExpression); + AddToProjection(projection.DiscriminatorExpression, DiscriminatorColumnAlias); } - } - if (projection.DiscriminatorExpression is not null) - { - AddToProjection(projection.DiscriminatorExpression, DiscriminatorColumnAlias); + return Constant(projections); } - - return Constant(projections); } - ConstantExpression AddJsonProjection(JsonQueryExpression jsonQueryExpression, JsonScalarExpression jsonScalarToAdd) + ConstantExpression AddJsonProjection(JsonQueryExpression jsonQueryExpression) { - var sqlExpression = AssignUniqueAliases(jsonScalarToAdd); + var jsonScalarExpression = new JsonScalarExpression( + jsonQueryExpression.JsonColumn, + jsonQueryExpression.Path, + jsonQueryExpression.JsonColumn.Type, + jsonQueryExpression.JsonColumn.TypeMapping!, + jsonQueryExpression.IsNullable); + + var sqlExpression = AssignUniqueAliases(jsonScalarExpression); _projection.Add(new ProjectionExpression(sqlExpression, "")); var jsonColumnIndex = _projection.Count - 1; var keyAccessInfo = new List<(IProperty?, int?, int?)>(); @@ -1862,7 +1925,7 @@ ConstantExpression AddJsonProjection(JsonQueryExpression jsonQueryExpression, Js keyAccessInfo.Add((keyProperty, null, AddToProjection(keyColumn))); } - foreach (var elementAccessSegment in jsonScalarToAdd.Path.Where(x => x.ArrayIndex != null)) + foreach (var elementAccessSegment in jsonScalarExpression.Path.Where(x => x.ArrayIndex != null)) { if (elementAccessSegment.ArrayIndex is SqlConstantExpression { Value: int intValue }) { diff --git a/src/EFCore.Relational/Query/StructuralTypeProjectionExpression.cs b/src/EFCore.Relational/Query/StructuralTypeProjectionExpression.cs index 129eff60447..732f143af92 100644 --- a/src/EFCore.Relational/Query/StructuralTypeProjectionExpression.cs +++ b/src/EFCore.Relational/Query/StructuralTypeProjectionExpression.cs @@ -307,7 +307,7 @@ public virtual void AddNavigationBinding(INavigation navigation, StructuralTypeS { if (StructuralType is not IEntityType entityType) { - throw new InvalidOperationException(); // TODO: Message + throw new UnreachableException("Navigations are only supported on entity types"); } if (!entityType.IsAssignableFrom(navigation.DeclaringEntityType) @@ -330,7 +330,7 @@ public virtual void AddNavigationBinding(INavigation navigation, StructuralTypeS { if (StructuralType is not IEntityType entityType) { - throw new InvalidOperationException(); // TODO: Message + throw new UnreachableException("Navigations are only supported on entity types"); } if (!entityType.IsAssignableFrom(navigation.DeclaringEntityType) diff --git a/src/EFCore.Relational/Storage/Internal/NullTypeMapping.cs b/src/EFCore.Relational/Storage/Internal/NullTypeMapping.cs index 366d62c80ed..19e4aa9aeb3 100644 --- a/src/EFCore.Relational/Storage/Internal/NullTypeMapping.cs +++ b/src/EFCore.Relational/Storage/Internal/NullTypeMapping.cs @@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore.Storage.Json; -namespace Microsoft.EntityFrameworkCore.Storage; +namespace Microsoft.EntityFrameworkCore.Storage.Internal; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to diff --git a/src/EFCore.Relational/Storage/RelationalTypeMapping.cs b/src/EFCore.Relational/Storage/RelationalTypeMapping.cs index 1f12134f7b3..e6588e4e1d9 100644 --- a/src/EFCore.Relational/Storage/RelationalTypeMapping.cs +++ b/src/EFCore.Relational/Storage/RelationalTypeMapping.cs @@ -4,6 +4,7 @@ using System.Collections.Concurrent; using System.Data; using System.Globalization; +using Microsoft.EntityFrameworkCore.Storage.Internal; using Microsoft.EntityFrameworkCore.Storage.Json; namespace Microsoft.EntityFrameworkCore.Storage; @@ -231,16 +232,18 @@ public RelationalTypeMappingParameters WithScale(int? scale) /// /// The converter. /// The comparer. + /// The key comparer. /// The element mapping, or for non-collection mappings. /// The JSON reader/writer, or to leave unchanged. /// The new parameter object. public RelationalTypeMappingParameters WithComposedConverter( ValueConverter? converter, ValueComparer? comparer, + ValueComparer? keyComparer, CoreTypeMapping? elementMapping, JsonValueReaderWriter? jsonValueReaderWriter) => new( - CoreParameters.WithComposedConverter(converter, comparer, elementMapping, jsonValueReaderWriter), + CoreParameters.WithComposedConverter(converter, comparer, keyComparer, elementMapping, jsonValueReaderWriter), StoreType, StoreTypePostfix, DbType, @@ -276,7 +279,7 @@ private static MethodInfo GetDataReaderMethod(string name) /// /// Gets the mapping to be used when the only piece of information is that there is a null value. /// - public static readonly NullTypeMapping NullMapping = NullTypeMapping.Default; + public static readonly RelationalTypeMapping NullMapping = NullTypeMapping.Default; /// /// Initializes a new instance of the class. @@ -415,7 +418,7 @@ protected override CoreTypeMapping Clone(CoreTypeMappingParameters parameters) /// The name of the database type. /// The size of data the property is configured to store, or null if no size is configured. /// The newly created mapping. - public virtual RelationalTypeMapping Clone(string storeType, int? size) + public virtual RelationalTypeMapping WithStoreTypeAndSize(string storeType, int? size) => Clone(Parameters.WithStoreTypeAndSize(storeType, size)); /// @@ -424,52 +427,27 @@ public virtual RelationalTypeMapping Clone(string storeType, int? size) /// The precision of data the property is configured to store, or null if no size is configured. /// The scale of data the property is configured to store, or null if no size is configured. /// The newly created mapping. - public virtual RelationalTypeMapping Clone(int? precision, int? scale) + public virtual RelationalTypeMapping WithPrecisionAndScale(int? precision, int? scale) => Clone(Parameters.WithPrecisionAndScale(precision, scale)); /// - public override CoreTypeMapping Clone( + public override CoreTypeMapping WithComposedConverter( ValueConverter? converter, ValueComparer? comparer = null, + ValueComparer? keyComparer = null, CoreTypeMapping? elementMapping = null, JsonValueReaderWriter? jsonValueReaderWriter = null) - => Clone(Parameters.WithComposedConverter(converter, comparer, elementMapping, jsonValueReaderWriter)); + => Clone(Parameters.WithComposedConverter(converter, comparer, keyComparer, elementMapping, jsonValueReaderWriter)); /// /// Clones the type mapping to update facets from the mapping info, if needed. /// /// The mapping info containing the facets to use. /// The cloned mapping, or the original mapping if no clone was needed. - public virtual RelationalTypeMapping Clone( + public virtual RelationalTypeMapping WithTypeMappingInfo( in RelationalTypeMappingInfo mappingInfo) => Clone(Parameters.WithTypeMappingInfo(mappingInfo)); - /// - public override CoreTypeMapping Clone( - in TypeMappingInfo? mappingInfo = null, - Type? clrType = null, - ValueConverter? converter = null, - ValueComparer? comparer = null, - ValueComparer? keyComparer = null, - ValueComparer? providerValueComparer = null, - CoreTypeMapping? elementMapping = null, - JsonValueReaderWriter? jsonValueReaderWriter = null) - => Clone( - mappingInfo == null - ? null - : new RelationalTypeMappingInfo( - unicode: mappingInfo.Value.IsUnicode, - size: mappingInfo.Value.Size, - precision: mappingInfo.Value.Precision, - scale: mappingInfo.Value.Scale), - clrType, - converter, - comparer, - keyComparer, - providerValueComparer, - elementMapping, - jsonValueReaderWriter); - /// /// Clones the type mapping to update any parameter if needed. /// diff --git a/src/EFCore.Relational/Storage/RelationalTypeMappingInfo.cs b/src/EFCore.Relational/Storage/RelationalTypeMappingInfo.cs index 3c21d0202ed..4804af11bd0 100644 --- a/src/EFCore.Relational/Storage/RelationalTypeMappingInfo.cs +++ b/src/EFCore.Relational/Storage/RelationalTypeMappingInfo.cs @@ -151,7 +151,7 @@ public RelationalTypeMappingInfo( int? scale) { // Note: Empty string is allowed for store type name because SQLite - _coreTypeMappingInfo = new TypeMappingInfo(null, false, unicode, size, null, precision, scale); + _coreTypeMappingInfo = new TypeMappingInfo(null, null, false, unicode, size, null, precision, scale); StoreTypeName = storeTypeName; StoreTypeNameBase = storeTypeNameBase; IsFixedLength = null; @@ -161,6 +161,7 @@ public RelationalTypeMappingInfo( /// Creates a new instance of . /// /// The property or field for which mapping is needed. + /// The type mapping for elements, if known. /// The provider-specific relational type name for which mapping is needed. /// The provider-specific relational type name, with any facets removed. /// Specifies Unicode or ANSI mapping, or for default. @@ -169,6 +170,7 @@ public RelationalTypeMappingInfo( /// Specifies a scale for the mapping, or for default. public RelationalTypeMappingInfo( MemberInfo member, + RelationalTypeMapping? elementTypeMapping = null, string? storeTypeName = null, string? storeTypeNameBase = null, bool? unicode = null, @@ -176,7 +178,7 @@ public RelationalTypeMappingInfo( int? precision = null, int? scale = null) { - _coreTypeMappingInfo = new TypeMappingInfo(member, unicode, size, precision, scale); + _coreTypeMappingInfo = new TypeMappingInfo(member, elementTypeMapping, unicode, size, precision, scale); StoreTypeName = storeTypeName; StoreTypeNameBase = storeTypeNameBase; @@ -212,6 +214,7 @@ public RelationalTypeMappingInfo( /// Creates a new instance of . /// /// The CLR type in the model for which mapping is needed. + /// The type mapping for elements, if known. /// The database type name. /// The provider-specific relational type name, with any facets removed. /// If , then a special mapping for a key or index may be returned. @@ -224,6 +227,7 @@ public RelationalTypeMappingInfo( /// The suggested , or for default. public RelationalTypeMappingInfo( Type? type = null, + RelationalTypeMapping? elementTypeMapping = null, string? storeTypeName = null, string? storeTypeNameBase = null, bool keyOrIndex = false, @@ -235,7 +239,7 @@ public RelationalTypeMappingInfo( int? scale = null, DbType? dbType = null) { - _coreTypeMappingInfo = new TypeMappingInfo(type, keyOrIndex, unicode, size, rowVersion, precision, scale); + _coreTypeMappingInfo = new TypeMappingInfo(type, elementTypeMapping, keyOrIndex, unicode, size, rowVersion, precision, scale); IsFixedLength = fixedLength; StoreTypeName = storeTypeName; @@ -341,6 +345,15 @@ public JsonValueReaderWriter? JsonValueReaderWriter init => _coreTypeMappingInfo = _coreTypeMappingInfo with { JsonValueReaderWriter = value }; } + /// + /// The element type of the mapping, if any. + /// + public RelationalTypeMapping? ElementTypeMapping + { + get => (RelationalTypeMapping?)_coreTypeMappingInfo.ElementTypeMapping; + init => _coreTypeMappingInfo = _coreTypeMappingInfo with { ElementTypeMapping = value }; + } + /// /// Returns a new with the given converter applied. /// diff --git a/src/EFCore.Relational/Storage/RelationalTypeMappingSource.cs b/src/EFCore.Relational/Storage/RelationalTypeMappingSource.cs index 06097c77f64..bb0d5bf2bac 100644 --- a/src/EFCore.Relational/Storage/RelationalTypeMappingSource.cs +++ b/src/EFCore.Relational/Storage/RelationalTypeMappingSource.cs @@ -4,6 +4,7 @@ using System.Collections.Concurrent; using System.ComponentModel.DataAnnotations.Schema; using System.Diagnostics.CodeAnalysis; +using Microsoft.EntityFrameworkCore.ChangeTracking.Internal; namespace Microsoft.EntityFrameworkCore.Storage; @@ -85,7 +86,7 @@ protected override CoreTypeMapping FindMapping(in TypeMappingInfo mappingInfo) RelationalStrings.NoneRelationalTypeMappingOnARelationalTypeMappingSource); private RelationalTypeMapping? FindMappingWithConversion( - in RelationalTypeMappingInfo mappingInfo, + RelationalTypeMappingInfo mappingInfo, IReadOnlyList? principals) { Type? providerClrType = null; @@ -114,15 +115,19 @@ protected override CoreTypeMapping FindMapping(in TypeMappingInfo mappingInfo) } } - var element = principal.GetElementType(); - if (element != null) + if (elementMapping == null) { - elementMapping = FindMapping(element); + var element = principal.GetElementType(); + if (element != null) + { + elementMapping = FindMapping(element); + mappingInfo = mappingInfo with { ElementTypeMapping = (RelationalTypeMapping?)elementMapping }; + } } } } - var resolvedMapping = FindMappingWithConversion(mappingInfo, providerClrType, customConverter, elementMapping); + var resolvedMapping = FindMappingWithConversion(mappingInfo, providerClrType, customConverter); ValidateMapping(resolvedMapping, principals?[0]); @@ -132,26 +137,23 @@ protected override CoreTypeMapping FindMapping(in TypeMappingInfo mappingInfo) private RelationalTypeMapping? FindMappingWithConversion( RelationalTypeMappingInfo mappingInfo, Type? providerClrType, - ValueConverter? customConverter, - CoreTypeMapping? elementMapping) + ValueConverter? customConverter) => _explicitMappings.GetOrAdd( - (mappingInfo, providerClrType, customConverter, elementMapping), + (mappingInfo, providerClrType, customConverter, mappingInfo.ElementTypeMapping), static (k, self) => { var (mappingInfo, providerClrType, customConverter, elementMapping) = k; var sourceType = mappingInfo.ClrType; - RelationalTypeMapping? mapping = null; + var mapping = providerClrType == null + || providerClrType == mappingInfo.ClrType + ? self.FindMapping(mappingInfo) + : null; - if (elementMapping == null - || customConverter != null) + if (mapping == null) { - mapping = providerClrType == null - || providerClrType == mappingInfo.ClrType - ? self.FindMapping(mappingInfo) - : null; - - if (mapping == null) + if (elementMapping == null + || customConverter != null) { if (sourceType != null) { @@ -173,7 +175,7 @@ protected override CoreTypeMapping FindMapping(in TypeMappingInfo mappingInfo) if (mapping != null) { - mapping = (RelationalTypeMapping)mapping.Clone( + mapping = (RelationalTypeMapping)mapping.WithComposedConverter( secondConverterInfo.Create(), jsonValueReaderWriter: mappingInfoUsed.JsonValueReaderWriter); break; @@ -183,7 +185,7 @@ protected override CoreTypeMapping FindMapping(in TypeMappingInfo mappingInfo) if (mapping != null) { - mapping = (RelationalTypeMapping)mapping.Clone( + mapping = (RelationalTypeMapping)mapping.WithComposedConverter( converterInfo.Create(), jsonValueReaderWriter: mappingInfo.JsonValueReaderWriter); break; @@ -193,16 +195,16 @@ protected override CoreTypeMapping FindMapping(in TypeMappingInfo mappingInfo) mapping ??= self.FindCollectionMapping(mappingInfo, sourceType, providerClrType, elementMapping); } } - } - else if (sourceType != null) - { - mapping = self.FindCollectionMapping(mappingInfo, sourceType, providerClrType, elementMapping); + else if (sourceType != null) + { + mapping = self.FindCollectionMapping(mappingInfo, sourceType, providerClrType, elementMapping); + } } if (mapping != null && customConverter != null) { - mapping = (RelationalTypeMapping)mapping.Clone( + mapping = (RelationalTypeMapping)mapping.WithComposedConverter( customConverter, jsonValueReaderWriter: mappingInfo.JsonValueReaderWriter); } @@ -212,39 +214,32 @@ protected override CoreTypeMapping FindMapping(in TypeMappingInfo mappingInfo) this); /// - /// Attempts to find a type mapping for a collection of primitive types. + /// 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. /// - /// The mapping info being used. - /// The model type. - /// The provider type. - /// The element mapping, if known. - /// The type mapping, or if none was found. + [EntityFrameworkInternal] protected virtual RelationalTypeMapping? FindCollectionMapping( RelationalTypeMappingInfo info, Type modelType, Type? providerType, CoreTypeMapping? elementMapping) - { - var elementType = modelType.TryGetElementType(typeof(IEnumerable<>))!; - - return TryFindJsonCollectionMapping( - info.CoreTypeMappingInfo, modelType, providerType, ref elementMapping, out var collectionReaderWriter) + => TryFindJsonCollectionMapping( + info.CoreTypeMappingInfo, modelType, providerType, ref elementMapping, out var comparer, out var collectionReaderWriter) ? (RelationalTypeMapping)FindMapping( info.WithConverter( // Note that the converter info is only used temporarily here and never creates an instance. new ValueConverterInfo(modelType, typeof(string), _ => null!)))! - .Clone( + .WithComposedConverter( (ValueConverter)Activator.CreateInstance( - typeof(CollectionToJsonStringConverter<>).MakeGenericType(elementType), collectionReaderWriter!)!, - (ValueComparer?)Activator.CreateInstance( - elementType.IsNullableValueType() - ? typeof(NullableValueTypeListComparer<>).MakeGenericType(elementType.UnwrapNullableType()) - : typeof(ListComparer<>).MakeGenericType(elementMapping!.Comparer.Type), - elementMapping!.Comparer), + typeof(CollectionToJsonStringConverter<>).MakeGenericType( + modelType.TryGetElementType(typeof(IEnumerable<>))!), collectionReaderWriter!)!, + comparer, + comparer, elementMapping, collectionReaderWriter) : null; - } /// /// Finds the type mapping for a given . @@ -309,7 +304,7 @@ protected override CoreTypeMapping FindMapping(in TypeMappingInfo mappingInfo) var resolvedMapping = FindMappingWithConversion( new RelationalTypeMappingInfo(elementType, storeTypeName, storeTypeNameBase, unicode, isFixedLength, size, precision, scale), - providerClrType, customConverter, null); + providerClrType, customConverter); ValidateMapping(resolvedMapping, null); @@ -354,7 +349,7 @@ protected override CoreTypeMapping FindMapping(in TypeMappingInfo mappingInfo) ValueConverter? customConverter = null; if (typeConfiguration == null) { - mappingInfo = new RelationalTypeMappingInfo(type); + mappingInfo = new RelationalTypeMappingInfo(type, (RelationalTypeMapping?)elementMapping); } else { @@ -376,6 +371,7 @@ protected override CoreTypeMapping FindMapping(in TypeMappingInfo mappingInfo) var isFixedLength = (bool?)typeConfiguration[RelationalAnnotationNames.IsFixedLength]; mappingInfo = new RelationalTypeMappingInfo( customConverter?.ProviderClrType ?? type, + (RelationalTypeMapping?)elementMapping, storeTypeName, storeTypeBaseName, keyOrIndex: false, @@ -387,7 +383,7 @@ protected override CoreTypeMapping FindMapping(in TypeMappingInfo mappingInfo) scale: scale); } - return FindMappingWithConversion(mappingInfo, providerClrType, customConverter, (RelationalTypeMapping?)elementMapping); + return FindMappingWithConversion(mappingInfo, providerClrType, customConverter); } /// @@ -419,7 +415,7 @@ protected override CoreTypeMapping FindMapping(in TypeMappingInfo mappingInfo) attribute.TypeName, ref unicode, ref size, ref precision, ref scale); return FindMappingWithConversion( - new RelationalTypeMappingInfo(member, storeTypeName, storeTypeNameBase, unicode, size, precision, scale), null); + new RelationalTypeMappingInfo(member, null, storeTypeName, storeTypeNameBase, unicode, size, precision, scale), null); } return FindMappingWithConversion(new RelationalTypeMappingInfo(member), null); @@ -493,7 +489,7 @@ protected override CoreTypeMapping FindMapping(in TypeMappingInfo mappingInfo) return FindMappingWithConversion( new RelationalTypeMappingInfo( - type, storeTypeName, storeTypeBaseName, keyOrIndex, unicode, size, rowVersion, fixedLength, precision, scale), null); + type, null, storeTypeName, storeTypeBaseName, keyOrIndex, unicode, size, rowVersion, fixedLength, precision, scale), null); } /// diff --git a/src/EFCore.Relational/Storage/ValueConversion/RelationalConverterMappingHints.cs b/src/EFCore.Relational/Storage/ValueConversion/RelationalConverterMappingHints.cs index ea677e5b762..24ab3940d2b 100644 --- a/src/EFCore.Relational/Storage/ValueConversion/RelationalConverterMappingHints.cs +++ b/src/EFCore.Relational/Storage/ValueConversion/RelationalConverterMappingHints.cs @@ -77,7 +77,7 @@ public override ConverterMappingHints With(ConverterMappingHints? hints) (hints as RelationalConverterMappingHints)?.DbType ?? DbType); /// - public override ConverterMappingHints Override(ConverterMappingHints? hints) + public override ConverterMappingHints OverrideWith(ConverterMappingHints? hints) => hints == null ? this : new RelationalConverterMappingHints( diff --git a/src/EFCore.SqlServer.NTS/Extensions/SqlServerNetTopologySuiteDbContextOptionsBuilderExtensions.cs b/src/EFCore.SqlServer.NTS/Extensions/SqlServerNetTopologySuiteDbContextOptionsBuilderExtensions.cs index 5374f027ef4..2d16c843968 100644 --- a/src/EFCore.SqlServer.NTS/Extensions/SqlServerNetTopologySuiteDbContextOptionsBuilderExtensions.cs +++ b/src/EFCore.SqlServer.NTS/Extensions/SqlServerNetTopologySuiteDbContextOptionsBuilderExtensions.cs @@ -11,7 +11,7 @@ namespace Microsoft.EntityFrameworkCore; /// /// /// See Spatial data, and -/// Accessing SQL Server and SQL Azure databases with EF Core +/// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// public static class SqlServerNetTopologySuiteDbContextOptionsBuilderExtensions diff --git a/src/EFCore.SqlServer.NTS/Extensions/SqlServerNetTopologySuiteServiceCollectionExtensions.cs b/src/EFCore.SqlServer.NTS/Extensions/SqlServerNetTopologySuiteServiceCollectionExtensions.cs index eed9924ec79..2dcf2a7b4e1 100644 --- a/src/EFCore.SqlServer.NTS/Extensions/SqlServerNetTopologySuiteServiceCollectionExtensions.cs +++ b/src/EFCore.SqlServer.NTS/Extensions/SqlServerNetTopologySuiteServiceCollectionExtensions.cs @@ -14,7 +14,7 @@ namespace Microsoft.Extensions.DependencyInjection; /// /// /// See Spatial data, and -/// Accessing SQL Server and SQL Azure databases with EF Core +/// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// public static class SqlServerNetTopologySuiteServiceCollectionExtensions diff --git a/src/EFCore.SqlServer/DataCompressionType.cs b/src/EFCore.SqlServer/DataCompressionType.cs new file mode 100644 index 00000000000..058f8f0567d --- /dev/null +++ b/src/EFCore.SqlServer/DataCompressionType.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// ReSharper disable once CheckNamespace +namespace Microsoft.EntityFrameworkCore; + +/// +/// Indicates type of data compression used on a index. +/// +/// +/// See Data Compression for more information on data compression. +/// +public enum DataCompressionType +{ + /// + /// Index is not compressed. + /// + None, + + /// + /// Index is compressed by using row compression. + /// + Row, + + /// + /// Index is compressed by using page compression. + /// + Page +} diff --git a/src/EFCore.SqlServer/Design/Internal/SqlServerAnnotationCodeGenerator.cs b/src/EFCore.SqlServer/Design/Internal/SqlServerAnnotationCodeGenerator.cs index bb46a3e6be7..701b3896e90 100644 --- a/src/EFCore.SqlServer/Design/Internal/SqlServerAnnotationCodeGenerator.cs +++ b/src/EFCore.SqlServer/Design/Internal/SqlServerAnnotationCodeGenerator.cs @@ -79,6 +79,14 @@ private static readonly MethodInfo IndexHasFillFactorMethodInfo = typeof(SqlServerIndexBuilderExtensions).GetRuntimeMethod( nameof(SqlServerIndexBuilderExtensions.HasFillFactor), new[] { typeof(IndexBuilder), typeof(int) })!; + private static readonly MethodInfo IndexSortInTempDbMethodInfo + = typeof(SqlServerIndexBuilderExtensions).GetRuntimeMethod( + nameof(SqlServerIndexBuilderExtensions.SortInTempDb), new[] { typeof(IndexBuilder), typeof(bool) })!; + + private static readonly MethodInfo IndexUseDataCompressionMethodInfo + = typeof(SqlServerIndexBuilderExtensions).GetRuntimeMethod( + nameof(SqlServerIndexBuilderExtensions.UseDataCompression), new[] { typeof(IndexBuilder), typeof(DataCompressionType) })!; + private static readonly MethodInfo KeyIsClusteredMethodInfo = typeof(SqlServerKeyBuilderExtensions).GetRuntimeMethod( nameof(SqlServerKeyBuilderExtensions.IsClustered), new[] { typeof(KeyBuilder), typeof(bool) })!; @@ -341,6 +349,8 @@ protected override bool IsHandledByConvention(IProperty property, IAnnotation an SqlServerAnnotationNames.Include => new MethodCallCodeFragment(IndexIncludePropertiesMethodInfo, annotation.Value), SqlServerAnnotationNames.FillFactor => new MethodCallCodeFragment(IndexHasFillFactorMethodInfo, annotation.Value), + SqlServerAnnotationNames.SortInTempDb => new MethodCallCodeFragment(IndexSortInTempDbMethodInfo, annotation.Value), + SqlServerAnnotationNames.DataCompression => new MethodCallCodeFragment(IndexUseDataCompressionMethodInfo, annotation.Value), _ => null }; diff --git a/src/EFCore.SqlServer/Design/Internal/SqlServerCSharpRuntimeAnnotationCodeGenerator.cs b/src/EFCore.SqlServer/Design/Internal/SqlServerCSharpRuntimeAnnotationCodeGenerator.cs index 793eb75ca00..7bb1012a136 100644 --- a/src/EFCore.SqlServer/Design/Internal/SqlServerCSharpRuntimeAnnotationCodeGenerator.cs +++ b/src/EFCore.SqlServer/Design/Internal/SqlServerCSharpRuntimeAnnotationCodeGenerator.cs @@ -104,6 +104,8 @@ public override void Generate(IIndex index, CSharpRuntimeAnnotationCodeGenerator annotations.Remove(SqlServerAnnotationNames.CreatedOnline); annotations.Remove(SqlServerAnnotationNames.Include); annotations.Remove(SqlServerAnnotationNames.FillFactor); + annotations.Remove(SqlServerAnnotationNames.SortInTempDb); + annotations.Remove(SqlServerAnnotationNames.DataCompression); } base.Generate(index, parameters); @@ -119,6 +121,8 @@ public override void Generate(ITableIndex index, CSharpRuntimeAnnotationCodeGene annotations.Remove(SqlServerAnnotationNames.CreatedOnline); annotations.Remove(SqlServerAnnotationNames.Include); annotations.Remove(SqlServerAnnotationNames.FillFactor); + annotations.Remove(SqlServerAnnotationNames.SortInTempDb); + annotations.Remove(SqlServerAnnotationNames.DataCompression); } base.Generate(index, parameters); diff --git a/src/EFCore.SqlServer/Diagnostics/ConflictingValueGenerationStrategiesEventData.cs b/src/EFCore.SqlServer/Diagnostics/ConflictingValueGenerationStrategiesEventData.cs index 5e3790bbc7b..689afa66ef5 100644 --- a/src/EFCore.SqlServer/Diagnostics/ConflictingValueGenerationStrategiesEventData.cs +++ b/src/EFCore.SqlServer/Diagnostics/ConflictingValueGenerationStrategiesEventData.cs @@ -9,7 +9,7 @@ namespace Microsoft.EntityFrameworkCore.Diagnostics; /// /// /// See Logging, events, and diagnostics, and -/// Accessing SQL Server and SQL Azure databases with EF Core +/// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// public class ConflictingValueGenerationStrategiesEventData : EventData diff --git a/src/EFCore.SqlServer/Diagnostics/SqlServerEventId.cs b/src/EFCore.SqlServer/Diagnostics/SqlServerEventId.cs index a952d93627d..2fd40c196ef 100644 --- a/src/EFCore.SqlServer/Diagnostics/SqlServerEventId.cs +++ b/src/EFCore.SqlServer/Diagnostics/SqlServerEventId.cs @@ -14,7 +14,7 @@ namespace Microsoft.EntityFrameworkCore.Diagnostics; /// /// /// See Logging, events, and diagnostics, and -/// Accessing SQL Server and SQL Azure databases with EF Core +/// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// diff --git a/src/EFCore.SqlServer/Extensions/SqlServerComplexTypePrimitiveCollectionBuilderExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerComplexTypePrimitiveCollectionBuilderExtensions.cs index afd29bd2087..c4747993ba7 100644 --- a/src/EFCore.SqlServer/Extensions/SqlServerComplexTypePrimitiveCollectionBuilderExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/SqlServerComplexTypePrimitiveCollectionBuilderExtensions.cs @@ -8,7 +8,7 @@ namespace Microsoft.EntityFrameworkCore; /// /// /// See Modeling entity types and relationships, and -/// Accessing SQL Server and SQL Azure databases with EF Core +/// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// public static class SqlServerComplexTypePrimitiveCollectionBuilderExtensions @@ -18,7 +18,7 @@ public static class SqlServerComplexTypePrimitiveCollectionBuilderExtensions /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. Also see /// Sparse columns for /// general information on SQL Server sparse columns. @@ -38,7 +38,7 @@ public static ComplexTypePrimitiveCollectionBuilder IsSparse(this ComplexTypePri /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. Also see /// Sparse columns for /// general information on SQL Server sparse columns. diff --git a/src/EFCore.SqlServer/Extensions/SqlServerComplexTypePropertyBuilderExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerComplexTypePropertyBuilderExtensions.cs index 3e55e93207a..bb624ebc71d 100644 --- a/src/EFCore.SqlServer/Extensions/SqlServerComplexTypePropertyBuilderExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/SqlServerComplexTypePropertyBuilderExtensions.cs @@ -9,7 +9,7 @@ namespace Microsoft.EntityFrameworkCore; /// /// /// See Modeling entity types and relationships, and -/// Accessing SQL Server and SQL Azure databases with EF Core +/// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// public static class SqlServerComplexTypePropertyBuilderExtensions @@ -20,7 +20,7 @@ public static class SqlServerComplexTypePropertyBuilderExtensions /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the property being configured. @@ -61,7 +61,7 @@ public static ComplexTypePropertyBuilder UseHiLo( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The type of the property being configured. @@ -81,7 +81,7 @@ public static ComplexTypePropertyBuilder UseHiLo( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the property being configured. @@ -115,7 +115,7 @@ public static ComplexTypePropertyBuilder UseSequence( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The type of the property being configured. @@ -135,7 +135,7 @@ public static ComplexTypePropertyBuilder UseSequence( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the property being configured. @@ -165,7 +165,7 @@ public static ComplexTypePropertyBuilder UseIdentityColumn( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the property being configured. @@ -184,7 +184,7 @@ public static ComplexTypePropertyBuilder UseIdentityColumn( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The type of the property being configured. @@ -204,7 +204,7 @@ public static ComplexTypePropertyBuilder UseIdentityColumn /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The type of the property being configured. @@ -223,7 +223,7 @@ public static ComplexTypePropertyBuilder UseIdentityColumn /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. Also see /// Sparse columns for /// general information on SQL Server sparse columns. @@ -243,7 +243,7 @@ public static ComplexTypePropertyBuilder IsSparse(this ComplexTypePropertyBuilde /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. Also see /// Sparse columns for /// general information on SQL Server sparse columns. diff --git a/src/EFCore.SqlServer/Extensions/SqlServerDatabaseFacadeExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerDatabaseFacadeExtensions.cs index 3b9ba661860..d623ef40586 100644 --- a/src/EFCore.SqlServer/Extensions/SqlServerDatabaseFacadeExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/SqlServerDatabaseFacadeExtensions.cs @@ -22,7 +22,7 @@ public static class SqlServerDatabaseFacadeExtensions /// provider to use as part of configuring the context. /// /// - /// See Accessing SQL Server and SQL Azure databases with EF Core + /// See Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// diff --git a/src/EFCore.SqlServer/Extensions/SqlServerDbContextOptionsBuilderExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerDbContextOptionsBuilderExtensions.cs index 6859203cfc4..8ab8663f40f 100644 --- a/src/EFCore.SqlServer/Extensions/SqlServerDbContextOptionsBuilderExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/SqlServerDbContextOptionsBuilderExtensions.cs @@ -11,7 +11,7 @@ namespace Microsoft.EntityFrameworkCore; /// /// /// See Using DbContextOptions, and -/// Accessing SQL Server and SQL Azure databases with EF Core +/// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// public static class SqlServerDbContextOptionsExtensions @@ -28,7 +28,7 @@ public static class SqlServerDbContextOptionsExtensions /// /// /// See Using DbContextOptions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// @@ -41,11 +41,7 @@ public static DbContextOptionsBuilder UseSqlServer( { ((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(GetOrCreateExtension(optionsBuilder)); - ConfigureWarnings(optionsBuilder); - - sqlServerOptionsAction?.Invoke(new SqlServerDbContextOptionsBuilder(optionsBuilder)); - - return optionsBuilder; + return ApplyConfiguration(optionsBuilder, sqlServerOptionsAction); } /// @@ -53,7 +49,7 @@ public static DbContextOptionsBuilder UseSqlServer( /// /// /// See Using DbContextOptions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder being used to configure the context. @@ -68,11 +64,7 @@ public static DbContextOptionsBuilder UseSqlServer( var extension = (SqlServerOptionsExtension)GetOrCreateExtension(optionsBuilder).WithConnectionString(connectionString); ((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension); - ConfigureWarnings(optionsBuilder); - - sqlServerOptionsAction?.Invoke(new SqlServerDbContextOptionsBuilder(optionsBuilder)); - - return optionsBuilder; + return ApplyConfiguration(optionsBuilder, sqlServerOptionsAction); } // Note: Decision made to use DbConnection not SqlConnection: Issue #772 @@ -81,7 +73,7 @@ public static DbContextOptionsBuilder UseSqlServer( /// /// /// See Using DbContextOptions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder being used to configure the context. @@ -105,7 +97,7 @@ public static DbContextOptionsBuilder UseSqlServer( /// /// /// See Using DbContextOptions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder being used to configure the context. @@ -132,11 +124,7 @@ public static DbContextOptionsBuilder UseSqlServer( var extension = (SqlServerOptionsExtension)GetOrCreateExtension(optionsBuilder).WithConnection(connection, contextOwnsConnection); ((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension); - ConfigureWarnings(optionsBuilder); - - sqlServerOptionsAction?.Invoke(new SqlServerDbContextOptionsBuilder(optionsBuilder)); - - return optionsBuilder; + return ApplyConfiguration(optionsBuilder, sqlServerOptionsAction); } /// @@ -151,7 +139,7 @@ public static DbContextOptionsBuilder UseSqlServer( /// /// /// See Using DbContextOptions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// @@ -170,7 +158,7 @@ public static DbContextOptionsBuilder UseSqlServer( /// /// /// See Using DbContextOptions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The type of context to be configured. @@ -192,7 +180,7 @@ public static DbContextOptionsBuilder UseSqlServer( /// /// /// See Using DbContextOptions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The type of context to be configured. @@ -219,7 +207,7 @@ public static DbContextOptionsBuilder UseSqlServer( /// /// /// See Using DbContextOptions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The type of context to be configured. @@ -249,6 +237,18 @@ private static SqlServerOptionsExtension GetOrCreateExtension(DbContextOptionsBu => optionsBuilder.Options.FindExtension() ?? new SqlServerOptionsExtension(); + private static DbContextOptionsBuilder ApplyConfiguration(DbContextOptionsBuilder optionsBuilder, Action? sqlServerOptionsAction) + { + ConfigureWarnings(optionsBuilder); + + sqlServerOptionsAction?.Invoke(new SqlServerDbContextOptionsBuilder(optionsBuilder)); + + var extension = (SqlServerOptionsExtension)GetOrCreateExtension(optionsBuilder).ApplyDefaults(optionsBuilder.Options); + ((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension); + + return optionsBuilder; + } + private static void ConfigureWarnings(DbContextOptionsBuilder optionsBuilder) { var coreOptionsExtension diff --git a/src/EFCore.SqlServer/Extensions/SqlServerDbFunctionsExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerDbFunctionsExtensions.cs index 6180a64a702..9854fd86dde 100644 --- a/src/EFCore.SqlServer/Extensions/SqlServerDbFunctionsExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/SqlServerDbFunctionsExtensions.cs @@ -11,7 +11,7 @@ namespace Microsoft.EntityFrameworkCore; /// /// /// See Database functions, and -/// Accessing SQL Server and SQL Azure databases with EF Core +/// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// public static class SqlServerDbFunctionsExtensions @@ -23,7 +23,7 @@ public static class SqlServerDbFunctionsExtensions /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -42,7 +42,7 @@ public static bool FreeText( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -59,7 +59,7 @@ public static bool FreeText( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -78,7 +78,7 @@ public static bool Contains( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -100,7 +100,7 @@ public static bool Contains( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -119,7 +119,7 @@ public static int DateDiffYear( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -138,7 +138,7 @@ public static int DateDiffYear( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -157,7 +157,7 @@ public static int DateDiffYear( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -176,7 +176,7 @@ public static int DateDiffYear( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -195,7 +195,7 @@ public static int DateDiffYear( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -218,7 +218,7 @@ public static int DateDiffYear( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -237,7 +237,7 @@ public static int DateDiffMonth( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -256,7 +256,7 @@ public static int DateDiffMonth( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -275,7 +275,7 @@ public static int DateDiffMonth( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -294,7 +294,7 @@ public static int DateDiffMonth( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -313,7 +313,7 @@ public static int DateDiffMonth( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -336,7 +336,7 @@ public static int DateDiffMonth( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -355,7 +355,7 @@ public static int DateDiffDay( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -374,7 +374,7 @@ public static int DateDiffDay( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -393,7 +393,7 @@ public static int DateDiffDay( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -412,7 +412,7 @@ public static int DateDiffDay( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -431,7 +431,7 @@ public static int DateDiffDay( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -454,7 +454,7 @@ public static int DateDiffDay( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -473,7 +473,7 @@ public static int DateDiffHour( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -492,7 +492,7 @@ public static int DateDiffHour( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -511,7 +511,7 @@ public static int DateDiffHour( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -530,7 +530,7 @@ public static int DateDiffHour( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -549,7 +549,7 @@ public static int DateDiffHour( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -568,7 +568,7 @@ public static int DateDiffHour( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -587,7 +587,7 @@ public static int DateDiffHour( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -606,7 +606,7 @@ public static int DateDiffHour( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -625,7 +625,7 @@ public static int DateDiffHour( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -648,7 +648,7 @@ public static int DateDiffHour( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -667,7 +667,7 @@ public static int DateDiffMinute( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -686,7 +686,7 @@ public static int DateDiffMinute( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -705,7 +705,7 @@ public static int DateDiffMinute( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -724,7 +724,7 @@ public static int DateDiffMinute( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -743,7 +743,7 @@ public static int DateDiffMinute( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -762,7 +762,7 @@ public static int DateDiffMinute( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -781,7 +781,7 @@ public static int DateDiffMinute( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -800,7 +800,7 @@ public static int DateDiffMinute( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -819,7 +819,7 @@ public static int DateDiffMinute( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -842,7 +842,7 @@ public static int DateDiffMinute( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -861,7 +861,7 @@ public static int DateDiffSecond( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -880,7 +880,7 @@ public static int DateDiffSecond( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -899,7 +899,7 @@ public static int DateDiffSecond( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -918,7 +918,7 @@ public static int DateDiffSecond( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -937,7 +937,7 @@ public static int DateDiffSecond( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -956,7 +956,7 @@ public static int DateDiffSecond( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -975,7 +975,7 @@ public static int DateDiffSecond( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -994,7 +994,7 @@ public static int DateDiffSecond( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1013,7 +1013,7 @@ public static int DateDiffSecond( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1036,7 +1036,7 @@ public static int DateDiffSecond( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1055,7 +1055,7 @@ public static int DateDiffMillisecond( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1074,7 +1074,7 @@ public static int DateDiffMillisecond( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1093,7 +1093,7 @@ public static int DateDiffMillisecond( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1112,7 +1112,7 @@ public static int DateDiffMillisecond( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1131,7 +1131,7 @@ public static int DateDiffMillisecond( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1150,7 +1150,7 @@ public static int DateDiffMillisecond( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1169,7 +1169,7 @@ public static int DateDiffMillisecond( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1188,7 +1188,7 @@ public static int DateDiffMillisecond( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1207,7 +1207,7 @@ public static int DateDiffMillisecond( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1230,7 +1230,7 @@ public static int DateDiffMillisecond( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1249,7 +1249,7 @@ public static int DateDiffMicrosecond( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1268,7 +1268,7 @@ public static int DateDiffMicrosecond( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1287,7 +1287,7 @@ public static int DateDiffMicrosecond( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1306,7 +1306,7 @@ public static int DateDiffMicrosecond( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1325,7 +1325,7 @@ public static int DateDiffMicrosecond( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1344,7 +1344,7 @@ public static int DateDiffMicrosecond( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1363,7 +1363,7 @@ public static int DateDiffMicrosecond( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1382,7 +1382,7 @@ public static int DateDiffMicrosecond( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1401,7 +1401,7 @@ public static int DateDiffMicrosecond( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1424,7 +1424,7 @@ public static int DateDiffMicrosecond( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1443,7 +1443,7 @@ public static int DateDiffNanosecond( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1462,7 +1462,7 @@ public static int DateDiffNanosecond( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1481,7 +1481,7 @@ public static int DateDiffNanosecond( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1500,7 +1500,7 @@ public static int DateDiffNanosecond( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1519,7 +1519,7 @@ public static int DateDiffNanosecond( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1538,7 +1538,7 @@ public static int DateDiffNanosecond( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1557,7 +1557,7 @@ public static int DateDiffNanosecond( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1576,7 +1576,7 @@ public static int DateDiffNanosecond( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1595,7 +1595,7 @@ public static int DateDiffNanosecond( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1618,7 +1618,7 @@ public static int DateDiffNanosecond( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1637,7 +1637,7 @@ public static int DateDiffWeek( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1656,7 +1656,7 @@ public static int DateDiffWeek( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1675,7 +1675,7 @@ public static int DateDiffWeek( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1694,7 +1694,7 @@ public static int DateDiffWeek( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1713,7 +1713,7 @@ public static int DateDiffWeek( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1734,7 +1734,7 @@ public static int DateDiffWeek( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1752,7 +1752,7 @@ public static bool IsDate( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1784,7 +1784,7 @@ public static DateTime DateTimeFromParts( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1806,7 +1806,7 @@ public static DateTime DateFromParts( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1846,7 +1846,7 @@ public static DateTime DateTime2FromParts( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1884,7 +1884,7 @@ public static DateTimeOffset DateTimeOffsetFromParts( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1909,7 +1909,7 @@ public static DateTime SmallDateTimeFromParts( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1935,7 +1935,7 @@ public static TimeSpan TimeFromParts( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1951,7 +1951,7 @@ public static TimeSpan TimeFromParts( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1967,7 +1967,7 @@ public static TimeSpan TimeFromParts( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1983,7 +1983,7 @@ public static TimeSpan TimeFromParts( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -1999,7 +1999,7 @@ public static TimeSpan TimeFromParts( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -2015,7 +2015,7 @@ public static TimeSpan TimeFromParts( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -2031,7 +2031,7 @@ public static TimeSpan TimeFromParts( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -2047,7 +2047,7 @@ public static TimeSpan TimeFromParts( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -2063,7 +2063,7 @@ public static TimeSpan TimeFromParts( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -2080,7 +2080,7 @@ public static TimeSpan TimeFromParts( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. @@ -2102,7 +2102,7 @@ public static bool IsNumeric( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// @@ -2123,7 +2123,7 @@ public static DateTimeOffset AtTimeZone( /// /// /// See Database functions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The instance. diff --git a/src/EFCore.SqlServer/Extensions/SqlServerEntityTypeBuilderExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerEntityTypeBuilderExtensions.cs index 30c00ffcbb6..c5ce3eac1fc 100644 --- a/src/EFCore.SqlServer/Extensions/SqlServerEntityTypeBuilderExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/SqlServerEntityTypeBuilderExtensions.cs @@ -11,7 +11,7 @@ namespace Microsoft.EntityFrameworkCore; /// /// /// See Modeling entity types and relationships, and -/// Accessing SQL Server and SQL Azure databases with EF Core +/// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// public static class SqlServerEntityTypeBuilderExtensions diff --git a/src/EFCore.SqlServer/Extensions/SqlServerEntityTypeExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerEntityTypeExtensions.cs index f0e15f9f2a3..1f69486cb80 100644 --- a/src/EFCore.SqlServer/Extensions/SqlServerEntityTypeExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/SqlServerEntityTypeExtensions.cs @@ -11,7 +11,7 @@ namespace Microsoft.EntityFrameworkCore; /// /// /// See Modeling entity types and relationships, and -/// Accessing SQL Server and SQL Azure databases with EF Core +/// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// public static class SqlServerEntityTypeExtensions diff --git a/src/EFCore.SqlServer/Extensions/SqlServerIndexBuilderExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerIndexBuilderExtensions.cs index 4d5e3de65db..f57205ac507 100644 --- a/src/EFCore.SqlServer/Extensions/SqlServerIndexBuilderExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/SqlServerIndexBuilderExtensions.cs @@ -11,7 +11,7 @@ namespace Microsoft.EntityFrameworkCore; /// /// /// See Modeling entity types and relationships, and -/// Accessing SQL Server and SQL Azure databases with EF Core +/// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// public static class SqlServerIndexBuilderExtensions @@ -21,7 +21,7 @@ public static class SqlServerIndexBuilderExtensions /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the index being configured. @@ -39,7 +39,7 @@ public static IndexBuilder IsClustered(this IndexBuilder indexBuilder, bool clus /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the index being configured. @@ -55,7 +55,7 @@ public static IndexBuilder IsClustered( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the index being configured. @@ -84,7 +84,7 @@ public static IndexBuilder IsClustered( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the index being configured. @@ -102,7 +102,7 @@ public static bool CanSetIsClustered( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the index being configured. @@ -122,7 +122,7 @@ public static IndexBuilder IncludeProperties(this IndexBuilder indexBuilder, par /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the index being configured. @@ -144,7 +144,7 @@ public static IndexBuilder IncludeProperties( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the index being configured. @@ -177,7 +177,7 @@ public static IndexBuilder IncludeProperties( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the index being configured. @@ -207,7 +207,7 @@ public static IndexBuilder IncludeProperties( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the index being configured. @@ -229,7 +229,7 @@ public static bool CanSetIncludeProperties( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the index being configured. @@ -247,7 +247,7 @@ public static IndexBuilder IsCreatedOnline(this IndexBuilder indexBuilder, bool /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the index being configured. @@ -263,7 +263,7 @@ public static IndexBuilder IsCreatedOnline( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the index being configured. @@ -293,7 +293,7 @@ public static IndexBuilder IsCreatedOnline( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the index being configured. @@ -315,7 +315,7 @@ public static bool CanSetIsCreatedOnline( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the index being configured. @@ -333,7 +333,7 @@ public static IndexBuilder HasFillFactor(this IndexBuilder indexBuilder, int fil /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the index being configured. @@ -349,7 +349,7 @@ public static IndexBuilder HasFillFactor( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the index being configured. @@ -379,7 +379,7 @@ public static IndexBuilder HasFillFactor( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the index being configured. @@ -391,4 +391,176 @@ public static bool CanSetFillFactor( int? fillFactor, bool fromDataAnnotation = false) => indexBuilder.CanSetAnnotation(SqlServerAnnotationNames.FillFactor, fillFactor, fromDataAnnotation); + + /// + /// Configures whether the index is created with sort in tempdb option when targeting SQL Server. + /// + /// + /// See Modeling entity types and relationships, and + /// Accessing SQL Server and SQL Azure databases with EF Core + /// for more information and examples. + /// + /// The builder for the index being configured. + /// A value indicating whether the index is created with sort in tempdb option. + /// A builder to further configure the index. + public static IndexBuilder SortInTempDb(this IndexBuilder indexBuilder, bool sortInTempDb = true) + { + indexBuilder.Metadata.SetSortInTempDb(sortInTempDb); + + return indexBuilder; + } + + /// + /// Configures whether the index is created with sort in tempdb option when targeting SQL Server. + /// + /// + /// See Modeling entity types and relationships, and + /// Accessing SQL Server and SQL Azure databases with EF Core + /// for more information and examples. + /// + /// The builder for the index being configured. + /// A value indicating whether the index is created with sort in tempdb option. + /// A builder to further configure the index. + public static IndexBuilder SortInTempDb( + this IndexBuilder indexBuilder, + bool sortInTempDb = true) + => (IndexBuilder)SortInTempDb((IndexBuilder)indexBuilder, sortInTempDb); + + /// + /// Configures whether the index is created with sort in tempdb option when targeting SQL Server. + /// + /// + /// See Modeling entity types and relationships, and + /// Accessing SQL Server and SQL Azure databases with EF Core + /// for more information and examples. + /// + /// The builder for the index being configured. + /// A value indicating whether the index is created with sort in tempdb option. + /// Indicates whether the configuration was specified using a data annotation. + /// + /// The same builder instance if the configuration was applied, + /// otherwise. + /// + public static IConventionIndexBuilder? SortInTempDb( + this IConventionIndexBuilder indexBuilder, + bool? sortInTempDb, + bool fromDataAnnotation = false) + { + if (indexBuilder.CanSetSortInTempDb(sortInTempDb, fromDataAnnotation)) + { + indexBuilder.Metadata.SetSortInTempDb(sortInTempDb, fromDataAnnotation); + + return indexBuilder; + } + + return null; + } + + /// + /// Returns a value indicating whether the index can be configured with sort in tempdb option when targeting SQL Server. + /// + /// + /// See Modeling entity types and relationships, and + /// Accessing SQL Server and SQL Azure databases with EF Core + /// for more information and examples. + /// + /// The builder for the index being configured. + /// A value indicating whether the index is created with sort in tempdb option. + /// Indicates whether the configuration was specified using a data annotation. + /// + /// The same builder instance if the configuration was applied, + /// otherwise. + /// + /// if the index can be configured with sort in tempdb option when targeting SQL Server. + public static bool CanSetSortInTempDb( + this IConventionIndexBuilder indexBuilder, + bool? sortInTempDb, + bool fromDataAnnotation = false) + => indexBuilder.CanSetAnnotation(SqlServerAnnotationNames.SortInTempDb, sortInTempDb, fromDataAnnotation); + + /// + /// Configures whether the index is created with data compression option when targeting SQL Server. + /// + /// + /// See Modeling entity types and relationships, and + /// Accessing SQL Server and SQL Azure databases with EF Core + /// for more information and examples. + /// + /// The builder for the index being configured. + /// A value indicating the data compression option to be used. + /// A builder to further configure the index. + public static IndexBuilder UseDataCompression(this IndexBuilder indexBuilder, DataCompressionType dataCompressionType) + { + indexBuilder.Metadata.SetDataCompression(dataCompressionType); + + return indexBuilder; + } + + /// + /// Configures whether the index is created with data compression option when targeting SQL Server. + /// + /// + /// See Modeling entity types and relationships, and + /// Accessing SQL Server and SQL Azure databases with EF Core + /// for more information and examples. + /// + /// The builder for the index being configured. + /// A value indicating the data compression option to be used. + /// A builder to further configure the index. + public static IndexBuilder UseDataCompression( + this IndexBuilder indexBuilder, + DataCompressionType dataCompressionType) + => (IndexBuilder)UseDataCompression((IndexBuilder)indexBuilder, dataCompressionType); + + /// + /// Configures whether the index is created with data compression option when targeting SQL Server. + /// + /// + /// See Modeling entity types and relationships, and + /// Accessing SQL Server and SQL Azure databases with EF Core + /// for more information and examples. + /// + /// The builder for the index being configured. + /// A value indicating the data compression option to be used. + /// Indicates whether the configuration was specified using a data annotation. + /// + /// The same builder instance if the configuration was applied, + /// otherwise. + /// + public static IConventionIndexBuilder? UseDataCompression( + this IConventionIndexBuilder indexBuilder, + DataCompressionType? dataCompressionType, + bool fromDataAnnotation = false) + { + if (indexBuilder.CanSetDataCompression(dataCompressionType, fromDataAnnotation)) + { + indexBuilder.Metadata.SetDataCompression(dataCompressionType, fromDataAnnotation); + + return indexBuilder; + } + + return null; + } + + /// + /// Returns a value indicating whether the index can be configured with data compression option when targeting SQL Server. + /// + /// + /// See Modeling entity types and relationships, and + /// Accessing SQL Server and SQL Azure databases with EF Core + /// for more information and examples. + /// + /// The builder for the index being configured. + /// A value indicating the data compression option to be used. + /// Indicates whether the configuration was specified using a data annotation. + /// + /// The same builder instance if the configuration was applied, + /// otherwise. + /// + /// if the index can be configured with data compression option when targeting SQL Server. + public static bool CanSetDataCompression( + this IConventionIndexBuilder indexBuilder, + DataCompressionType? dataCompressionType, + bool fromDataAnnotation = false) + => indexBuilder.CanSetAnnotation(SqlServerAnnotationNames.DataCompression, dataCompressionType, fromDataAnnotation); } diff --git a/src/EFCore.SqlServer/Extensions/SqlServerIndexExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerIndexExtensions.cs index af32ab3f1dd..58310d77a54 100644 --- a/src/EFCore.SqlServer/Extensions/SqlServerIndexExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/SqlServerIndexExtensions.cs @@ -11,7 +11,7 @@ namespace Microsoft.EntityFrameworkCore; /// /// /// See Modeling entity types and relationships, and -/// Accessing SQL Server and SQL Azure databases with EF Core +/// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// public static class SqlServerIndexExtensions @@ -297,4 +297,136 @@ public static void SetFillFactor(this IMutableIndex index, int? fillFactor) /// The for whether the index uses the fill factor. public static ConfigurationSource? GetFillFactorConfigurationSource(this IConventionIndex index) => index.FindAnnotation(SqlServerAnnotationNames.FillFactor)?.GetConfigurationSource(); + + /// + /// Returns a value indicating whether the index is sorted in tempdb. + /// + /// The index. + /// if the index is sorted in tempdb. + public static bool? GetSortInTempDb(this IReadOnlyIndex index) + => (index is RuntimeIndex) + ? throw new InvalidOperationException(CoreStrings.RuntimeModelMissingData) + : (bool?)index[SqlServerAnnotationNames.SortInTempDb]; + + /// + /// Returns a value indicating whether the index is sorted in tempdb. + /// + /// The index. + /// The identifier of the store object. + /// if the index is sorted in tempdb. + public static bool? GetSortInTempDb(this IReadOnlyIndex index, in StoreObjectIdentifier storeObject) + { + if (index is RuntimeIndex) + { + throw new InvalidOperationException(CoreStrings.RuntimeModelMissingData); + } + + var annotation = index.FindAnnotation(SqlServerAnnotationNames.SortInTempDb); + if (annotation != null) + { + return (bool?)annotation.Value; + } + + var sharedTableRootIndex = index.FindSharedObjectRootIndex(storeObject); + return sharedTableRootIndex?.GetSortInTempDb(storeObject); + } + + /// + /// Sets a value indicating whether the index is sorted in tempdb. + /// + /// The index. + /// The value to set. + public static void SetSortInTempDb(this IMutableIndex index, bool? sortInTempDb) + => index.SetAnnotation( + SqlServerAnnotationNames.SortInTempDb, + sortInTempDb); + + /// + /// Sets a value indicating whether the index is sorted in tempdb. + /// + /// The index. + /// The value to set. + /// Indicates whether the configuration was specified using a data annotation. + /// The configured value. + public static bool? SetSortInTempDb( + this IConventionIndex index, + bool? sortInTempDb, + bool fromDataAnnotation = false) + => (bool?)index.SetAnnotation( + SqlServerAnnotationNames.SortInTempDb, + sortInTempDb, + fromDataAnnotation)?.Value; + + /// + /// Returns the for whether the index is sorted in tempdb. + /// + /// The index. + /// The for whether the index is sorted in tempdb. + public static ConfigurationSource? GetSortInTempDbConfigurationSource(this IConventionIndex index) + => index.FindAnnotation(SqlServerAnnotationNames.SortInTempDb)?.GetConfigurationSource(); + + /// + /// Returns the data compression that the index uses. + /// + /// The index. + /// The data compression that the index uses + public static DataCompressionType? GetDataCompression(this IReadOnlyIndex index) + => (index is RuntimeIndex) + ? throw new InvalidOperationException(CoreStrings.RuntimeModelMissingData) + : (DataCompressionType?)index[SqlServerAnnotationNames.DataCompression]; + + /// + /// Returns the data compression that the index uses. + /// + /// The index. + /// The identifier of the store object. + /// The data compression that the index uses + public static DataCompressionType? GetDataCompression(this IReadOnlyIndex index, in StoreObjectIdentifier storeObject) + { + if (index is RuntimeIndex) + { + throw new InvalidOperationException(CoreStrings.RuntimeModelMissingData); + } + + var annotation = index.FindAnnotation(SqlServerAnnotationNames.DataCompression); + if (annotation != null) + { + return (DataCompressionType?)annotation.Value; + } + + var sharedTableRootIndex = index.FindSharedObjectRootIndex(storeObject); + return sharedTableRootIndex?.GetDataCompression(storeObject); + } + + /// + /// Sets a value indicating the data compression the index uses. + /// + /// The index. + /// The value to set. + public static void SetDataCompression(this IMutableIndex index, DataCompressionType? dataCompression) => index.SetAnnotation( + SqlServerAnnotationNames.DataCompression, + dataCompression); + + /// + /// Sets a value indicating the data compression the index uses. + /// + /// The index. + /// The value to set. + /// Indicates whether the configuration was specified using a data annotation. + /// The configured value. + public static DataCompressionType? SetDataCompression( + this IConventionIndex index, + DataCompressionType? dataCompression, + bool fromDataAnnotation = false) => (DataCompressionType?)index.SetAnnotation( + SqlServerAnnotationNames.DataCompression, + dataCompression, + fromDataAnnotation)?.Value; + + /// + /// Returns the for the data compression the index uses. + /// + /// The index. + /// The for the data compression the index uses. + public static ConfigurationSource? GetDataCompressionConfigurationSource(this IConventionIndex index) + => index.FindAnnotation(SqlServerAnnotationNames.DataCompression)?.GetConfigurationSource(); } diff --git a/src/EFCore.SqlServer/Extensions/SqlServerKeyBuilderExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerKeyBuilderExtensions.cs index 2a248e20eb6..0deb6c04875 100644 --- a/src/EFCore.SqlServer/Extensions/SqlServerKeyBuilderExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/SqlServerKeyBuilderExtensions.cs @@ -11,7 +11,7 @@ namespace Microsoft.EntityFrameworkCore; /// /// /// See Modeling entity types and relationships, and -/// Accessing SQL Server and SQL Azure databases with EF Core +/// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// public static class SqlServerKeyBuilderExtensions @@ -21,7 +21,7 @@ public static class SqlServerKeyBuilderExtensions /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the key being configured. @@ -39,7 +39,7 @@ public static KeyBuilder IsClustered(this KeyBuilder keyBuilder, bool clustered /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the key being configured. @@ -55,7 +55,7 @@ public static KeyBuilder IsClustered( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the key being configured. @@ -84,7 +84,7 @@ public static KeyBuilder IsClustered( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the key being configured. diff --git a/src/EFCore.SqlServer/Extensions/SqlServerKeyExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerKeyExtensions.cs index 8f22e06b691..5a5c7d1e1b4 100644 --- a/src/EFCore.SqlServer/Extensions/SqlServerKeyExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/SqlServerKeyExtensions.cs @@ -11,7 +11,7 @@ namespace Microsoft.EntityFrameworkCore; /// /// /// See Modeling entity types and relationships, and -/// Accessing SQL Server and SQL Azure databases with EF Core +/// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// public static class SqlServerKeyExtensions diff --git a/src/EFCore.SqlServer/Extensions/SqlServerMigrationBuilderExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerMigrationBuilderExtensions.cs index 368ba006a8a..d996d0ca99a 100644 --- a/src/EFCore.SqlServer/Extensions/SqlServerMigrationBuilderExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/SqlServerMigrationBuilderExtensions.cs @@ -15,7 +15,7 @@ public static class SqlServerMigrationBuilderExtensions /// Returns if the database provider currently in use is the SQL Server provider. /// /// - /// See Accessing SQL Server and SQL Azure databases with EF Core + /// See Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// diff --git a/src/EFCore.SqlServer/Extensions/SqlServerModelBuilderExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerModelBuilderExtensions.cs index 0ab2c60f7ae..f615ffad5c3 100644 --- a/src/EFCore.SqlServer/Extensions/SqlServerModelBuilderExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/SqlServerModelBuilderExtensions.cs @@ -11,7 +11,7 @@ namespace Microsoft.EntityFrameworkCore; /// /// /// See Modeling entity types and relationships, and -/// Accessing SQL Server and SQL Azure databases with EF Core +/// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// public static class SqlServerModelBuilderExtensions @@ -22,7 +22,7 @@ public static class SqlServerModelBuilderExtensions /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The model builder. @@ -63,7 +63,7 @@ public static ModelBuilder UseHiLo( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The model builder. @@ -93,7 +93,7 @@ public static ModelBuilder UseHiLo( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The model builder. @@ -120,7 +120,7 @@ public static bool CanSetHiLoSequence( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The model builder. @@ -157,7 +157,7 @@ public static ModelBuilder UseKeySequences( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The model builder. @@ -189,7 +189,7 @@ public static ModelBuilder UseIdentityColumns( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The model builder. @@ -207,7 +207,7 @@ public static ModelBuilder UseIdentityColumns( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The model builder. @@ -236,7 +236,7 @@ public static ModelBuilder UseIdentityColumns( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The model builder. @@ -254,7 +254,7 @@ public static bool CanSetIdentityColumnSeed( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The model builder. @@ -283,7 +283,7 @@ public static bool CanSetIdentityColumnSeed( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The model builder. @@ -302,7 +302,7 @@ public static bool CanSetIdentityColumnIncrement( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The model builder. @@ -357,7 +357,7 @@ void RemoveKeySequenceAnnotations() /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The model builder. @@ -380,7 +380,7 @@ public static bool CanSetValueGenerationStrategy( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// @@ -405,7 +405,7 @@ public static ModelBuilder HasDatabaseMaxSize(this ModelBuilder modelBuilder, st /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// @@ -435,7 +435,7 @@ public static ModelBuilder HasDatabaseMaxSize(this ModelBuilder modelBuilder, st /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The model builder. @@ -457,7 +457,7 @@ public static bool CanSetDatabaseMaxSize( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// @@ -482,7 +482,7 @@ public static ModelBuilder HasServiceTier(this ModelBuilder modelBuilder, string /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// @@ -507,7 +507,7 @@ public static ModelBuilder HasServiceTierSql(this ModelBuilder modelBuilder, str /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// @@ -537,7 +537,7 @@ public static ModelBuilder HasServiceTierSql(this ModelBuilder modelBuilder, str /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The model builder. @@ -559,7 +559,7 @@ public static bool CanSetServiceTierSql( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// @@ -584,7 +584,7 @@ public static ModelBuilder HasPerformanceLevel(this ModelBuilder modelBuilder, s /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// @@ -609,7 +609,7 @@ public static ModelBuilder HasPerformanceLevelSql(this ModelBuilder modelBuilder /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// @@ -639,7 +639,7 @@ public static ModelBuilder HasPerformanceLevelSql(this ModelBuilder modelBuilder /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The model builder. diff --git a/src/EFCore.SqlServer/Extensions/SqlServerModelExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerModelExtensions.cs index 7d494049b24..2925804927e 100644 --- a/src/EFCore.SqlServer/Extensions/SqlServerModelExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/SqlServerModelExtensions.cs @@ -11,7 +11,7 @@ namespace Microsoft.EntityFrameworkCore; /// /// /// See Modeling entity types and relationships, and -/// Accessing SQL Server and SQL Azure databases with EF Core +/// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// public static class SqlServerModelExtensions diff --git a/src/EFCore.SqlServer/Extensions/SqlServerPrimitiveCollectionBuilderExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerPrimitiveCollectionBuilderExtensions.cs index 17dd2d26dfe..87fa60c5132 100644 --- a/src/EFCore.SqlServer/Extensions/SqlServerPrimitiveCollectionBuilderExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/SqlServerPrimitiveCollectionBuilderExtensions.cs @@ -8,7 +8,7 @@ namespace Microsoft.EntityFrameworkCore; /// /// /// See Modeling entity types and relationships, and -/// Accessing SQL Server and SQL Azure databases with EF Core +/// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// public static class SqlServerPrimitiveCollectionBuilderExtensions @@ -18,7 +18,7 @@ public static class SqlServerPrimitiveCollectionBuilderExtensions /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. Also see /// Sparse columns for /// general information on SQL Server sparse columns. @@ -38,7 +38,7 @@ public static PrimitiveCollectionBuilder IsSparse(this PrimitiveCollectionBuilde /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. Also see /// Sparse columns for /// general information on SQL Server sparse columns. diff --git a/src/EFCore.SqlServer/Extensions/SqlServerPropertyBuilderExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerPropertyBuilderExtensions.cs index 11d8e694a1c..f959733c7e3 100644 --- a/src/EFCore.SqlServer/Extensions/SqlServerPropertyBuilderExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/SqlServerPropertyBuilderExtensions.cs @@ -11,7 +11,7 @@ namespace Microsoft.EntityFrameworkCore; /// /// /// See Modeling entity types and relationships, and -/// Accessing SQL Server and SQL Azure databases with EF Core +/// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// public static class SqlServerPropertyBuilderExtensions @@ -22,7 +22,7 @@ public static class SqlServerPropertyBuilderExtensions /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the property being configured. @@ -63,7 +63,7 @@ public static PropertyBuilder UseHiLo( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The type of the property being configured. @@ -83,7 +83,7 @@ public static PropertyBuilder UseHiLo( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the property being configured. @@ -115,7 +115,7 @@ public static PropertyBuilder UseHiLo( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the property being configured. @@ -142,7 +142,7 @@ public static bool CanSetHiLoSequence( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the property being configured. @@ -176,7 +176,7 @@ public static PropertyBuilder UseSequence( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The type of the property being configured. @@ -196,7 +196,7 @@ public static PropertyBuilder UseSequence( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the property being configured. @@ -228,7 +228,7 @@ public static PropertyBuilder UseSequence( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the property being configured. @@ -255,7 +255,7 @@ public static bool CanSetSequence( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the property being configured. @@ -285,7 +285,7 @@ public static PropertyBuilder UseIdentityColumn( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the property being configured. @@ -304,7 +304,7 @@ public static PropertyBuilder UseIdentityColumn( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the column being configured. @@ -330,7 +330,7 @@ public static ColumnBuilder UseIdentityColumn( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The type of the property being configured. @@ -350,7 +350,7 @@ public static PropertyBuilder UseIdentityColumn( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The type of the property being configured. @@ -370,7 +370,7 @@ public static PropertyBuilder UseIdentityColumn( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The type of the property being configured. @@ -389,7 +389,7 @@ public static ColumnBuilder UseIdentityColumn( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the property being configured. @@ -418,7 +418,7 @@ public static ColumnBuilder UseIdentityColumn( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the property being configured. @@ -449,7 +449,7 @@ public static ColumnBuilder UseIdentityColumn( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the property being configured. @@ -468,7 +468,7 @@ public static bool CanSetIdentityColumnSeed( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the property being configured. @@ -493,7 +493,7 @@ public static bool CanSetIdentityColumnSeed( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the property being configured. @@ -522,7 +522,7 @@ public static bool CanSetIdentityColumnSeed( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the property being configured. @@ -553,7 +553,7 @@ public static bool CanSetIdentityColumnSeed( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the property being configured. @@ -572,7 +572,7 @@ public static bool CanSetIdentityColumnIncrement( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the property being configured. @@ -597,7 +597,7 @@ public static bool CanSetIdentityColumnIncrement( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the property being configured. @@ -647,7 +647,7 @@ public static bool CanSetIdentityColumnIncrement( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the property being configured. @@ -684,7 +684,7 @@ public static bool CanSetIdentityColumnIncrement( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the property being configured. @@ -705,7 +705,7 @@ public static bool CanSetValueGenerationStrategy( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// The builder for the property being configured. @@ -732,7 +732,7 @@ public static bool CanSetValueGenerationStrategy( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. Also see /// Sparse columns for /// general information on SQL Server sparse columns. @@ -752,7 +752,7 @@ public static PropertyBuilder IsSparse(this PropertyBuilder propertyBuilder, boo /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. Also see /// Sparse columns for /// general information on SQL Server sparse columns. @@ -770,7 +770,7 @@ public static PropertyBuilder IsSparse( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. Also see /// Sparse columns for /// general information on SQL Server sparse columns. @@ -799,7 +799,7 @@ public static PropertyBuilder IsSparse( /// /// /// See Modeling entity types and relationships, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. Also see /// Sparse columns for /// general information on SQL Server sparse columns. diff --git a/src/EFCore.SqlServer/Extensions/SqlServerPropertyExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerPropertyExtensions.cs index 5251dbdabbf..079a85b724e 100644 --- a/src/EFCore.SqlServer/Extensions/SqlServerPropertyExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/SqlServerPropertyExtensions.cs @@ -12,7 +12,7 @@ namespace Microsoft.EntityFrameworkCore; /// /// /// See Modeling entity types and relationships, and -/// Accessing SQL Server and SQL Azure databases with EF Core +/// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// public static class SqlServerPropertyExtensions diff --git a/src/EFCore.SqlServer/Extensions/SqlServerServiceCollectionExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerServiceCollectionExtensions.cs index a994c313168..12f2d4cf882 100644 --- a/src/EFCore.SqlServer/Extensions/SqlServerServiceCollectionExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/SqlServerServiceCollectionExtensions.cs @@ -45,7 +45,7 @@ public static class SqlServerServiceCollectionExtensions /// /// /// See Using DbContextOptions, and - /// Accessing SQL Server and SQL Azure databases with EF Core + /// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// diff --git a/src/EFCore.SqlServer/Infrastructure/Internal/SqlServerOptionsExtension.cs b/src/EFCore.SqlServer/Infrastructure/Internal/SqlServerOptionsExtension.cs index 50c0d07e894..69a5c9802a7 100644 --- a/src/EFCore.SqlServer/Infrastructure/Internal/SqlServerOptionsExtension.cs +++ b/src/EFCore.SqlServer/Infrastructure/Internal/SqlServerOptionsExtension.cs @@ -11,10 +11,11 @@ namespace Microsoft.EntityFrameworkCore.SqlServer.Infrastructure.Internal; /// 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. /// -public class SqlServerOptionsExtension : RelationalOptionsExtension +public class SqlServerOptionsExtension : RelationalOptionsExtension, IDbContextOptionsExtension { private DbContextOptionsExtensionInfo? _info; private int? _compatibilityLevel; + private bool? _azureSql; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -52,6 +53,7 @@ protected SqlServerOptionsExtension(SqlServerOptionsExtension copyFrom) : base(copyFrom) { _compatibilityLevel = copyFrom._compatibilityLevel; + _azureSql = copyFrom._azureSql; } /// @@ -105,6 +107,50 @@ public virtual SqlServerOptionsExtension WithCompatibilityLevel(int? compatibili return clone; } + /// + /// 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. + /// + public virtual bool IsAzureSql + => _azureSql ?? IsAzureSqlDefault; + + private bool IsAzureSqlDefault + => (ConnectionString ?? Connection?.ConnectionString) + ?.Contains(".database.windows.net", StringComparison.InvariantCultureIgnoreCase) == true; + + /// + /// 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. + /// + public virtual SqlServerOptionsExtension WithAzureSql(bool enable) + { + var clone = (SqlServerOptionsExtension)Clone(); + + clone._azureSql = enable; + + return clone; + } + + /// + public virtual IDbContextOptionsExtension ApplyDefaults(IDbContextOptions options) + { + if (!IsAzureSql) + { + return this; + } + + if (ExecutionStrategyFactory == null) + { + return WithExecutionStrategyFactory(c => new SqlServerRetryingExecutionStrategy(c)); + } + + return this; + } + /// /// 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 diff --git a/src/EFCore.SqlServer/Infrastructure/SqlServerDbContextOptionsBuilder.cs b/src/EFCore.SqlServer/Infrastructure/SqlServerDbContextOptionsBuilder.cs index 066f6eccc03..16e9221fbc8 100644 --- a/src/EFCore.SqlServer/Infrastructure/SqlServerDbContextOptionsBuilder.cs +++ b/src/EFCore.SqlServer/Infrastructure/SqlServerDbContextOptionsBuilder.cs @@ -29,7 +29,7 @@ public SqlServerDbContextOptionsBuilder(DbContextOptionsBuilder optionsBuilder) /// /// /// - /// This strategy is specifically tailored to SQL Server (including SQL Azure). It is pre-configured with + /// This strategy is specifically tailored to SQL Server (including Azure SQL). It is pre-configured with /// error numbers for transient errors that can be retried. /// /// @@ -48,7 +48,7 @@ public virtual SqlServerDbContextOptionsBuilder EnableRetryOnFailure() /// /// /// - /// This strategy is specifically tailored to SQL Server (including SQL Azure). It is pre-configured with + /// This strategy is specifically tailored to SQL Server (including Azure SQL). It is pre-configured with /// error numbers for transient errors that can be retried. /// /// @@ -67,7 +67,7 @@ public virtual SqlServerDbContextOptionsBuilder EnableRetryOnFailure(int maxRetr /// /// /// - /// This strategy is specifically tailored to SQL Server (including SQL Azure). It is pre-configured with + /// This strategy is specifically tailored to SQL Server (including Azure SQL). It is pre-configured with /// error numbers for transient errors that can be retried. /// /// @@ -87,7 +87,7 @@ public virtual SqlServerDbContextOptionsBuilder EnableRetryOnFailure(ICollection /// /// /// - /// This strategy is specifically tailored to SQL Server (including SQL Azure). It is pre-configured with + /// This strategy is specifically tailored to SQL Server (including Azure SQL). It is pre-configured with /// error numbers for transient errors that can be retried, but additional error numbers can also be supplied. /// /// @@ -116,4 +116,11 @@ public virtual SqlServerDbContextOptionsBuilder EnableRetryOnFailure( /// to have null resource public virtual SqlServerDbContextOptionsBuilder UseCompatibilityLevel(int compatibilityLevel) => WithOption(e => e.WithCompatibilityLevel(compatibilityLevel)); + + /// + /// Configures the context to use defaults optimized for Azure SQL, including retries on errors. + /// + /// Whether the defaults should be enabled. + public virtual SqlServerDbContextOptionsBuilder UseAzureSql(bool enable = true) + => WithOption(e => e.WithAzureSql(enable)); } diff --git a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerConventionSetBuilder.cs b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerConventionSetBuilder.cs index 3638209f525..2ad3a9237be 100644 --- a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerConventionSetBuilder.cs +++ b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerConventionSetBuilder.cs @@ -18,7 +18,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Conventions; /// /// /// See Model building conventions, and -/// Accessing SQL Server and SQL Azure databases with EF Core +/// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// diff --git a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerDbFunctionConvention.cs b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerDbFunctionConvention.cs index 872b6ee91c1..0a256c30604 100644 --- a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerDbFunctionConvention.cs +++ b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerDbFunctionConvention.cs @@ -11,7 +11,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Conventions; /// /// /// See Model building conventions, and -/// Accessing SQL Server and SQL Azure databases with EF Core +/// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// public class SqlServerDbFunctionConvention : IModelFinalizingConvention diff --git a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerIndexConvention.cs b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerIndexConvention.cs index 16df1332b66..b83cc49b1d7 100644 --- a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerIndexConvention.cs +++ b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerIndexConvention.cs @@ -13,7 +13,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Conventions; /// /// /// See Model building conventions, and -/// Accessing SQL Server and SQL Azure databases with EF Core +/// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// public class SqlServerIndexConvention : diff --git a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerMemoryOptimizedTablesConvention.cs b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerMemoryOptimizedTablesConvention.cs index 0f71d79bb53..5dc3c3705bc 100644 --- a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerMemoryOptimizedTablesConvention.cs +++ b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerMemoryOptimizedTablesConvention.cs @@ -11,7 +11,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Conventions; /// /// /// See Model building conventions, and -/// Accessing SQL Server and SQL Azure databases with EF Core +/// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// public class SqlServerMemoryOptimizedTablesConvention : diff --git a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerOnDeleteConvention.cs b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerOnDeleteConvention.cs index 3c29be31606..d250176508b 100644 --- a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerOnDeleteConvention.cs +++ b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerOnDeleteConvention.cs @@ -11,7 +11,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Conventions; /// /// /// See Model building conventions, and -/// Accessing SQL Server and SQL Azure databases with EF Core +/// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// public class SqlServerOnDeleteConvention : CascadeDeleteConvention, diff --git a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerOutputClauseConvention.cs b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerOutputClauseConvention.cs index eb128e7620c..0be9eb80f6c 100644 --- a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerOutputClauseConvention.cs +++ b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerOutputClauseConvention.cs @@ -9,7 +9,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Conventions; /// /// /// See Model building conventions, and -/// Accessing SQL Server and SQL Azure databases with EF Core +/// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// public class SqlServerOutputClauseConvention : ITriggerAddedConvention, ITriggerRemovedConvention diff --git a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerRuntimeModelConvention.cs b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerRuntimeModelConvention.cs index e9118e97711..b821f0a0a21 100644 --- a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerRuntimeModelConvention.cs +++ b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerRuntimeModelConvention.cs @@ -12,7 +12,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Conventions; /// /// /// See Model building conventions, and -/// Accessing SQL Server and SQL Azure databases with EF Core +/// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// public class SqlServerRuntimeModelConvention : RelationalRuntimeModelConvention @@ -101,6 +101,8 @@ protected override void ProcessIndexAnnotations( annotations.Remove(SqlServerAnnotationNames.CreatedOnline); annotations.Remove(SqlServerAnnotationNames.Include); annotations.Remove(SqlServerAnnotationNames.FillFactor); + annotations.Remove(SqlServerAnnotationNames.SortInTempDb); + annotations.Remove(SqlServerAnnotationNames.DataCompression); } } diff --git a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerSharedTableConvention.cs b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerSharedTableConvention.cs index 3c26013b06a..d40b142c7e2 100644 --- a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerSharedTableConvention.cs +++ b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerSharedTableConvention.cs @@ -11,7 +11,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Conventions; /// /// /// See Model building conventions, and -/// Accessing SQL Server and SQL Azure databases with EF Core +/// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// public class SqlServerSharedTableConvention : SharedTableConvention diff --git a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerStoreGenerationConvention.cs b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerStoreGenerationConvention.cs index 090821f4581..01e251ed153 100644 --- a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerStoreGenerationConvention.cs +++ b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerStoreGenerationConvention.cs @@ -13,7 +13,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Conventions; /// /// /// See Model building conventions, and -/// Accessing SQL Server and SQL Azure databases with EF Core +/// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// public class SqlServerStoreGenerationConvention : StoreGenerationConvention diff --git a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerTemporalConvention.cs b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerTemporalConvention.cs index 722eca989e2..32bc4f23ca1 100644 --- a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerTemporalConvention.cs +++ b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerTemporalConvention.cs @@ -10,7 +10,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Conventions; /// /// /// See Model building conventions, and -/// Accessing SQL Server and SQL Azure databases with EF Core +/// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// public class SqlServerTemporalConvention : IEntityTypeAnnotationChangedConvention, diff --git a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerValueGenerationConvention.cs b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerValueGenerationConvention.cs index 338cd8c1fd4..b7a201bed31 100644 --- a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerValueGenerationConvention.cs +++ b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerValueGenerationConvention.cs @@ -15,7 +15,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Conventions; /// /// /// See Model building conventions, and -/// Accessing SQL Server and SQL Azure databases with EF Core +/// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// public class SqlServerValueGenerationConvention : RelationalValueGenerationConvention diff --git a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerValueGenerationStrategyConvention.cs b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerValueGenerationStrategyConvention.cs index 2e58ef8dde1..8085d6f5027 100644 --- a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerValueGenerationStrategyConvention.cs +++ b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerValueGenerationStrategyConvention.cs @@ -11,7 +11,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Conventions; /// /// /// See Model building conventions, and -/// Accessing SQL Server and SQL Azure databases with EF Core +/// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// public class SqlServerValueGenerationStrategyConvention : IModelInitializedConvention, IModelFinalizingConvention diff --git a/src/EFCore.SqlServer/Metadata/Internal/SqlServerAnnotationNames.cs b/src/EFCore.SqlServer/Metadata/Internal/SqlServerAnnotationNames.cs index 8bf33226c28..c7905166617 100644 --- a/src/EFCore.SqlServer/Metadata/Internal/SqlServerAnnotationNames.cs +++ b/src/EFCore.SqlServer/Metadata/Internal/SqlServerAnnotationNames.cs @@ -51,6 +51,22 @@ public static class SqlServerAnnotationNames /// public const string FillFactor = Prefix + "FillFactor"; + /// + /// 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. + /// + public const string SortInTempDb = Prefix + "SortInTempDb"; + + /// + /// 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. + /// + public const string DataCompression = Prefix + "DataCompression"; + /// /// 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 diff --git a/src/EFCore.SqlServer/Metadata/Internal/SqlServerAnnotationProvider.cs b/src/EFCore.SqlServer/Metadata/Internal/SqlServerAnnotationProvider.cs index 40ed99df85c..deec2eb5561 100644 --- a/src/EFCore.SqlServer/Metadata/Internal/SqlServerAnnotationProvider.cs +++ b/src/EFCore.SqlServer/Metadata/Internal/SqlServerAnnotationProvider.cs @@ -203,6 +203,16 @@ public override IEnumerable For(ITableIndex index, bool designTime) { yield return new Annotation(SqlServerAnnotationNames.FillFactor, fillFactor); } + + if (modelIndex.GetSortInTempDb(table) is bool sortInTempDb) + { + yield return new Annotation(SqlServerAnnotationNames.SortInTempDb, sortInTempDb); + } + + if (modelIndex.GetDataCompression(table) is DataCompressionType dataCompressionType) + { + yield return new Annotation(SqlServerAnnotationNames.DataCompression, dataCompressionType); + } } /// diff --git a/src/EFCore.SqlServer/Metadata/Internal/SqlServerIndexExtensions.cs b/src/EFCore.SqlServer/Metadata/Internal/SqlServerIndexExtensions.cs index 49f21891482..416f1064329 100644 --- a/src/EFCore.SqlServer/Metadata/Internal/SqlServerIndexExtensions.cs +++ b/src/EFCore.SqlServer/Metadata/Internal/SqlServerIndexExtensions.cs @@ -100,6 +100,40 @@ public static bool AreCompatibleForSqlServer( return false; } + if (index.GetSortInTempDb() != duplicateIndex.GetSortInTempDb()) + { + if (shouldThrow) + { + throw new InvalidOperationException( + SqlServerStrings.DuplicateIndexSortInTempDbMismatch( + index.DisplayName(), + index.DeclaringEntityType.DisplayName(), + duplicateIndex.DisplayName(), + duplicateIndex.DeclaringEntityType.DisplayName(), + index.DeclaringEntityType.GetSchemaQualifiedTableName(), + index.GetDatabaseName(storeObject))); + } + + return false; + } + + if (index.GetDataCompression() != duplicateIndex.GetDataCompression()) + { + if (shouldThrow) + { + throw new InvalidOperationException( + SqlServerStrings.DuplicateIndexDataCompressionMismatch( + index.DisplayName(), + index.DeclaringEntityType.DisplayName(), + duplicateIndex.DisplayName(), + duplicateIndex.DeclaringEntityType.DisplayName(), + index.DeclaringEntityType.GetSchemaQualifiedTableName(), + index.GetDatabaseName(storeObject))); + } + + return false; + } + return true; static bool SameColumnNames(IReadOnlyIndex index, IReadOnlyIndex duplicateIndex, StoreObjectIdentifier storeObject) diff --git a/src/EFCore.SqlServer/Metadata/SqlServerValueGenerationStrategy.cs b/src/EFCore.SqlServer/Metadata/SqlServerValueGenerationStrategy.cs index e90240b9cee..8e88482a994 100644 --- a/src/EFCore.SqlServer/Metadata/SqlServerValueGenerationStrategy.cs +++ b/src/EFCore.SqlServer/Metadata/SqlServerValueGenerationStrategy.cs @@ -11,7 +11,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata; /// /// /// See Model building conventions, and -/// Accessing SQL Server and SQL Azure databases with EF Core +/// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// public enum SqlServerValueGenerationStrategy diff --git a/src/EFCore.SqlServer/Migrations/Operations/SqlServerCreateDatabaseOperation.cs b/src/EFCore.SqlServer/Migrations/Operations/SqlServerCreateDatabaseOperation.cs index 05ecc66db12..6be93893200 100644 --- a/src/EFCore.SqlServer/Migrations/Operations/SqlServerCreateDatabaseOperation.cs +++ b/src/EFCore.SqlServer/Migrations/Operations/SqlServerCreateDatabaseOperation.cs @@ -8,7 +8,7 @@ namespace Microsoft.EntityFrameworkCore.Migrations.Operations; /// /// /// See Database migrations, and -/// Accessing SQL Server and SQL Azure databases with EF Core +/// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// [DebuggerDisplay("CREATE DATABASE {Name}")] diff --git a/src/EFCore.SqlServer/Migrations/Operations/SqlServerDropDatabaseOperation.cs b/src/EFCore.SqlServer/Migrations/Operations/SqlServerDropDatabaseOperation.cs index 0cc5675a2e0..6d638a91f23 100644 --- a/src/EFCore.SqlServer/Migrations/Operations/SqlServerDropDatabaseOperation.cs +++ b/src/EFCore.SqlServer/Migrations/Operations/SqlServerDropDatabaseOperation.cs @@ -8,7 +8,7 @@ namespace Microsoft.EntityFrameworkCore.Migrations.Operations; /// /// /// See Database migrations, and -/// Accessing SQL Server and SQL Azure databases with EF Core +/// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// public class SqlServerDropDatabaseOperation : MigrationOperation diff --git a/src/EFCore.SqlServer/Migrations/SqlServerMigrationsSqlGenerator.cs b/src/EFCore.SqlServer/Migrations/SqlServerMigrationsSqlGenerator.cs index ded9236cb74..e478aad78de 100644 --- a/src/EFCore.SqlServer/Migrations/SqlServerMigrationsSqlGenerator.cs +++ b/src/EFCore.SqlServer/Migrations/SqlServerMigrationsSqlGenerator.cs @@ -23,7 +23,7 @@ namespace Microsoft.EntityFrameworkCore.Migrations; /// /// /// See Database migrations, and -/// Accessing SQL Server and SQL Azure databases with EF Core +/// Accessing SQL Server and Azure SQL databases with EF Core /// for more information and examples. /// /// @@ -1851,6 +1851,27 @@ private static void IndexWithOptions(CreateIndexOperation operation, MigrationCo options.Add("ONLINE = ON"); } + if (operation[SqlServerAnnotationNames.SortInTempDb] is bool sortInTempDb && sortInTempDb) + { + options.Add("SORT_IN_TEMPDB = ON"); + } + + if (operation[SqlServerAnnotationNames.DataCompression] is DataCompressionType dataCompressionType) + { + switch (dataCompressionType) + { + case DataCompressionType.None: + options.Add("DATA_COMPRESSION = NONE"); + break; + case DataCompressionType.Row: + options.Add("DATA_COMPRESSION = ROW"); + break; + case DataCompressionType.Page: + options.Add("DATA_COMPRESSION = PAGE"); + break; + } + } + if (options.Count > 0) { builder diff --git a/src/EFCore.SqlServer/Properties/SqlServerStrings.Designer.cs b/src/EFCore.SqlServer/Properties/SqlServerStrings.Designer.cs index 22c3a3a7fe8..a36445d1869 100644 --- a/src/EFCore.SqlServer/Properties/SqlServerStrings.Designer.cs +++ b/src/EFCore.SqlServer/Properties/SqlServerStrings.Designer.cs @@ -99,6 +99,14 @@ public static string DuplicateIndexClusteredMismatch(object? index1, object? ent GetString("DuplicateIndexClusteredMismatch", nameof(index1), nameof(entityType1), nameof(index2), nameof(entityType2), nameof(table), nameof(indexName)), index1, entityType1, index2, entityType2, table, indexName); + /// + /// The indexes {index1} on '{entityType1}' and {index2} on '{entityType2}' are both mapped to '{table}.{indexName}', but have different data compression configurations. + /// + public static string DuplicateIndexDataCompressionMismatch(object? index1, object? entityType1, object? index2, object? entityType2, object? table, object? indexName) + => string.Format( + GetString("DuplicateIndexDataCompressionMismatch", nameof(index1), nameof(entityType1), nameof(index2), nameof(entityType2), nameof(table), nameof(indexName)), + index1, entityType1, index2, entityType2, table, indexName); + /// /// The indexes {index1} on '{entityType1}' and {index2} on '{entityType2}' are both mapped to '{table}.{indexName}', but have different fill factor configurations. /// @@ -123,6 +131,14 @@ public static string DuplicateIndexOnlineMismatch(object? index1, object? entity GetString("DuplicateIndexOnlineMismatch", nameof(index1), nameof(entityType1), nameof(index2), nameof(entityType2), nameof(table), nameof(indexName)), index1, entityType1, index2, entityType2, table, indexName); + /// + /// The indexes {index1} on '{entityType1}' and {index2} on '{entityType2}' are both mapped to '{table}.{indexName}', but have different sort in tempdb configurations. + /// + public static string DuplicateIndexSortInTempDbMismatch(object? index1, object? entityType1, object? index2, object? entityType2, object? table, object? indexName) + => string.Format( + GetString("DuplicateIndexSortInTempDbMismatch", nameof(index1), nameof(entityType1), nameof(index2), nameof(entityType2), nameof(table), nameof(indexName)), + index1, entityType1, index2, entityType2, table, indexName); + /// /// The keys {key1} on '{entityType1}' and {key2} on '{entityType2}' are both mapped to '{table}.{keyName}', but have different clustering configurations. /// diff --git a/src/EFCore.SqlServer/Properties/SqlServerStrings.resx b/src/EFCore.SqlServer/Properties/SqlServerStrings.resx index beb38d33c4e..59c92b4e2b0 100644 --- a/src/EFCore.SqlServer/Properties/SqlServerStrings.resx +++ b/src/EFCore.SqlServer/Properties/SqlServerStrings.resx @@ -156,6 +156,12 @@ The indexes {index1} on '{entityType1}' and {index2} on '{entityType2}' are both mapped to '{table}.{indexName}', but have different online configurations. + + The indexes {index1} on '{entityType1}' and {index2} on '{entityType2}' are both mapped to '{table}.{indexName}', but have different sort in tempdb configurations. + + + The indexes {index1} on '{entityType1}' and {index2} on '{entityType2}' are both mapped to '{table}.{indexName}', but have different data compression configurations. + The keys {key1} on '{entityType1}' and {key2} on '{entityType2}' are both mapped to '{table}.{keyName}', but have different clustering configurations. diff --git a/src/EFCore.SqlServer/Query/Internal/SqlServerJsonPostprocessor.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerJsonPostprocessor.cs index b7499aa02a3..b7fdcb9111a 100644 --- a/src/EFCore.SqlServer/Query/Internal/SqlServerJsonPostprocessor.cs +++ b/src/EFCore.SqlServer/Query/Internal/SqlServerJsonPostprocessor.cs @@ -92,12 +92,9 @@ public virtual Expression Process(Expression expression) table = table switch { - InnerJoinExpression ij => ij.Update(newOpenJsonExpression, ij.JoinPredicate), - LeftJoinExpression lj => lj.Update(newOpenJsonExpression, lj.JoinPredicate), - CrossJoinExpression cj => cj.Update(newOpenJsonExpression), - CrossApplyExpression ca => ca.Update(newOpenJsonExpression), - OuterApplyExpression oa => oa.Update(newOpenJsonExpression), - _ => newOpenJsonExpression, + JoinExpressionBase j => j.Update(newOpenJsonExpression), + SqlServerOpenJsonExpression => newOpenJsonExpression, + _ => throw new UnreachableException() }; foreach (var columnInfo in openJsonExpression.ColumnInfos!) @@ -239,8 +236,10 @@ SqlExpression RewriteOpenJsonColumn( columnExpression.Table, "value", columnExpression.Type, _nvarcharMaxTypeMapping, columnExpression.IsNullable); Check.DebugAssert(columnInfo.Path is not null, "Path shouldn't be null in OPENJSON WITH"); - Check.DebugAssert(!columnInfo.AsJson, "AS JSON signifies an owned sub-entity being projected out of OPENJSON/WITH. " - + "Columns referring to that must be wrapped be Json{Scalar,Query}Expression and will have been already dealt with above"); + //Check.DebugAssert( + // !columnInfo.AsJson || columnInfo.TypeMapping.ElementTypeMapping is not null, + // "AS JSON signifies an owned sub-entity or array of primitives being projected out of OPENJSON/WITH. " + // + "Columns referring to sub-entities must be wrapped in Json{Scalar,Query}Expression and will have been already dealt with above"); if (columnInfo.Path is []) { diff --git a/src/EFCore.SqlServer/Query/Internal/SqlServerQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerQueryableMethodTranslatingExpressionVisitor.cs index 73ec9a2759b..7a725559658 100644 --- a/src/EFCore.SqlServer/Query/Internal/SqlServerQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.SqlServer/Query/Internal/SqlServerQueryableMethodTranslatingExpressionVisitor.cs @@ -167,10 +167,7 @@ protected override Expression VisitExtension(Expression extensionExpression) // which case we only have the CLR type (note that we cannot produce different SQLs based on the nullability of an *element* in // a parameter collection - our caching mechanism only supports varying by the nullability of the parameter itself (i.e. the // collection). - // TODO: if property is non-null, GetElementType() should never be null, but we have #31469 for shadow properties - var isElementNullable = property?.GetElementType() is null - ? elementClrType.IsNullableType() - : property.GetElementType()!.IsNullable; + var isElementNullable = property?.GetElementType()!.IsNullable; #pragma warning disable EF1001 // Internal EF Core API usage. var selectExpression = new SelectExpression( @@ -245,7 +242,8 @@ protected override ShapedQueryExpression TransformJsonQueryToTable(JsonQueryExpr { Name = jsonPropertyName, TypeMapping = property.GetRelationalTypeMapping(), - Path = new PathSegment[] { new(jsonPropertyName) } + Path = new PathSegment[] { new(jsonPropertyName) }, + AsJson = property.GetRelationalTypeMapping().ElementTypeMapping is not null }); } } @@ -606,14 +604,14 @@ protected virtual SqlServerOpenJsonExpression ApplyTypeMappingsOnOpenJsonExpress // First, find the collection type mapping and apply it to the parameter if (_typeMappingSource.FindMapping(parameterExpression.Type, Model, elementTypeMapping) is not SqlServerStringTypeMapping - parameterTypeMapping) + { + ElementTypeMapping: not null + } + parameterTypeMapping) { - // TODO: Message - throw new InvalidOperationException("Type mapping for 'string' could not be found or was not a SqlServerStringTypeMapping"); + throw new UnreachableException("A SqlServerStringTypeMapping collection type mapping could not be found"); } - Check.DebugAssert(parameterTypeMapping.ElementTypeMapping != null, "Collection type mapping missing element mapping."); - return openJsonExpression.Update( parameterExpression.ApplyTypeMapping(parameterTypeMapping), path: null, diff --git a/src/EFCore.SqlServer/README.md b/src/EFCore.SqlServer/README.md new file mode 100644 index 00000000000..e1540d20a2b --- /dev/null +++ b/src/EFCore.SqlServer/README.md @@ -0,0 +1,35 @@ +Microsoft.EntityFrameworkCore.SqlServer is the database provider for Microsoft SQL Server and Azure SQL. This providers allows you to use Entity Framework Core with Microsoft SQL Server and Azure SQL databases. + +## Getting started + +`Microsoft.EntityFrameworkCore.SqlServer` is the EF Core provider for Microsoft SQL Server and Azure SQL. See [Getting Started](https://learn.microsoft.com/ef/core/modeling/#use-fluent-api-to-configure-a-model) for more information. + +### Prerequisites + +- Supported database Engines: Microsoft SQL Server (2012 onwards) +- The provider references Microsoft.Data.SqlClient (not System.Data.SqlClient). If your project takes a direct dependency on SqlClient, make sure it references the Microsoft.Data.SqlClient package. + +## Usage + +Once you've installed the package, you can use it in your Entity Framework Core application by specifying the SQL Server provider in your DbContext's OnConfiguring method: + +```csharp +protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) +{ + optionsBuilder.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=MyDatabase;Trusted_Connection=True;"); +} +``` + +In this example, we're using the (localdb)\mssqllocaldb server with the MyDatabase database. You'll need to adjust the connection string to match your own SQL Server instance and database. + +## Features + +The SQL Server provider supports all common features of [Entity Framework Core](https://learn.microsoft.com/ef/core/) as well as some [SQL Server-specific features](https://learn.microsoft.com/ef/core/providers/sql-server/?tabs=dotnet-core-cli) including temporal tables and memory-optimized tables. + +## Additional documentation + +For more information on using the SQL Server provider for Entity Framework Core, you can refer to the official [documentation](https://learn.microsoft.com/en-us/ef/core/providers/sql-server/?tabs=dotnet-core-cli). + +## Feedback + +If you encounter a bug or would like to request a feature, [submit an Github issue](https://github.com/dotnet/efcore/issues/new/choose). For more details, see [getting support](https://github.com/dotnet/efcore/blob/main/.github/SUPPORT.md). \ No newline at end of file diff --git a/src/EFCore.SqlServer/SqlServerRetryingExecutionStrategy.cs b/src/EFCore.SqlServer/SqlServerRetryingExecutionStrategy.cs index b621c7be216..498be2db2b3 100644 --- a/src/EFCore.SqlServer/SqlServerRetryingExecutionStrategy.cs +++ b/src/EFCore.SqlServer/SqlServerRetryingExecutionStrategy.cs @@ -12,7 +12,7 @@ namespace Microsoft.EntityFrameworkCore; /// /// /// -/// This strategy is specifically tailored to SQL Server (including SQL Azure). It is pre-configured with +/// This strategy is specifically tailored to SQL Server (including Azure SQL). It is pre-configured with /// error numbers for transient errors that can be retried. Additional error numbers to retry on can also be supplied. /// /// @@ -30,6 +30,11 @@ public class SqlServerRetryingExecutionStrategy : ExecutionStrategy { private readonly HashSet? _additionalErrorNumbers; + /// + /// The default minimum time delay between retries for Azure SQL. + /// + protected static readonly TimeSpan DefaultMinDelayAzureSql = TimeSpan.FromSeconds(5); + /// /// Creates a new instance of . /// @@ -138,6 +143,11 @@ public SqlServerRetryingExecutionStrategy( _additionalErrorNumbers = errorNumbersToAdd?.ToHashSet(); } + /// + /// Additional SQL error numbers that should be considered transient. + /// + public virtual IEnumerable? AdditionalErrorNumbers => _additionalErrorNumbers; + /// /// Determines whether the specified exception represents a transient failure that can be /// compensated by a retry. Additional exceptions to retry on can be passed to the constructor. @@ -181,7 +191,9 @@ protected override bool ShouldRetryOn(Exception exception) return CallOnWrappedException(lastException, IsMemoryOptimizedError) ? TimeSpan.FromMilliseconds(baseDelay.Value.TotalSeconds) - : baseDelay; + : CallOnWrappedException(lastException, IsThrottlingError) + ? baseDelay + DefaultMinDelayAzureSql + : baseDelay; } private static bool IsMemoryOptimizedError(Exception exception) @@ -204,4 +216,58 @@ private static bool IsMemoryOptimizedError(Exception exception) return false; } + + private static bool IsThrottlingError(Exception exception) + { + if (exception is SqlException sqlException) + { + foreach (SqlError err in sqlException.Errors) + { + switch (err.Number) + { + case 49977: + case 49920: + case 49919: + case 49918: + case 45319: + case 45182: + case 45161: + case 45157: + case 45156: + case 41840: + case 41823: + case 40903: + case 40890: + case 40675: + case 40648: + case 40642: + case 40613: + case 40501: + case 40189: + case 39110: + case 39108: + case 37327: + case 30085: + case 25740: + case 25738: + case 22498: + case 22335: + case 17889: + case 14355: + case 10930: + case 10929: + case 9985: + case 3950: + case 3935: + case 1404: + case 1204: + case 233: + case -2: + return true; + } + } + } + + return false; + } } diff --git a/src/EFCore.SqlServer/Storage/Internal/SqlServerTransientExceptionDetector.cs b/src/EFCore.SqlServer/Storage/Internal/SqlServerTransientExceptionDetector.cs index 92d26598bce..e41457194f3 100644 --- a/src/EFCore.SqlServer/Storage/Internal/SqlServerTransientExceptionDetector.cs +++ b/src/EFCore.SqlServer/Storage/Internal/SqlServerTransientExceptionDetector.cs @@ -1,7 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Text.RegularExpressions; using Microsoft.Data.SqlClient; +using static System.Net.Mime.MediaTypeNames; namespace Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal; @@ -24,6 +26,12 @@ public static bool ShouldRetryOn(Exception? ex) { switch (err.Number) { + // SQL Error Code: 49983 + // The '%ls' operation failed to complete. Retry the operation. Create a support request if the retry attempts do not succeed. + case 49983: + // SQL Error Code: 49977 + // The elastic pool '%ls' is busy with another operation. Please wait until the ongoing operation finishes and try again. + case 49977: // SQL Error Code: 49920 // Cannot process request. Too many operations in progress for subscription "%ld". // The service is busy processing multiple requests for this subscription. @@ -39,11 +47,95 @@ public static bool ShouldRetryOn(Exception? ex) case 49919: // SQL Error Code: 49918 // Cannot process request. Not enough resources to process request. - // The service is currently busy.Please retry the request later. + // The service is currently busy. Please retry the request later. case 49918: + // SQL Error Code: 49802 + // Database is unavailable at the moment, please retry connection at later time. + case 49802: + // SQL Error Code: 49510 + // Managed instance is busy with another operation. Please try your operation later. + case 49510: + // SQL Error Code: 47139 + // Join contained availability group '%.*ls' failed to create group master '%.*ls' Database ID. Please retry the operation again. + case 47139: + // SQL Error Code: 47137 + // Cannot create contained system databases in contained availability group '%.*ls' It might be caused by + // temporary condition. Retry the operation. + case 47137: + // SQL Error Code: 47132 + // Joining availability group '%.*ls' with rebuilding contained system DB has failed because rebuilding contained MSDB + // has failed. This is caused by contained MSDB is still used. Retry the operation later. + case 47132: + // SQL Error Code: 45547 + // The create operation has timed out in one of the backend workflows. Please retry the operation. + case 45547: + // SQL Error Code: 45319 + // The service objective assignment for database '%.*ls' on server '%.*ls' could not be completed as the database + // is too busy. Reduce the workload before initiating another service objective update. + case 45319: + // SQL Error Code: 45182 + // Database '%ls' is busy with another operation. Please try your operation later. + case 45182: + // SQL Error Code: 45179 + // The operation timed out and automatically rolled back. Please retry the operation. + case 45179: + // SQL Error Code: 45161 + // Managed instance '%.*ls' is busy with another operation. Please try your operation later. + case 45161: + // SQL Error Code: 45157 + // Server '%.*ls' is busy with another operation. Please try your operation later. + case 45157: + // SQL Error Code: 45156 + // Subscription '%.*ls' is busy with another operation. Please try your operation later. + case 45156: + // SQL Error Code: 45153 + // Management Service is not currently available. Please retry the operation later. If the problem persists, + // contact customer support, and provide them the session tracing ID of '%ls'. + case 45153: + // SQL Error Code: 42029 + // An internal error happened while generating a new DBTS for database %.*ls. Please retry the operation. + case 42029: + // SQL Error Code: 41840 + // Could not perform the operation because the elastic pool or managed instance has reached its quota for in-memory tables. + // This error may be transient. Please retry the operation. See 'http://go.microsoft.com/fwlink/?LinkID=623028' for more information. + case 41840: // SQL Error Code: 41839 // Transaction exceeded the maximum number of commit dependencies. case 41839: + // SQL Error Code: 41823 + // Could not perform the operation because the database has reached its quota for in-memory tables. This error may be transient. + // Please retry the operation. See 'http://go.microsoft.com/fwlink/?LinkID=623028' for more information. + case 41823: + // SQL Error Code: 41701 + // The Activation Context is unavailable at this time. The Windows Fabric Runtime is unavailable at this time, + // retry later. Wait for the activation context to become available, then retry. + case 41701: + // SQL Error Code: 41700 + // System views related to Windows Fabric partitions and replicas are not available at this time, + // because replica manager has not yet started. Wait for replica manager to start, then retry the system view query. + case 41700: + // SQL Error Code: 41640 + // Database '%ls' encountered a transient error (error code: 0x%08X) while performing task '%ls'. + // Refer to the SQL Server error log for information about the errors that were encountered. + // If this condition persists, contact the system administrator. + case 41640: + // SQL Error Code: 41619 + // Windows Fabric '%ls' (partition ID '%ls')encountered transient error %d while waiting for build replica operation + // on database '%ls' (ID %d). Refer to the SQL Server error log for information about the errors that were encountered. + // If this condition persists, contact the system administrator. + case 41619: + // SQL Error Code: 41614 + // Fabric Service '%ls' encountered a transient error while performing Windows Fabric operation on '%ls' database + // (ID %d). Refer to the SQL Server error log for information about the errors that were encountered. + // If this condition persists, contact the system administrator. + case 41614: + // SQL Error Code: 41383 + // An internal error occurred while running the DMV query. This was likely caused by concurrent DDL operations. Please retry the query. + case 41383: + // SQL Error Code: 41339 + // The table '%.*ls' has been created or altered after the start of the current transaction. The transaction was aborted. + // Please retry the transaction. + case 41339: // SQL Error Code: 41325 // The current transaction failed to commit due to a serializable validation failure. case 41325: @@ -56,30 +148,274 @@ public static bool ShouldRetryOn(Exception? ex) // SQL Error Code: 41301 // Dependency failure: a dependency was taken on another transaction that later failed to commit. case 41301: + // SQL Error Code: 40938 + // The Server DNS Alias '%.*ls' is busy with another operation and cannot perform the '%.*ls' operation. Please try again later. + case 40938: + // SQL Error Code: 40918 + // The Failover Group '%.*ls' is busy with another operation and cannot perform the '%.*ls' operation. Please try again later + case 40918: + // SQL Error Code: 40903 + // The server '%.*ls' is currently busy. Please wait a few minutes before trying again. + case 40903: + // SQL Error Code: 40890 + // The elastic pool is busy with another operation. + case 40890: + // SQL Error Code: 40675 + // The service is currently too busy. Please try again later. + case 40675: + // SQL Error Code: 40671 + // Unable to '%.*ls' '%.*ls' on server '%.*ls'. Please retry the connection later. + case 40671: + // SQL Error Code: 40648 + // Too many requests have been performed. Please retry later. + case 40648: + // SQL Error Code: 40642 + // The server is currently too busy. Please try again later. + case 40642: + // SQL Error Code: 40623 + // Reauthentication failed for login %.*ls. Within the past reauthentification interval, the login has become invalid + // due to a password change, a dropped login, or other cause. Please retry login. + case 40623: // SQL Error Code: 40613 // Database XXXX on server YYYY is not currently available. Please retry the connection later. // If the problem persists, contact customer support, and provide them the session tracing ID of ZZZZZ. case 40613: + // SQL Error Code: 40540 + // The service has encountered an error processing your request. Please try again. + case 40540: // SQL Error Code: 40501 // The service is currently busy. Retry the request after 10 seconds. Code: (reason code to be decoded). case 40501: // SQL Error Code: 40197 // The service has encountered an error processing your request. Please try again. case 40197: + // SQL Error Code: 40189 + // The resource quota for the current database has been exceeded and this request has been aborted. + // Please rerun your request in the next quota window. %s + case 40189: + // SQL Error Code: 40143 + // The service has encountered an error processing your request. Please try again. + case 40143: + // SQL Error Code: 40106 + // The schema scope set in the session is not the current schema scope for the current partition. Please rerun your query. + case 40106: + // SQL Error Code: 39152 + // Transient error communicating with the streaming runtime due to unfinished stop streaming job operation. Please retry the operation. + case 39152: + // SQL Error Code: 39151 + // Transient error communicating with the streaming runtime. Please retry the operation. + case 39151: + // SQL Error Code: 39110 + // Maximum number of concurrent external script queries for this user has been reached. Limit is %d. + // Please retry the operation. External script request id is %ls. + case 39110: + // SQL Error Code: 39108 + // Maximum number of concurrent external script users has been reached. Limit is %d. Please retry the operation. External script request id is %ls. + case 39108: + // SQL Error Code: 39025 + // External script execution failed as extensibility environment is not ready yet. Retry the operation when the server is fully started. + case 39025: + // SQL Error Code: 37327 + // Maximum number of concurrent DBCC commands running in the enclave has been reached. The maximum number of + // concurrent DBCC queries is %d. Try rerunning the query. + case 37327: + // SQL Error Code: 37202 + // An instance pool with name '%.*ls' is busy with another ongoing operation. + case 37202: + // SQL Error Code: 35293 + // Error in retrieving extended recovery forks from the primary replica. The extended-recovery-fork stack changed + // while being retrieved by the secondary replica. Retry the operation. + case 35293: + // SQL Error Code: 35256 + // The session timeout value was exceeded while waiting for a response from the other availability replica in the + // session. That replica or the network might be down, or the command might be misconfigured. If the partner is running + // and visible over the network, retry the command using correctly configured partner-connection parameters. + case 35256: + // SQL Error Code: 35218 + // An error occurred while trying to set the initial Backup LSN of database '%.*ls'. Primary database is temporarily + // offline due to restart or other transient condition. Retry the operation. + case 35218: + // SQL Error Code: 35216 + // An error occurred while adding or removing a log truncation holdup to build secondary replica from primary availability + // database '%.*ls'. Primary database is temporarily offline due to restart or other transient condition. Retry the operation. + case 35216: + // SQL Error Code: 33123 + // Cannot drop or alter the database encryption key since it is currently in use on a mirror or secondary availability replica. + // Retry the command after all the previous reencryption scans have propagated to the mirror or secondary availability replicas + // or after availability relationship has been disabled. + case 33123: + // SQL Error Code: 30085 + // A stoplist cache cannot be generated while processing a full-text query or performing full-text indexing. There is + // not enough memory to load the stoplist cache. Rerun the query or indexing command when more resources are available. + case 30085: + // SQL Error Code: 30080 + // The full-text population on table '%ls' cannot be started because the full-text catalog is importing data from + // existing catalogs. After the import operation finishes, rerun the command. + case 30080: + // SQL Error Code: 25740 + // Unable to start event session '%.*ls' because system is busy. Please try again later. + case 25740: + // SQL Error Code: 25738 + // Event session '%.*ls' could not be started because system is currently busy. Please try again later. + case 25738: + // SQL Error Code: 25003 + // Upgrade of the distribution database MSmerge_subscriptions table failed. Rerun the upgrade procedure in order + // to upgrade the distribution database. + case 25003: + // SQL Error Code: 22984 + // An error occurred while waiting on the log reader history cache event. This error is reported by the internal task + // scheduling and might be transient. Retry the operation. + case 22984: + // SQL Error Code: 22760 + // Aborting Synapse Link Publish task for partition %ld timed out. Retry this operation later. + case 22760: + // SQL Error Code: 22759 + // Aborting Synapse Link Snapshot task for table %ld timed out. Retry this operation later. + case 22759: + // SQL Error Code: 22758 + // Aborting Synapse Link Commit task for table group '%s' timed out. Retry this operation later. + case 22758: + // SQL Error Code: 22754 + // Aborting Synapse Link Capture task for this database timed out. Retry this operation later. + case 22754: + // SQL Error Code: 22498 + // There is not enough resources to perform the operation. Please retry your operation later. + case 22498: + // SQL Error Code: 22493 + // The database '%.*ls' failed to sync. Please retry the operation again. If the problem persists contact + // Microsoft Azure Customer Support. + case 22493: + // SQL Error Code: 22491 + // The DDL statement failed due to an internal error. Please retry the operation again. If the problem persists contact + // Microsoft Azure Customer Support. + case 22491: + // SQL Error Code: 22430 + // Operation failed as the Database '%.*ls' is shutting down. Please retry the operation again. If the problem persists + // contact Microsoft Azure Customer Support. + case 22430: + // SQL Error Code: 22427 + // Operation failed due to an error in a background task. Please retry the operation again. If the problem persists + // contact Microsoft Azure Customer Support. + case 22427: + // SQL Error Code: 22358 + // The Database Controller required for this operation was not found. Please retry the operation again. If the problem + // persists contact Microsoft Azure Customer Support. + case 22358: + // SQL Error Code: 22355 + // Lock manager does not exist. Please retry the operation again. If the problem persists contact + // Microsoft Azure Customer Support. + case 22355: + // SQL Error Code: 22353 + // The SQL instances has not been correctly setup to allow this operation. Please retry the operation again. + // If the problem persists contact Microsoft Azure Customer Support. + case 22353: + // SQL Error Code: 22335 + // Cannot obtain a LOCK resource at this time due to internal error. Rerun your statement when there are fewer active users. + case 22335: + // SQL Error Code: 22226 + // An internal error (%d, %d) occured. Please retry the operation again. If the problem persists contact + // Microsoft Azure Customer Support. + case 22226: + // SQL Error Code: 22225 + // An internal error (%d, %d) occured. Please retry the operation again. If the problem persists contact + // Microsoft Azure Customer Support. + case 22225: + // SQL Error Code: 21503 + // Cleanup of merge meta data cannot be performed while merge processes are running. Retry this operation after + // the merge processes have completed. + case 21503: // SQL Error Code: 20041 // Transaction rolled back. Could not execute trigger. Retry your transaction. case 20041: + // SQL Error Code: 19494 + // Automatic seeding of availability database '%ls' in availability group '%.*ls' failed with a transient error. + // The operation will be retried. + case 19494: + // SQL Error Code: 19416 + // One or more databases in availability group '%.*ls' are not synchronized. On a synchronous-commit availability + // replica, ALTER AVAILABILITY GROUP OFFLINE is not allowed when one or more databases are not + // synchronized. Wait for all databases to reach the SYNCHRONIZED state, and retry the command. + case 19416: + // SQL Error Code: 19413 + // An attempt to switch Always On Availability Groups to the local Windows Server Failover Clustering (WSFC) cluster + // context failed. This attempt failed because switching the cluster context back to the local cluster at this time + // might cause data loss because one or more secondary databases on synchronous-commit replicas are not in the + // SYNCHRONIZED state. Wait until all synchronous-commit secondary databases are synchronized, and then retry the + // ALTER SERVER CONFIGURATION SET HADR CLUSTER LOCAL command. + case 19413: + // SQL Error Code: 18858 + // Could not read data from replication table '%s'. If retrying does not fix the issue, drop and reconfigure replication. + case 18858: + // SQL Error Code: 18401 + // Login failed for user '%s'. Reason: Server is in script upgrade mode. Only administrator can connect at this time. + // Devnote: this can happen when SQL is going through recovery (e.g. after failover) + case 18401: + // SQL Error Code: 17889 + // A new connection was rejected because the maximum number of connections on session ID %d has been reached. + // Close an existing connection on this session and retry.%.*ls + case 17889: // SQL Error Code: 17197 // Login failed due to timeout; the connection has been closed. This error may indicate heavy server load. // Reduce the load on the server and retry login. case 17197: + // SQL Error Code: 17067 + // SQL Server Assertion: File: <%s>, line = %d %s. This error may be timing-related. If the error persists after + // rerunning the statement, use DBCC CHECKDB to check the database for structural integrity, or restart the server + // to ensure in-memory data structures are not corrupted. + case 17067: + // SQL Error Code: 17066 + // SQL Server Assertion: File: <%s>, line=%d Failed Assertion = '%s'. This error may be timing-related. If the error + // persists after rerunning the statement, use DBCC CHECKDB to check the database for structural integrity, or restart + // the server to ensure in-memory data structures are not corrupted. + case 17066: + // SQL Error Code: 17065 + // SQL Server Assertion: File: <%s>, line = %d Failed Assertion = '%s' %s. This error may be timing-related. + // If the error persists after rerunning the statement, use DBCC CHECKDB to check the database for structural integrity, + // or restart the server to ensure in-memory data structures are not corrupted. + case 17065: + // SQL Error Code: 16555 + // Operation failed due to an error while processing rejected rows. Intermediate results, if any, should be discarded + // as such results may not be complete. Please retry the operation. If the problem persists, contact + // Microsoft Azure Customer Support. + case 16555: + // SQL Error Code: 16554 + // Operation failed due to an error in a background task. Please retry the operation again. If the problem persists + // contact Microsoft Azure Customer Support. + case 16554: + // SQL Error Code: 16528 + // Operation '%ls %ls' failed. Retry the operation later. + case 16528: + // SQL Error Code: 14906 + // The database '%s' is not accessible. Ensure that the remote database exists. If you believe that your database should + // be accessible please retry the command. + case 14906: + // SQL Error Code: 14868 + // Inbound migration is in progress or paused. Migration direction outbound cannot be set at this time. Please retry + // after inbound migration is complete. + case 14868: + // SQL Error Code: 14817 + // The server '%s' is not accessible. Ensure that the remote server exists and the Azure SQL DB Firewall Rules permit + // access to the server. If you believe that your server should be accessible please retry the command. + case 14817: // SQL Error Code: 14355 // The MSSQLServerADHelper service is busy. Retry this operation later. case 14355: + // SQL Error Code: 11539 + // One of the types specified in WITH RESULT SETS clause has been modified after the EXECUTE statement started running. + // Please rerun the statement. + case 11539: + // SQL Error Code: 11001 + // An error has occurred while establishing a connection to the server. When connecting to SQL Server, + // this failure may be caused by the fact that under the default settings SQL Server does not allow remote connections. + // (provider: TCP Provider, error: 0 - No such host is known.) (.Net SqlClient Data Provider) + case 11001: // SQL Error Code: 10936 // Resource ID : %d. The request limit for the elastic pool is %d and has been reached. // See 'https://go.microsoft.com/fwlink/?LinkId=267637' for assistance. case 10936: + // SQL Error Code: 10930 + // The service is currently too busy. Please try again later. + case 10930: // SQL Error Code: 10929 // Resource ID: %d. The %s minimum guarantee is %d, maximum limit is %d and the current usage for the database is %d. // However, the server is currently too busy to support requests greater than %d for this database. @@ -107,9 +443,15 @@ public static bool ShouldRetryOn(Exception? ex) // A transport-level error has occurred when receiving results from the server. // An established connection was aborted by the software in your host machine. case 10053: + // SQL Error Code: 9985 + // There is not enough memory to generate a search property list cache. Rerun your full-text indexing statement when more resources are available. + case 9985: // SQL Error Code: 9515 // An XML schema has been altered or dropped, and the query plan is no longer valid. Please rerun the query batch. case 9515: + // SQL Error Code: 9020 + // The log for database '%ls' failed to grow while shrink in progress. Please retry. + case 9020: // SQL Error Code: 8651 // Could not perform the operation because the requested memory grant was not available in resource pool '%ls' (%ld). // Rerun the query, reduce the query load, or check resource governor configuration setting. @@ -120,12 +462,42 @@ public static bool ShouldRetryOn(Exception? ex) // SQL Error Code: 8628 // A time out occurred while waiting to optimize the query. Rerun the query. case 8628: + // SQL Error Code: 7951 + // Warning: Could not complete filestream consistency checks due to an operating system error. Any consistency errors + // found in the filestream subsystem will be silenced. Please refer to other errors for more information. + // This condition is likely transient; try rerunning the command. + case 7951: + // SQL Error Code: 6292 + // The transaction that is associated with this operation has been committed or rolled back. Retry with a different transaction. + case 6292: + // SQL Error Code: 5529 + // Failed to remove a FILESTREAM file. The database is a primary database in an availability group. Wait for the + // FILESTREAM data files to be hardened on every secondary availability replica. Then retry the drop file operation. + case 5529: + // SQL Error Code: 5280 + // An unexpected protocol element was recevied during the execution of a consistency check command. Retry the operation. + case 5280: // SQL Error Code: 4221 // Login to read-secondary failed due to long wait on 'HADR_DATABASE_WAIT_FOR_TRANSITION_TO_VERSIONING'. // The replica is not available for login because row versions are missing for transactions that were in-flight // when the replica was recycled. The issue can be resolved by rolling back or committing the active transactions // on the primary replica. Occurrences of this condition can be minimized by avoiding long write transactions on the primary. case 4221: + // SQL Error Code: 3980 + // The request failed to run because the batch is aborted, this can be caused by abort signal sent from client, + // or another request is running in the same session, which makes the session busy. + case 3980: + // SQL Error Code: 4184 + // Cannot retrieve table data for the query operation because the table ""%.*ls"" schema is being altered too frequently. + // Because the table ""%.*ls"" contains a filtered index or filtered statistics, changes to the table schema require + // a refresh of all table data. Retry the query operation, and if the problem persists, use SQL Server Profiler to identify + // what schema-altering operations are occurring. + case 4184: + // SQL Error Code: 4117 + // Cannot retrieve table data for the query operation because the table ""%.*ls"" schema is being altered too frequently. + // Because the table ""%.*ls"" contains a computed column, changes to the table schema require a refresh of all table data. + // Retry the query operation, and if the problem persists, use SQL Server Profiler to identify what schema-altering operations are occurring. + case 4117: // SQL Error Code: 4060 // Cannot open database "%.*ls" requested by the login. The login failed. case 4060: @@ -139,16 +511,99 @@ public static bool ShouldRetryOn(Exception? ex) // directly or indirectly in database '%.*ls' to update, delete, or insert the row that has been modified or deleted // by another transaction. Retry the transaction or change the isolation level for the update/delete statement. case 3960: + // SQL Error Code: 3957 + // Snapshot isolation transaction failed in database '%.*ls' because the database did not allow snapshot isolation when + // the current transaction started. It may help to retry the transaction. + case 3957: + // SQL Error Code: 3953 + // Snapshot isolation transaction failed in database '%.*ls' because the database was not recovered when the current + // transaction was started. Retry the transaction after the database has recovered. + case 3953: + // SQL Error Code: 3950 + // Version store scan timed out when attempting to read the next row. Please try the statement again later when the system is not as busy. + case 3950: + // SQL Error Code: 3948 + // The transaction was terminated because of the availability replica config/state change or because ghost records + // are being deleted on the primary and the secondary availability replica that might be needed by queries running under + // snapshot isolation. Retry the transaction. + case 3948: + // SQL Error Code: 3947 + // The transaction was aborted because the secondary compute failed to catch up redo. Retry the transaction. + case 3947: + // SQL Error Code: 3941 + // The transaction cannot modify an object that is published for replication or has Change Data Capture enabled + // because the transaction started before replication or Change Data Capture was enabled on the database. Retry the transaction. + case 3941: // SQL Error Code: 3935 // A FILESTREAM transaction context could not be initialized. This might be caused by a resource shortage. Retry the operation. case 3935: + // SQL Error Code: 3635 + // An error occurred while processing '%ls' metadata for database id %d, file id %d, and transaction='%.*ls'. + // Additional Context='%ls'. Location='%hs'(%d). Retry the operation; if the problem persists, + // contact the database administrator to review locking and memory configurations. + // Review the application for possible deadlock conflicts. + case 3635: + // SQL Error Code: 3429 + // Recovery could not determine the outcome of a cross-database transaction %S_XID, named '%.*ls', + // in database '%.*ls' (database ID %d:%d). The coordinating database (database ID %d:%d) was unavailable. + // The transaction was assumed to be committed. If the transaction was not committed, you can retry recovery + // when the coordinating database is available. + case 3429: + // SQL Error Code: 2816 + // The metadata for object with id %d has changed. Retry the statement. + case 2816: + // SQL Error Code: 2021 + // The referenced entity '%.*ls' was modified during DDL execution. Please retry the operation. + case 2021: // SQL Error Code: 1807 // Could not obtain exclusive lock on database 'model'. Retry the operation later. case 1807: + // SQL Error Code: 1535 + // Cannot share extent %S_PGID. Shared extent directory is full. Retry the transaction. If the problem persists, contact Technical Support. + case 1535: + // SQL Error Code: 1534 + // Extent %S_PGID not found in shared extent directory. Retry the transaction. If the problem persists, contact Technical Support. + case 1534: + // SQL Error Code: 1533 + // Cannot share extent %S_PGID. The correct extents could not be identified. Retry the transaction. + case 1533: + // SQL Error Code: 1532 + // New sort run starting on page %S_PGID found an extent not marked as shared. Retry the transaction. + // If the problem persists, contact Technical Support. + case 1532: + // SQL Error Code: 1438 + // The server instance %ls rejected configure request; read its error log file for more information. + // The reason %u, and state %u, can be of use for diagnostics by Microsoft. + // This is a transient error hence retrying the request is likely to succeed. Correct the cause if any and retry. + case 1438: + // SQL Error Code: 1421 + // Communications to the remote server instance '%.*ls' failed to complete before its timeout. + // The ALTER DATABASE command may have not completed. Retry the command. + case 1421: + // SQL Error Code: 1413 + // Communications to the remote server instance '%.*ls' failed before database mirroring was fully started. + // The ALTER DATABASE command failed. Retry the command when the remote database is started. + case 1413: + // SQL Error Code: 1404 + // The command failed because the database mirror is busy. Reissue the command later. + case 1404: + // SQL Error Code: 1232 + // Failed to acquire lock with lock manager service, it could be due to many reasons including transient service failure. + case 1232: + // SQL Error Code: 1222 + // Lock request time out period exceeded. + case 1222: // SQL Error Code: 1221 // The Database Engine is attempting to release a group of locks that are not currently held by the transaction. // Retry the transaction. If the problem persists, contact your support provider. case 1221: + // SQL Error Code: 1216 + // The DDL statement with ABORT_AFTER_WAIT = BLOCKERS option cannot be completed due to a conflicting system task. The request can abort only user transactions. Please wait for the system task to complete and retry. + case 1216: + // SQL Error Code: 1215 + // A conflicting ABORT_AFTER_WAIT = BLOCKERS request is waiting for existing transactions to rollback. + // This request cannot be executed. Please retry when the previous request is completed. + case 1215: // SQL Error Code: 1205 // Deadlock case 1205: @@ -165,6 +620,26 @@ public static bool ShouldRetryOn(Exception? ex) // A connection was successfully established with the server, but then an error occurred during the login process. // (provider: Named Pipes Provider, error: 0 - Overlapped I/O operation is in progress) case 997: + // SQL Error Code: 988 + // Unable to access database '%.*ls' because it lacks a quorum of nodes for high availability. Try the operation again later. + case 988: + // SQL Error Code: 982 + // Unable to access the '%.*ls' database because no online secondary replicas are enabled for read-only access. + // Check the availability group configuration to verify that at least one secondary replica is configured for read-only access. + // Wait for an enabled replica to come online, and retry your read-only operation. + case 982: + // SQL Error Code: 952 + // Database '%.*ls' is in transition. Try the statement later. + case 952: + // SQL Error Code: 941 + // Database '%.*ls' cannot be opened because it is not started. Retry when the database is started. + case 941: + // SQL Error Code: 927 + // Database '%.*ls' cannot be opened. It is in the middle of a restore. + case 927: + // SQL Error Code: 926 + // Database '%.*ls' cannot be opened. It has been marked SUSPECT by recovery. See the SQL Server errorlog for more information. + case 926: // SQL Error Code: 921 // Database '%.*ls' has not been recovered yet. Wait and try again. case 921: @@ -175,9 +650,15 @@ public static bool ShouldRetryOn(Exception? ex) // Descriptor for object ID %ld in database ID %d not found in the hash table during attempt to un-hash it. // A work table is missing an entry. Rerun the query. If a cursor is involved, close and reopen the cursor. case 617: + // SQL Error Code: 615 + // Could not find database ID %d, name '%.*ls'. The database may be offline. Wait a few minutes and try again. + case 615: // SQL Error Code: 601 // Could not continue scan with NOLOCK due to data movement. case 601: + // SQL Error Code: 539 + // Schema changed after the target table was created. Rerun the Select Into query. + case 539: // SQL Error Code: 233 // The client was unable to establish a connection because of an error during connection initialization process before login. // Possible causes include the following: the client tried to connect to an unsupported version of SQL Server; diff --git a/src/EFCore.SqlServer/Storage/Internal/SqlServerTypeMappingSource.cs b/src/EFCore.SqlServer/Storage/Internal/SqlServerTypeMappingSource.cs index d9362c84df3..f05557997c9 100644 --- a/src/EFCore.SqlServer/Storage/Internal/SqlServerTypeMappingSource.cs +++ b/src/EFCore.SqlServer/Storage/Internal/SqlServerTypeMappingSource.cs @@ -220,8 +220,7 @@ static SqlServerTypeMappingSource() /// public SqlServerTypeMappingSource( TypeMappingSourceDependencies dependencies, - RelationalTypeMappingSourceDependencies relationalDependencies, - ISqlServerSingletonOptions sqlServerSingletonOptions) + RelationalTypeMappingSourceDependencies relationalDependencies) : base(dependencies, relationalDependencies) { } @@ -234,7 +233,7 @@ public SqlServerTypeMappingSource( /// protected override RelationalTypeMapping? FindMapping(in RelationalTypeMappingInfo mappingInfo) => base.FindMapping(mappingInfo) - ?? FindRawMapping(mappingInfo)?.Clone(mappingInfo); + ?? FindRawMapping(mappingInfo)?.WithTypeMappingInfo(mappingInfo); private RelationalTypeMapping? FindRawMapping(RelationalTypeMappingInfo mappingInfo) { @@ -350,20 +349,23 @@ public SqlServerTypeMappingSource( return Rowversion; } - var isFixedLength = mappingInfo.IsFixedLength == true; - - var size = mappingInfo.Size ?? (mappingInfo.IsKeyOrIndex ? 900 : null); - if (size is < 0 or > 8000) + if (mappingInfo.ElementTypeMapping == null) { - size = isFixedLength ? 8000 : null; - } + var isFixedLength = mappingInfo.IsFixedLength == true; - return size == null - ? VariableLengthMaxBinary - : new SqlServerByteArrayTypeMapping( - size: size, - fixedLength: isFixedLength, - storeTypePostfix: storeTypeName == null ? StoreTypePostfix.Size : StoreTypePostfix.None); + var size = mappingInfo.Size ?? (mappingInfo.IsKeyOrIndex ? 900 : null); + if (size is < 0 or > 8000) + { + size = isFixedLength ? 8000 : null; + } + + return size == null + ? VariableLengthMaxBinary + : new SqlServerByteArrayTypeMapping( + size: size, + fixedLength: isFixedLength, + storeTypePostfix: storeTypeName == null ? StoreTypePostfix.Size : StoreTypePostfix.None); + } } } diff --git a/src/EFCore.SqlServer/Update/Internal/SqlServerUpdateSqlGenerator.cs b/src/EFCore.SqlServer/Update/Internal/SqlServerUpdateSqlGenerator.cs index 49d230adbfc..9cef4d8619d 100644 --- a/src/EFCore.SqlServer/Update/Internal/SqlServerUpdateSqlGenerator.cs +++ b/src/EFCore.SqlServer/Update/Internal/SqlServerUpdateSqlGenerator.cs @@ -152,8 +152,7 @@ protected override void AppendUpdateColumnValue( stringBuilder.Append(columnModification.JsonPath); stringBuilder.Append("', "); - if (columnModification.Property != null - && columnModification.Property.GetTypeMapping().ElementTypeMapping == null) + if (columnModification.Property is { IsPrimitiveCollection: false }) { base.AppendUpdateColumnValue(updateSqlGeneratorHelper, columnModification, stringBuilder, name, schema); } diff --git a/src/EFCore.Sqlite.Core/Extensions/SqliteComplexTypePrimitiveCollectionBuilderExtensions.cs b/src/EFCore.Sqlite.Core/Extensions/SqliteComplexTypePrimitiveCollectionBuilderExtensions.cs deleted file mode 100644 index 19fd5c79297..00000000000 --- a/src/EFCore.Sqlite.Core/Extensions/SqliteComplexTypePrimitiveCollectionBuilderExtensions.cs +++ /dev/null @@ -1,49 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.EntityFrameworkCore; - -/// -/// SQLite-specific extension methods for . -/// -/// -/// See Modeling entity types and relationships, and -/// Accessing SQLite databases with EF Core for more information and examples. -/// -public static class SqliteComplexTypePrimitiveCollectionBuilderExtensions -{ - /// - /// Configures the SRID of the column that the property maps to when targeting SQLite. - /// - /// - /// See Spatial data, and - /// Accessing SQLite databases with EF Core for more information and examples. - /// - /// The builder for the property being configured. - /// The SRID. - /// The same builder instance so that multiple calls can be chained. - public static ComplexTypePrimitiveCollectionBuilder HasSrid( - this ComplexTypePrimitiveCollectionBuilder primitiveCollectionBuilder, - int srid) - { - primitiveCollectionBuilder.Metadata.SetSrid(srid); - - return primitiveCollectionBuilder; - } - - /// - /// Configures the SRID of the column that the property maps to when targeting SQLite. - /// - /// - /// See Spatial data, and - /// Accessing SQLite databases with EF Core for more information and examples. - /// - /// The builder for the property being configured. - /// The SRID. - /// The same builder instance so that multiple calls can be chained. - public static ComplexTypePrimitiveCollectionBuilder HasSrid( - this ComplexTypePrimitiveCollectionBuilder primitiveCollectionBuilder, - int srid) - => (ComplexTypePrimitiveCollectionBuilder)HasSrid( - (ComplexTypePrimitiveCollectionBuilder)primitiveCollectionBuilder, srid); -} diff --git a/src/EFCore.Sqlite.Core/Extensions/SqliteDbFunctionsExtensions.cs b/src/EFCore.Sqlite.Core/Extensions/SqliteDbFunctionsExtensions.cs index 585a0936030..f52da2ec8f4 100644 --- a/src/EFCore.Sqlite.Core/Extensions/SqliteDbFunctionsExtensions.cs +++ b/src/EFCore.Sqlite.Core/Extensions/SqliteDbFunctionsExtensions.cs @@ -43,6 +43,33 @@ public static bool Glob(this DbFunctions _, string matchExpression, string patte public static string Hex(this DbFunctions _, byte[] bytes) => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Hex))); + /// + /// Maps to the SQLite unhex function which returns a BLOB representing decoding of the hexadecimal string. + /// + /// + /// See Database functions, and + /// Accessing SQLite databases with EF Core for more information and examples. + /// + /// The instance. + /// The hexadecimal string. + /// Decoded hexadecimal string as binary value. + public static byte[] Unhex(this DbFunctions _, string value) + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Unhex))); + + /// + /// Maps to the SQLite unhex function which returns a BLOB representing decoding of the hexadecimal string. + /// + /// + /// See Database functions, and + /// Accessing SQLite databases with EF Core for more information and examples. + /// + /// The instance. + /// The hexadecimal string. + /// Characters that are ignored in . + /// Decoded hexadecimal string as binary value. + public static byte[] Unhex(this DbFunctions _, string value, string ignoreChars) + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Unhex))); + /// /// Maps to the SQLite substr function which returns a subarray of the specified value. The subarray starts /// at and continues to the end of the value. diff --git a/src/EFCore.Sqlite.Core/Extensions/SqlitePrimitiveCollectionBuilderExtensions.cs b/src/EFCore.Sqlite.Core/Extensions/SqlitePrimitiveCollectionBuilderExtensions.cs deleted file mode 100644 index 0a675c038ff..00000000000 --- a/src/EFCore.Sqlite.Core/Extensions/SqlitePrimitiveCollectionBuilderExtensions.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.EntityFrameworkCore; - -/// -/// SQLite-specific extension methods for . -/// -/// -/// See Modeling entity types and relationships, and -/// Accessing SQLite databases with EF Core for more information and examples. -/// -public static class SqlitePrimitiveCollectionBuilderExtensions -{ - /// - /// Configures the SRID of the column that the property maps to when targeting SQLite. - /// - /// - /// See Spatial data, and - /// Accessing SQLite databases with EF Core for more information and examples. - /// - /// The builder for the property being configured. - /// The SRID. - /// The same builder instance so that multiple calls can be chained. - public static PrimitiveCollectionBuilder HasSrid(this PrimitiveCollectionBuilder primitiveCollectionBuilder, int srid) - { - primitiveCollectionBuilder.Metadata.SetSrid(srid); - - return primitiveCollectionBuilder; - } - - /// - /// Configures the SRID of the column that the property maps to when targeting SQLite. - /// - /// - /// See Spatial data, and - /// Accessing SQLite databases with EF Core for more information and examples. - /// - /// The builder for the property being configured. - /// The SRID. - /// The same builder instance so that multiple calls can be chained. - public static PrimitiveCollectionBuilder HasSrid( - this PrimitiveCollectionBuilder primitiveCollectionBuilder, - int srid) - => (PrimitiveCollectionBuilder)HasSrid((PrimitiveCollectionBuilder)primitiveCollectionBuilder, srid); -} diff --git a/src/EFCore.Sqlite.Core/Query/Internal/SqliteHexMethodTranslator.cs b/src/EFCore.Sqlite.Core/Query/Internal/SqliteHexMethodTranslator.cs index 68e759f7a85..ede730114ed 100644 --- a/src/EFCore.Sqlite.Core/Query/Internal/SqliteHexMethodTranslator.cs +++ b/src/EFCore.Sqlite.Core/Query/Internal/SqliteHexMethodTranslator.cs @@ -13,9 +13,15 @@ namespace Microsoft.EntityFrameworkCore.Sqlite.Query.Internal; /// public class SqliteHexMethodTranslator : IMethodCallTranslator { - private static readonly MethodInfo MethodInfo = typeof(SqliteDbFunctionsExtensions) + private static readonly MethodInfo HexMethodInfo = typeof(SqliteDbFunctionsExtensions) .GetMethod(nameof(SqliteDbFunctionsExtensions.Hex), new[] { typeof(DbFunctions), typeof(byte[]) })!; + private static readonly MethodInfo UnhexMethodInfo = typeof(SqliteDbFunctionsExtensions) + .GetMethod(nameof(SqliteDbFunctionsExtensions.Unhex), new[] { typeof(DbFunctions), typeof(string) })!; + + private static readonly MethodInfo UnhexWithIgnoreCharsMethodInfo = typeof(SqliteDbFunctionsExtensions) + .GetMethod(nameof(SqliteDbFunctionsExtensions.Unhex), new[] { typeof(DbFunctions), typeof(string), typeof(string) })!; + private readonly ISqlExpressionFactory _sqlExpressionFactory; /// @@ -41,7 +47,7 @@ public SqliteHexMethodTranslator(ISqlExpressionFactory sqlExpressionFactory) IReadOnlyList arguments, IDiagnosticsLogger logger) { - if (method.Equals(MethodInfo)) + if (method.Equals(HexMethodInfo)) { return _sqlExpressionFactory.Function( "hex", @@ -51,6 +57,17 @@ public SqliteHexMethodTranslator(ISqlExpressionFactory sqlExpressionFactory) typeof(string)); } + if (method.Equals(UnhexMethodInfo) + || method.Equals(UnhexWithIgnoreCharsMethodInfo)) + { + return _sqlExpressionFactory.Function( + "unhex", + arguments.Skip(1), + nullable: true, + arguments.Skip(1).Select(_ => true).ToArray(), + typeof(byte[])); + } + return null; } } diff --git a/src/EFCore.Sqlite.Core/Query/Internal/SqliteQuerySqlGenerator.cs b/src/EFCore.Sqlite.Core/Query/Internal/SqliteQuerySqlGenerator.cs index 656d144b856..4e3d4fd6ffa 100644 --- a/src/EFCore.Sqlite.Core/Query/Internal/SqliteQuerySqlGenerator.cs +++ b/src/EFCore.Sqlite.Core/Query/Internal/SqliteQuerySqlGenerator.cs @@ -313,6 +313,27 @@ protected override Expression VisitSqlUnary(SqlUnaryExpression sqlUnaryExpressio { switch (sqlUnaryExpression.OperatorType) { + case ExpressionType.Convert: + if (sqlUnaryExpression.Operand.Type == typeof(char) + && sqlUnaryExpression.Type.IsInteger()) + { + Sql.Append("unicode("); + Visit(sqlUnaryExpression.Operand); + Sql.Append(")"); + + return sqlUnaryExpression; + } + else if (sqlUnaryExpression.Operand.Type.IsInteger() + && sqlUnaryExpression.Type == typeof(char)) + { + Sql.Append("char("); + Visit(sqlUnaryExpression.Operand); + Sql.Append(")"); + + return sqlUnaryExpression; + } + goto default; + case ExpressionType.Not when sqlUnaryExpression.Type == typeof(bool): switch (sqlUnaryExpression.Operand) { diff --git a/src/EFCore.Sqlite.Core/Query/Internal/SqliteQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Sqlite.Core/Query/Internal/SqliteQueryableMethodTranslatingExpressionVisitor.cs index 807a17034b5..fae33b1871c 100644 --- a/src/EFCore.Sqlite.Core/Query/Internal/SqliteQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.Sqlite.Core/Query/Internal/SqliteQueryableMethodTranslatingExpressionVisitor.cs @@ -223,10 +223,7 @@ protected override QueryableMethodTranslatingExpressionVisitor CreateSubqueryVis // which case we only have the CLR type (note that we cannot produce different SQLs based on the nullability of an *element* in // a parameter collection - our caching mechanism only supports varying by the nullability of the parameter itself (i.e. the // collection). - // TODO: if property is non-null, GetElementType() should never be null, but we have #31469 for shadow properties - var isElementNullable = property?.GetElementType() is null - ? elementClrType.IsNullableType() - : property.GetElementType()!.IsNullable; + var isElementNullable = property?.GetElementType()!.IsNullable; #pragma warning disable EF1001 // Internal EF Core API usage. var selectExpression = new SelectExpression( diff --git a/src/EFCore.Sqlite.Core/Storage/Internal/SqliteTypeMappingSource.cs b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteTypeMappingSource.cs index f4c85a07c64..fce854a835e 100644 --- a/src/EFCore.Sqlite.Core/Storage/Internal/SqliteTypeMappingSource.cs +++ b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteTypeMappingSource.cs @@ -129,13 +129,18 @@ public static bool IsSpatialiteType(string columnType) return mapping != null && mappingInfo.StoreTypeName != null - ? mapping.Clone(mappingInfo.StoreTypeName, null) + ? mapping.WithStoreTypeAndSize(mappingInfo.StoreTypeName, null) : mapping; } private RelationalTypeMapping? FindRawMapping(RelationalTypeMappingInfo mappingInfo) { var clrType = mappingInfo.ClrType; + if (clrType == typeof(byte[]) && mappingInfo.ElementTypeMapping != null) + { + return null; + } + if (clrType != null && _clrTypeMappings.TryGetValue(clrType, out var mapping)) { diff --git a/src/EFCore.Sqlite.Core/Update/Internal/SqliteUpdateSqlGenerator.cs b/src/EFCore.Sqlite.Core/Update/Internal/SqliteUpdateSqlGenerator.cs index d5a51861e72..626a9a0ce68 100644 --- a/src/EFCore.Sqlite.Core/Update/Internal/SqliteUpdateSqlGenerator.cs +++ b/src/EFCore.Sqlite.Core/Update/Internal/SqliteUpdateSqlGenerator.cs @@ -159,8 +159,7 @@ protected override void AppendUpdateColumnValue( stringBuilder.Append(columnModification.JsonPath); stringBuilder.Append("', "); - if (columnModification.Property != null - && columnModification.Property.GetTypeMapping().ElementTypeMapping == null) + if (columnModification.Property is { IsPrimitiveCollection: false }) { var providerClrType = (columnModification.Property.GetTypeMapping().Converter?.ProviderClrType ?? columnModification.Property.ClrType).UnwrapNullableType(); diff --git a/src/EFCore.Sqlite/EFCore.Sqlite.csproj b/src/EFCore.Sqlite/EFCore.Sqlite.csproj index b7abc5d9684..f728029ae0c 100644 --- a/src/EFCore.Sqlite/EFCore.Sqlite.csproj +++ b/src/EFCore.Sqlite/EFCore.Sqlite.csproj @@ -47,7 +47,7 @@ - + diff --git a/src/EFCore.Tools/README.md b/src/EFCore.Tools/README.md new file mode 100644 index 00000000000..a82cd1c5ef2 --- /dev/null +++ b/src/EFCore.Tools/README.md @@ -0,0 +1,41 @@ +The Entity Framework Core tools help with design-time development tasks. They're primarily used to manage Migrations and to scaffold a DbContext and entity types by reverse engineering the schema of a database. +Microsoft.EntityFrameworkCore.Tools is for PowerShell tooling that works in the Visual Studio Package Manager Console. + +## Getting started + +The Package Manager Console (PMC) tools for Entity Framework Core perform design-time development tasks. For example, they create migrations, apply migrations, and generate code for a model based on an existing database. The commands run inside of Visual Studio using the Package Manager Console. These tools work with both .NET Framework and .NET Core projects. + +### Prerequisites + +Before using the tools: + +- [Understand the difference between target and startup project](https://learn.microsoft.com/en-us/ef/core/cli/powershell#target-and-startup-project). +- [Learn how to use the tools with .NET Standard class libraries](https://learn.microsoft.com/en-us/ef/core/cli/powershell#other-target-frameworks). +- [For ASP.NET Core projects, set the environment](https://learn.microsoft.com/en-us/ef/core/cli/powershell#aspnet-core-environment). + +## Usage + +PMC Command | Usage +-- | -- +Get-Help entityframework |Displays information about entity framework commands. +[Add-Migration](https://learn.microsoft.com/en-us/ef/core/cli/powershell#add-migration) | Creates a migration by adding a migration snapshot. +[Bundle-Migration](https://learn.microsoft.com/en-us/ef/core/cli/powershell#bundle-migration) | Creates an executable to update the database. +[Get-DbContext](https://learn.microsoft.com/en-us/ef/core/cli/powershell#get-dbcontext) | Gets information about a DbContext type. +[Drop-Database](https://learn.microsoft.com/en-us/ef/core/cli/powershell#drop-database) | Drops the database. +[Get-Migration](https://learn.microsoft.com/en-us/ef/core/cli/powershell#get-migration) | Lists available migrations. +[Optimize-DbContext](https://learn.microsoft.com/en-us/ef/core/cli/powershell#optimize-dbcontext) | Generates a compiled version of the model used by the `DbContext`. +[Remove-Migration](https://learn.microsoft.com/en-us/ef/core/cli/powershell#remove-migration) | Removes the last migration snapshot. +[Scaffold-DbContext](https://learn.microsoft.com/en-us/ef/core/cli/powershell#scaffold-dbcontext) | Generates a DbContext and entity type classes for a specified database. This is called reverse engineering. +[Script-DbContext](https://learn.microsoft.com/en-us/ef/core/cli/powershell#script-dbcontext) | Generates a SQL script from the DbContext. Bypasses any migrations. +[Script-Migration](https://learn.microsoft.com/en-us/ef/core/cli/powershell#script-migration) | Generates a SQL script using all the migration snapshots. +[Update-Database](https://learn.microsoft.com/en-us/ef/core/cli/powershell#update-database) | Updates the database schema based on the last migration snapshot. + +## Additional documentation + +- [Migrations](https://learn.microsoft.com/en-us/ef/core/managing-schemas/migrations/) +- [Reverse Engineering](https://learn.microsoft.com/en-us/ef/core/managing-schemas/scaffolding/?tabs=dotnet-core-cli) +- [Compiled models](https://learn.microsoft.com/en-us/ef/core/performance/advanced-performance-topics?tabs=with-di%2Cwith-constant#compiled-models) + +## Feedback + +If you encounter a bug or issues with this package,you can [open an Github issue](https://github.com/dotnet/efcore/issues/new/choose). For more details, see [getting support](https://github.com/dotnet/efcore/blob/main/.github/SUPPORT.md). \ No newline at end of file diff --git a/src/EFCore/ChangeTracking/Internal/ArrayPropertyValues.cs b/src/EFCore/ChangeTracking/Internal/ArrayPropertyValues.cs index c8c54fcc2d6..9ee9cc38daa 100644 --- a/src/EFCore/ChangeTracking/Internal/ArrayPropertyValues.cs +++ b/src/EFCore/ChangeTracking/Internal/ArrayPropertyValues.cs @@ -56,7 +56,7 @@ public override void SetValues(object obj) { if (!Properties[i].IsShadowProperty()) { - SetValue(i, Properties[i].GetGetter().GetClrValue(obj)); + SetValue(i, Properties[i].GetGetter().GetClrValueUsingContainingEntity(obj)); } } } diff --git a/src/EFCore/ChangeTracking/Internal/EntryPropertyValues.cs b/src/EFCore/ChangeTracking/Internal/EntryPropertyValues.cs index 0ee834c9b62..927f2e1c678 100644 --- a/src/EFCore/ChangeTracking/Internal/EntryPropertyValues.cs +++ b/src/EFCore/ChangeTracking/Internal/EntryPropertyValues.cs @@ -49,7 +49,7 @@ public override void SetValues(object obj) { foreach (var property in Properties.Where(p => !p.IsShadowProperty())) { - SetValueInternal(property, property.GetGetter().GetClrValue(obj)); + SetValueInternal(property, property.GetGetter().GetClrValueUsingContainingEntity(obj)); } } else diff --git a/src/EFCore/ChangeTracking/Internal/IInternalEntry.cs b/src/EFCore/ChangeTracking/Internal/IInternalEntry.cs index bd973bb81df..69944659dc7 100644 --- a/src/EFCore/ChangeTracking/Internal/IInternalEntry.cs +++ b/src/EFCore/ChangeTracking/Internal/IInternalEntry.cs @@ -1,322 +1,322 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.EntityFrameworkCore.Metadata.Internal; - -namespace Microsoft.EntityFrameworkCore.ChangeTracking.Internal; - -/// -/// 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. -/// -public interface IInternalEntry -{ - /// - /// 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. - /// - object? this[IPropertyBase propertyBase] { get; set; } - - /// - /// 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. - /// - IRuntimeEntityType EntityType { get; } - - /// - /// 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. - /// - bool HasConceptualNull { get; } - - /// - /// 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. - /// - IStateManager StateManager { get; } - - /// - /// 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. - /// - void AcceptChanges(); - - /// - /// 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. - /// - void DiscardStoreGeneratedValues(); - - /// - /// 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. - /// - object? GetCurrentValue(IPropertyBase propertyBase); - - /// - /// 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. - /// - TProperty GetCurrentValue(IPropertyBase propertyBase); - - /// - /// 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. - /// - object? GetOriginalValue(IPropertyBase propertyBase); - - /// - /// 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. - /// - TProperty GetOriginalValue(IProperty property); - - /// - /// 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. - /// - object? GetPreStoreGeneratedCurrentValue(IPropertyBase propertyBase); - - /// - /// 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. - /// - bool HasExplicitValue(IProperty property); - - /// - /// 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. - /// - bool HasTemporaryValue(IProperty property); - - /// - /// 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. - /// - bool IsConceptualNull(IProperty property); - - /// - /// 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. - /// - bool IsModified(IProperty property); - - /// - /// 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. - /// - bool FlaggedAsStoreGenerated(int propertyIndex); - - /// - /// 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. - /// - bool FlaggedAsTemporary(int propertyIndex); - - /// - /// 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. - /// - bool IsStoreGenerated(IProperty property); - - /// - /// 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. - /// - bool IsUnknown(IProperty property); - - /// - /// 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. - /// - void MarkAsTemporary(IProperty property, bool temporary); - - /// - /// 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. - /// - void MarkUnchangedFromQuery(); - - /// - /// 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. - /// - void MarkUnknown(IProperty property); - - /// - /// 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. - /// - IInternalEntry PrepareToSave(); - - /// - /// 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. - /// - public object Object { get; } // This won't work for value types - - /// - /// 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. - /// - public void HandleConceptualNulls(bool sensitiveLoggingEnabled, bool force, bool isCascadeDelete); - - /// - /// 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. - /// - void PropagateValue(InternalEntityEntry principalEntry, IProperty principalProperty, IProperty dependentProperty, bool isMaterialization = false, bool setModified = true); - - /// - /// 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. - /// - T ReadOriginalValue(IProperty property, int originalValueIndex); - - /// - /// 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. - /// - object? ReadPropertyValue(IPropertyBase propertyBase); - - /// - /// 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. - /// - T ReadStoreGeneratedValue(int storeGeneratedIndex); - - /// - /// 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. - /// - T ReadTemporaryValue(int storeGeneratedIndex); - - /// - /// 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. - /// - T ReadShadowValue(int shadowIndex); - - /// - /// 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. - /// - void SetOriginalValue(IPropertyBase propertyBase, object? value, int index = -1); - - /// - /// 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. - /// - void SetProperty(IPropertyBase propertyBase, object? value, bool isMaterialization, bool setModified = true, bool isCascadeDelete = false); - - /// - /// 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. - /// - void SetPropertyModified(IProperty property, bool changeState = true, bool isModified = true, bool isConceptualNull = false, bool acceptChanges = false); - - /// - /// 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. - /// - void SetEntityState( - EntityState entityState, - bool acceptChanges = false, - bool modifyProperties = true); - - /// - /// 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. - /// - void OnComplexPropertyModified(IComplexProperty property, bool isModified = true); - - /// - /// 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. - /// - void SetStoreGeneratedValue(IProperty property, object? value, bool setModified = true); - - /// - /// 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. - /// - void SetTemporaryValue(IProperty property, object? value, bool setModified = true); -} +// using Microsoft.EntityFrameworkCore.Metadata.Internal; +// +// namespace Microsoft.EntityFrameworkCore.ChangeTracking.Internal; +// +// /// +// /// 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. +// /// +// public interface IInternalEntry +// { +// /// +// /// 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. +// /// +// object? this[IPropertyBase propertyBase] { get; set; } +// +// /// +// /// 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. +// /// +// IRuntimeEntityType EntityType { get; } +// +// /// +// /// 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. +// /// +// bool HasConceptualNull { get; } +// +// /// +// /// 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. +// /// +// IStateManager StateManager { get; } +// +// /// +// /// 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. +// /// +// void AcceptChanges(); +// +// /// +// /// 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. +// /// +// void DiscardStoreGeneratedValues(); +// +// /// +// /// 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. +// /// +// object? GetCurrentValue(IPropertyBase propertyBase); +// +// /// +// /// 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. +// /// +// TProperty GetCurrentValue(IPropertyBase propertyBase); +// +// /// +// /// 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. +// /// +// object? GetOriginalValue(IPropertyBase propertyBase); +// +// /// +// /// 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. +// /// +// TProperty GetOriginalValue(IProperty property); +// +// /// +// /// 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. +// /// +// object? GetPreStoreGeneratedCurrentValue(IPropertyBase propertyBase); +// +// /// +// /// 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. +// /// +// bool HasExplicitValue(IProperty property); +// +// /// +// /// 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. +// /// +// bool HasTemporaryValue(IProperty property); +// +// /// +// /// 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. +// /// +// bool IsConceptualNull(IProperty property); +// +// /// +// /// 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. +// /// +// bool IsModified(IProperty property); +// +// /// +// /// 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. +// /// +// bool FlaggedAsStoreGenerated(int propertyIndex); +// +// /// +// /// 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. +// /// +// bool FlaggedAsTemporary(int propertyIndex); +// +// /// +// /// 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. +// /// +// bool IsStoreGenerated(IProperty property); +// +// /// +// /// 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. +// /// +// bool IsUnknown(IProperty property); +// +// /// +// /// 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. +// /// +// void MarkAsTemporary(IProperty property, bool temporary); +// +// /// +// /// 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. +// /// +// void MarkUnchangedFromQuery(); +// +// /// +// /// 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. +// /// +// void MarkUnknown(IProperty property); +// +// /// +// /// 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. +// /// +// IInternalEntry PrepareToSave(); +// +// /// +// /// 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. +// /// +// public object Object { get; } // This won't work for value types +// +// /// +// /// 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. +// /// +// public void HandleConceptualNulls(bool sensitiveLoggingEnabled, bool force, bool isCascadeDelete); +// +// /// +// /// 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. +// /// +// void PropagateValue(InternalEntityEntry principalEntry, IProperty principalProperty, IProperty dependentProperty, bool isMaterialization = false, bool setModified = true); +// +// /// +// /// 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. +// /// +// T ReadOriginalValue(IProperty property, int originalValueIndex); +// +// /// +// /// 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. +// /// +// object? ReadPropertyValue(IPropertyBase propertyBase); +// +// /// +// /// 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. +// /// +// T ReadStoreGeneratedValue(int storeGeneratedIndex); +// +// /// +// /// 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. +// /// +// T ReadTemporaryValue(int storeGeneratedIndex); +// +// /// +// /// 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. +// /// +// T ReadShadowValue(int shadowIndex); +// +// /// +// /// 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. +// /// +// void SetOriginalValue(IPropertyBase propertyBase, object? value, int index = -1); +// +// /// +// /// 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. +// /// +// void SetProperty(IPropertyBase propertyBase, object? value, bool isMaterialization, bool setModified = true, bool isCascadeDelete = false); +// +// /// +// /// 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. +// /// +// void SetPropertyModified(IProperty property, bool changeState = true, bool isModified = true, bool isConceptualNull = false, bool acceptChanges = false); +// +// /// +// /// 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. +// /// +// void SetEntityState( +// EntityState entityState, +// bool acceptChanges = false, +// bool modifyProperties = true); +// +// /// +// /// 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. +// /// +// void OnComplexPropertyModified(IComplexProperty property, bool isModified = true); +// +// /// +// /// 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. +// /// +// void SetStoreGeneratedValue(IProperty property, object? value, bool setModified = true); +// +// /// +// /// 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. +// /// +// void SetTemporaryValue(IProperty property, object? value, bool setModified = true); +// } diff --git a/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.OriginalValues.cs b/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.OriginalValues.cs index fd3b1581ea5..6e7b552b3fc 100644 --- a/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.OriginalValues.cs +++ b/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.OriginalValues.cs @@ -11,12 +11,12 @@ private readonly struct OriginalValues { private readonly ISnapshot _values; - public OriginalValues(IInternalEntry entry) + public OriginalValues(InternalEntityEntry entry) { _values = entry.EntityType.OriginalValuesFactory(entry); } - public object? GetValue(IInternalEntry entry, IProperty property) + public object? GetValue(InternalEntityEntry entry, IProperty property) { var index = property.GetOriginalValueIndex(); if (index == -1) @@ -28,7 +28,7 @@ public OriginalValues(IInternalEntry entry) return IsEmpty ? entry[property] : _values[index]; } - public T GetValue(IInternalEntry entry, IProperty property, int index) + public T GetValue(InternalEntityEntry entry, IProperty property, int index) { if (index == -1) { @@ -65,7 +65,7 @@ public void SetValue(IProperty property, object? value, int index) _values[index] = SnapshotValue(property, value); } - public void RejectChanges(IInternalEntry entry) + public void RejectChanges(InternalEntityEntry entry) { if (IsEmpty) { @@ -82,7 +82,7 @@ public void RejectChanges(IInternalEntry entry) } } - public void AcceptChanges(IInternalEntry entry) + public void AcceptChanges(InternalEntityEntry entry) { if (IsEmpty) { diff --git a/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.cs b/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.cs index b13479b77c2..8daa35ecbb2 100644 --- a/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.cs +++ b/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.cs @@ -16,7 +16,7 @@ namespace Microsoft.EntityFrameworkCore.ChangeTracking.Internal; /// 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. /// -public sealed partial class InternalEntityEntry : IUpdateEntry, IInternalEntry +public sealed partial class InternalEntityEntry : IUpdateEntry { private readonly StateData _stateData; private OriginalValues _originalValues; @@ -420,7 +420,7 @@ private void HandleSharedIdentityEntry(EntityState newState) throw new InvalidOperationException( CoreStrings.IdentityConflictSensitive( EntityType.DisplayName(), - BuildCurrentValuesString(EntityType.FindPrimaryKey()!.Properties))); + this.BuildCurrentValuesString(EntityType.FindPrimaryKey()!.Properties))); } throw new InvalidOperationException( @@ -793,7 +793,7 @@ public void MarkAsTemporary(IProperty property, bool temporary) /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public static readonly MethodInfo FlaggedAsTemporaryMethod - = typeof(IInternalEntry).GetMethod(nameof(IInternalEntry.FlaggedAsTemporary))!; + = typeof(InternalEntityEntry).GetMethod(nameof(InternalEntityEntry.FlaggedAsTemporary))!; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -802,7 +802,7 @@ public static readonly MethodInfo FlaggedAsTemporaryMethod /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public static readonly MethodInfo FlaggedAsStoreGeneratedMethod - = typeof(IInternalEntry).GetMethod(nameof(IInternalEntry.FlaggedAsStoreGenerated))!; + = typeof(InternalEntityEntry).GetMethod(nameof(InternalEntityEntry.FlaggedAsStoreGenerated))!; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -837,7 +837,7 @@ public void MarkUnknown(IProperty property) => _stateData.FlagProperty(property.GetIndex(), PropertyFlag.Unknown, true); internal static MethodInfo MakeReadShadowValueMethod(Type type) - => typeof(IInternalEntry).GetTypeInfo().GetDeclaredMethod(nameof(ReadShadowValue))! + => typeof(InternalEntityEntry).GetTypeInfo().GetDeclaredMethod(nameof(ReadShadowValue))! .MakeGenericMethod(type); /// @@ -850,7 +850,7 @@ public T ReadShadowValue(int shadowIndex) => _shadowValues.GetValue(shadowIndex); private static readonly MethodInfo ReadOriginalValueMethod - = typeof(IInternalEntry).GetTypeInfo().GetDeclaredMethod(nameof(ReadOriginalValue))!; + = typeof(InternalEntityEntry).GetTypeInfo().GetDeclaredMethod(nameof(ReadOriginalValue))!; [UnconditionalSuppressMessage( "ReflectionAnalysis", "IL2060", @@ -892,7 +892,7 @@ internal static MethodInfo MakeReadStoreGeneratedValueMethod(Type type) => ReadStoreGeneratedValueMethod.MakeGenericMethod(type); private static readonly MethodInfo ReadStoreGeneratedValueMethod - = typeof(IInternalEntry).GetTypeInfo().GetDeclaredMethod(nameof(ReadStoreGeneratedValue))!; + = typeof(InternalEntityEntry).GetTypeInfo().GetDeclaredMethod(nameof(ReadStoreGeneratedValue))!; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -904,7 +904,7 @@ public T ReadStoreGeneratedValue(int storeGeneratedIndex) => _storeGeneratedValues.GetValue(storeGeneratedIndex); private static readonly MethodInfo ReadTemporaryValueMethod - = typeof(IInternalEntry).GetMethod(nameof(ReadTemporaryValue))!; + = typeof(InternalEntityEntry).GetMethod(nameof(ReadTemporaryValue))!; [UnconditionalSuppressMessage( "ReflectionAnalysis", "IL2060", @@ -922,7 +922,7 @@ public T ReadTemporaryValue(int storeGeneratedIndex) => _temporaryValues.GetValue(storeGeneratedIndex); private static readonly MethodInfo GetCurrentValueMethod - = typeof(IInternalEntry).GetTypeInfo().GetDeclaredMethods(nameof(GetCurrentValue)).Single( + = typeof(InternalEntityEntry).GetTypeInfo().GetDeclaredMethods(nameof(GetCurrentValue)).Single( m => m.IsGenericMethod); [UnconditionalSuppressMessage( @@ -938,7 +938,7 @@ internal static MethodInfo MakeGetCurrentValueMethod(Type type) /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public TProperty GetCurrentValue(IPropertyBase propertyBase) - => ((Func)propertyBase.GetPropertyAccessors().CurrentValueGetter)(this); + => ((Func)propertyBase.GetPropertyAccessors().CurrentValueGetter)(this); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -947,7 +947,7 @@ public TProperty GetCurrentValue(IPropertyBase propertyBase) /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public TProperty GetOriginalValue(IProperty property) - => ((Func)property.GetPropertyAccessors().OriginalValueGetter!)(this); + => ((Func)property.GetPropertyAccessors().OriginalValueGetter!)(this); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -968,7 +968,7 @@ public TProperty GetRelationshipSnapshotValue(IPropertyBase propertyB public object? ReadPropertyValue(IPropertyBase propertyBase) => propertyBase.IsShadowProperty() ? _shadowValues[propertyBase.GetShadowIndex()] - : propertyBase.GetGetter().GetClrValue(Entity); + : propertyBase.GetGetter().GetClrValueUsingContainingEntity(Entity); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -1572,14 +1572,9 @@ public InternalEntityEntry PrepareToSave() throw new InvalidOperationException(CoreStrings.UnknownKeyValue(entityType.DisplayName(), property.Name)); } - if (property.GetElementType() != null - && !property.IsNullable - && GetCurrentValue(property) == null) - { - throw new InvalidOperationException( - CoreStrings.NullRequiredPrimitiveCollection(EntityType.DisplayName(), property.Name)); - } + CheckForNullCollection(property); } + CheckForNullComplexProperties(); } else if (EntityState == EntityState.Modified) { @@ -1594,8 +1589,10 @@ public InternalEntityEntry PrepareToSave() EntityType.DisplayName())); } + CheckForNullCollection(property); CheckForUnknownKey(property); } + CheckForNullComplexProperties(); } else if (EntityState == EntityState.Deleted) { @@ -1617,6 +1614,31 @@ void CheckForUnknownKey(IProperty property) throw new InvalidOperationException(CoreStrings.UnknownShadowKeyValue(entityType.DisplayName(), property.Name)); } } + + void CheckForNullCollection(IProperty property) + { + if (property.GetElementType() != null + && !property.IsNullable + && GetCurrentValue(property) == null) + { + throw new InvalidOperationException( + CoreStrings.NullRequiredPrimitiveCollection(EntityType.DisplayName(), property.Name)); + } + } + + void CheckForNullComplexProperties() + { + foreach (var complexProperty in entityType.GetFlattenedComplexProperties()) + { + if (!complexProperty.IsNullable + && this[complexProperty] == null) + { + throw new InvalidOperationException( + CoreStrings.NullRequiredComplexProperty( + complexProperty.DeclaringType.ClrType.ShortDisplayName(), complexProperty.Name)); + } + } + } } /// @@ -1695,7 +1717,7 @@ public void HandleConceptualNulls(bool sensitiveLoggingEnabled, bool force, bool CoreStrings.RelationshipConceptualNullSensitive( foreignKey.PrincipalEntityType.DisplayName(), EntityType.DisplayName(), - BuildOriginalValuesString(foreignKey.Properties))); + this.BuildOriginalValuesString(foreignKey.Properties))); } throw new InvalidOperationException( @@ -1718,7 +1740,7 @@ public void HandleConceptualNulls(bool sensitiveLoggingEnabled, bool force, bool CoreStrings.PropertyConceptualNullSensitive( property.Name, EntityType.DisplayName(), - BuildOriginalValuesString(new[] { property }))); + this.BuildOriginalValuesString(new[] { property }))); } throw new InvalidOperationException( @@ -1776,7 +1798,7 @@ public bool HasExplicitValue(IProperty property) private bool HasSentinel(IProperty property) => property.IsShadowProperty() ? AreEqual(_shadowValues[property.GetShadowIndex()], property.Sentinel, property) - : property.GetGetter().HasSentinel(Entity); + : property.GetGetter().HasSentinelUsingContainingEntity(Entity); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -2024,24 +2046,6 @@ public bool IsLoaded(INavigationBase navigation) return lazyLoaderProperty != null ? (ILazyLoader?)this[lazyLoaderProperty] : null; } - /// - /// 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. - /// - public string BuildCurrentValuesString(IEnumerable properties) - => ((IInternalEntry)this).BuildCurrentValuesString(properties); - - /// - /// 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. - /// - public string BuildOriginalValuesString(IEnumerable properties) - => ((IInternalEntry)this).BuildOriginalValuesString(properties); - /// /// 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 @@ -2068,18 +2072,6 @@ public DebugView DebugView IEntityType IUpdateEntry.EntityType => EntityType; - IRuntimeEntityType IInternalEntry.EntityType - => EntityType; - - object IInternalEntry.Object - => Entity; - - IInternalEntry IInternalEntry.PrepareToSave() - => PrepareToSave(); - - void IInternalEntry.SetEntityState(EntityState entityState, bool acceptChanges, bool modifyProperties) - => SetEntityState(entityState, acceptChanges, modifyProperties); - private enum CurrentValueType { Normal, diff --git a/src/EFCore/ChangeTracking/Internal/OriginalValuesFactoryFactory.cs b/src/EFCore/ChangeTracking/Internal/OriginalValuesFactoryFactory.cs index ebc0663ccdc..cb38d9f6878 100644 --- a/src/EFCore/ChangeTracking/Internal/OriginalValuesFactoryFactory.cs +++ b/src/EFCore/ChangeTracking/Internal/OriginalValuesFactoryFactory.cs @@ -11,7 +11,7 @@ namespace Microsoft.EntityFrameworkCore.ChangeTracking.Internal; /// 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. /// -public class OriginalValuesFactoryFactory : SnapshotFactoryFactory +public class OriginalValuesFactoryFactory : SnapshotFactoryFactory { /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to diff --git a/src/EFCore/ChangeTracking/Internal/SidecarValuesFactoryFactory.cs b/src/EFCore/ChangeTracking/Internal/SidecarValuesFactoryFactory.cs index e0f758c815e..c66736bdad9 100644 --- a/src/EFCore/ChangeTracking/Internal/SidecarValuesFactoryFactory.cs +++ b/src/EFCore/ChangeTracking/Internal/SidecarValuesFactoryFactory.cs @@ -11,7 +11,7 @@ namespace Microsoft.EntityFrameworkCore.ChangeTracking.Internal; /// 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. /// -public class SidecarValuesFactoryFactory : SnapshotFactoryFactory +public class SidecarValuesFactoryFactory : SnapshotFactoryFactory { /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to diff --git a/src/EFCore/ChangeTracking/Internal/SimpleFullyNullableDependentKeyValueFactory.cs b/src/EFCore/ChangeTracking/Internal/SimpleFullyNullableDependentKeyValueFactory.cs index 3f4334aa65b..7ac738c4966 100644 --- a/src/EFCore/ChangeTracking/Internal/SimpleFullyNullableDependentKeyValueFactory.cs +++ b/src/EFCore/ChangeTracking/Internal/SimpleFullyNullableDependentKeyValueFactory.cs @@ -61,7 +61,7 @@ public virtual bool TryCreateFromBuffer(in ValueBuffer valueBuffer, [NotNullWhen /// public override bool TryCreateFromCurrentValues(IUpdateEntry entry, [NotNullWhen(true)] out TKey? key) { - key = ((Func)_propertyAccessors.CurrentValueGetter)((IInternalEntry)entry); + key = ((Func)_propertyAccessors.CurrentValueGetter)((InternalEntityEntry)entry); return key != null; } @@ -73,7 +73,7 @@ public override bool TryCreateFromCurrentValues(IUpdateEntry entry, [NotNullWhen /// public virtual bool TryCreateFromPreStoreGeneratedCurrentValues(IUpdateEntry entry, [NotNullWhen(true)] out TKey? key) { - key = ((Func)_propertyAccessors.PreStoreGeneratedCurrentValueGetter)((IInternalEntry)entry); + key = ((Func)_propertyAccessors.PreStoreGeneratedCurrentValueGetter)((InternalEntityEntry)entry); return key != null; } @@ -85,7 +85,7 @@ public virtual bool TryCreateFromPreStoreGeneratedCurrentValues(IUpdateEntry ent /// public override bool TryCreateFromOriginalValues(IUpdateEntry entry, [NotNullWhen(true)] out TKey? key) { - key = ((Func)_propertyAccessors.OriginalValueGetter!)((IInternalEntry)entry); + key = ((Func)_propertyAccessors.OriginalValueGetter!)((InternalEntityEntry)entry); return key != null; } diff --git a/src/EFCore/ChangeTracking/Internal/SimpleNonNullableDependentKeyValueFactory.cs b/src/EFCore/ChangeTracking/Internal/SimpleNonNullableDependentKeyValueFactory.cs index c41962e0e35..2c9162b6ab5 100644 --- a/src/EFCore/ChangeTracking/Internal/SimpleNonNullableDependentKeyValueFactory.cs +++ b/src/EFCore/ChangeTracking/Internal/SimpleNonNullableDependentKeyValueFactory.cs @@ -68,7 +68,7 @@ public virtual bool TryCreateFromBuffer(in ValueBuffer valueBuffer, [NotNullWhen /// public override bool TryCreateFromCurrentValues(IUpdateEntry entry, [NotNullWhen(true)] out TKey? key) { - key = ((Func)_propertyAccessors.CurrentValueGetter)((IInternalEntry)entry)!; + key = ((Func)_propertyAccessors.CurrentValueGetter)((InternalEntityEntry)entry)!; return true; } @@ -80,7 +80,7 @@ public override bool TryCreateFromCurrentValues(IUpdateEntry entry, [NotNullWhen /// public virtual bool TryCreateFromPreStoreGeneratedCurrentValues(IUpdateEntry entry, [NotNullWhen(true)] out TKey? key) { - key = ((Func)_propertyAccessors.PreStoreGeneratedCurrentValueGetter)((IInternalEntry)entry)!; + key = ((Func)_propertyAccessors.PreStoreGeneratedCurrentValueGetter)((InternalEntityEntry)entry)!; return true; } @@ -92,7 +92,7 @@ public virtual bool TryCreateFromPreStoreGeneratedCurrentValues(IUpdateEntry ent /// public override bool TryCreateFromOriginalValues(IUpdateEntry entry, [NotNullWhen(true)] out TKey? key) { - key = ((Func)_propertyAccessors.OriginalValueGetter!)((IInternalEntry)entry)!; + key = ((Func)_propertyAccessors.OriginalValueGetter!)((InternalEntityEntry)entry)!; return true; } diff --git a/src/EFCore/ChangeTracking/Internal/SimpleNullableDependentKeyValueFactory.cs b/src/EFCore/ChangeTracking/Internal/SimpleNullableDependentKeyValueFactory.cs index 85b357c0e13..ce52fb1cd8d 100644 --- a/src/EFCore/ChangeTracking/Internal/SimpleNullableDependentKeyValueFactory.cs +++ b/src/EFCore/ChangeTracking/Internal/SimpleNullableDependentKeyValueFactory.cs @@ -66,7 +66,7 @@ public virtual bool TryCreateFromBuffer(in ValueBuffer valueBuffer, out TKey key /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public override bool TryCreateFromCurrentValues(IUpdateEntry entry, out TKey key) - => HandleNullableValue(((Func)_propertyAccessors.CurrentValueGetter)((IInternalEntry)entry), out key); + => HandleNullableValue(((Func)_propertyAccessors.CurrentValueGetter)((InternalEntityEntry)entry), out key); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -76,7 +76,7 @@ public override bool TryCreateFromCurrentValues(IUpdateEntry entry, out TKey key /// public virtual bool TryCreateFromPreStoreGeneratedCurrentValues(IUpdateEntry entry, out TKey key) => HandleNullableValue( - ((Func)_propertyAccessors.PreStoreGeneratedCurrentValueGetter)((IInternalEntry)entry), out key); + ((Func)_propertyAccessors.PreStoreGeneratedCurrentValueGetter)((InternalEntityEntry)entry), out key); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -85,7 +85,7 @@ public virtual bool TryCreateFromPreStoreGeneratedCurrentValues(IUpdateEntry ent /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public override bool TryCreateFromOriginalValues(IUpdateEntry entry, out TKey key) - => HandleNullableValue(((Func)_propertyAccessors.OriginalValueGetter!)((InternalEntityEntry)entry), out key); + => HandleNullableValue(((Func)_propertyAccessors.OriginalValueGetter!)((InternalEntityEntry)entry), out key); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to diff --git a/src/EFCore/ChangeTracking/Internal/SimpleNullablePrincipalDependentKeyValueFactory.cs b/src/EFCore/ChangeTracking/Internal/SimpleNullablePrincipalDependentKeyValueFactory.cs index a32ed396555..b350bec9434 100644 --- a/src/EFCore/ChangeTracking/Internal/SimpleNullablePrincipalDependentKeyValueFactory.cs +++ b/src/EFCore/ChangeTracking/Internal/SimpleNullablePrincipalDependentKeyValueFactory.cs @@ -72,7 +72,7 @@ public virtual bool TryCreateFromBuffer(in ValueBuffer valueBuffer, [NotNullWhen /// public override bool TryCreateFromCurrentValues(IUpdateEntry entry, [NotNullWhen(true)] out TKey? key) { - key = (TKey)(object)((Func)_propertyAccessors.CurrentValueGetter)((IInternalEntry)entry)!; + key = (TKey)(object)((Func)_propertyAccessors.CurrentValueGetter)((InternalEntityEntry)entry)!; return true; } @@ -84,7 +84,7 @@ public override bool TryCreateFromCurrentValues(IUpdateEntry entry, [NotNullWhen /// public virtual bool TryCreateFromPreStoreGeneratedCurrentValues(IUpdateEntry entry, [NotNullWhen(true)] out TKey? key) { - key = (TKey)(object)((Func)_propertyAccessors.PreStoreGeneratedCurrentValueGetter)((IInternalEntry)entry)!; + key = (TKey)(object)((Func)_propertyAccessors.PreStoreGeneratedCurrentValueGetter)((InternalEntityEntry)entry)!; return true; } @@ -96,7 +96,7 @@ public virtual bool TryCreateFromPreStoreGeneratedCurrentValues(IUpdateEntry ent /// public override bool TryCreateFromOriginalValues(IUpdateEntry entry, [NotNullWhen(true)] out TKey? key) { - key = (TKey)(object)((Func)_propertyAccessors.OriginalValueGetter!)((IInternalEntry)entry)!; + key = (TKey)(object)((Func)_propertyAccessors.OriginalValueGetter!)((InternalEntityEntry)entry)!; return true; } diff --git a/src/EFCore/ChangeTracking/Internal/SnapshotFactoryFactory.cs b/src/EFCore/ChangeTracking/Internal/SnapshotFactoryFactory.cs index 8ac652f44a1..aba7539759d 100644 --- a/src/EFCore/ChangeTracking/Internal/SnapshotFactoryFactory.cs +++ b/src/EFCore/ChangeTracking/Internal/SnapshotFactoryFactory.cs @@ -171,9 +171,7 @@ protected virtual Expression CreateSnapshotExpression( Expression.Assign( entityVariable, Expression.Convert( - Expression.Property(parameter!, parameter!.Type == typeof(InternalEntityEntry) - ? nameof(InternalEntityEntry.Entity) - : nameof(IInternalEntry.Object)), + Expression.Property(parameter!, nameof(InternalEntityEntry.Entity)), entityType!)), constructorExpression }) diff --git a/src/EFCore/ChangeTracking/ListComparer.cs b/src/EFCore/ChangeTracking/ListComparer.cs index 2f9220722b0..af6eea0fe6b 100644 --- a/src/EFCore/ChangeTracking/ListComparer.cs +++ b/src/EFCore/ChangeTracking/ListComparer.cs @@ -23,15 +23,28 @@ public sealed class ListComparer : ValueComparer /// Creates a new instance of the list comparer. /// /// The comparer to use for comparing elements. - public ListComparer(ValueComparer elementComparer) + public ListComparer(ValueComparer elementComparer) : base( - (a, b) => Compare(a, b, elementComparer), - o => GetHashCode(o, elementComparer), - source => Snapshot(source, elementComparer)) + (a, b) => Compare(a, b, (ValueComparer)elementComparer), + o => GetHashCode(o, (ValueComparer)elementComparer), + source => Snapshot(source, (ValueComparer)elementComparer)) { + ElementComparer = elementComparer; } - private static bool Compare(IEnumerable? a, IEnumerable? b, ValueComparer elementComparer) + /// + /// The comparer to use for comparing elements. + /// + public ValueComparer ElementComparer { get; } + + /// + /// 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. + /// + [EntityFrameworkInternal] + public static bool Compare(IEnumerable? a, IEnumerable? b, ValueComparer elementComparer) { if (ReferenceEquals(a, b)) { @@ -85,11 +98,17 @@ private static bool Compare(IEnumerable? a, IEnumerable? b, throw new InvalidOperationException( CoreStrings.BadListType( (a is IList ? b : a).GetType().ShortDisplayName(), - typeof(ListComparer).ShortDisplayName(), typeof(IList<>).MakeGenericType(elementComparer.Type).ShortDisplayName())); } - private static int GetHashCode(IEnumerable source, ValueComparer elementComparer) + /// + /// 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. + /// + [EntityFrameworkInternal] + public static int GetHashCode(IEnumerable source, ValueComparer elementComparer) { var hash = new HashCode(); @@ -101,14 +120,20 @@ private static int GetHashCode(IEnumerable source, ValueComparer Snapshot(IEnumerable source, ValueComparer elementComparer) + /// + /// 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. + /// + [EntityFrameworkInternal] + public static IList Snapshot(IEnumerable source, ValueComparer elementComparer) { - if (!(source is IList sourceList)) + if (source is not IList sourceList) { throw new InvalidOperationException( CoreStrings.BadListType( source.GetType().ShortDisplayName(), - typeof(ListComparer).ShortDisplayName(), typeof(IList<>).MakeGenericType(elementComparer.Type).ShortDisplayName())); } diff --git a/src/EFCore/ChangeTracking/NullableValueTypeListComparer.cs b/src/EFCore/ChangeTracking/NullableValueTypeListComparer.cs index d2e8ac223ee..58914950734 100644 --- a/src/EFCore/ChangeTracking/NullableValueTypeListComparer.cs +++ b/src/EFCore/ChangeTracking/NullableValueTypeListComparer.cs @@ -24,15 +24,28 @@ public sealed class NullableValueTypeListComparer : ValueComparer /// The comparer to use for comparing elements. - public NullableValueTypeListComparer(ValueComparer elementComparer) + public NullableValueTypeListComparer(ValueComparer elementComparer) : base( - (a, b) => Compare(a, b, elementComparer), - o => GetHashCode(o, elementComparer), - source => Snapshot(source, elementComparer)) + (a, b) => Compare(a, b, (ValueComparer)elementComparer), + o => GetHashCode(o, (ValueComparer)elementComparer), + source => Snapshot(source, (ValueComparer)elementComparer)) { + ElementComparer = elementComparer; } - private static bool Compare(IEnumerable? a, IEnumerable? b, ValueComparer elementComparer) + /// + /// The comparer to use for comparing elements. + /// + public ValueComparer ElementComparer { get; } + + /// + /// 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. + /// + [EntityFrameworkInternal] + public static bool Compare(IEnumerable? a, IEnumerable? b, ValueComparer elementComparer) { if (ReferenceEquals(a, b)) { @@ -86,11 +99,17 @@ private static bool Compare(IEnumerable? a, IEnumerable? b throw new InvalidOperationException( CoreStrings.BadListType( (a is IList ? b : a).GetType().ShortDisplayName(), - typeof(NullableValueTypeListComparer).ShortDisplayName(), typeof(IList<>).MakeGenericType(elementComparer.Type.MakeNullable()).ShortDisplayName())); } - private static int GetHashCode(IEnumerable source, ValueComparer elementComparer) + /// + /// 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. + /// + [EntityFrameworkInternal] + public static int GetHashCode(IEnumerable source, ValueComparer elementComparer) { var hash = new HashCode(); @@ -102,14 +121,20 @@ private static int GetHashCode(IEnumerable source, ValueComparer Snapshot(IEnumerable source, ValueComparer elementComparer) + /// + /// 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. + /// + [EntityFrameworkInternal] + public static IList Snapshot(IEnumerable source, ValueComparer elementComparer) { - if (!(source is IList sourceList)) + if (source is not IList sourceList) { throw new InvalidOperationException( CoreStrings.BadListType( source.GetType().ShortDisplayName(), - typeof(NullableValueTypeListComparer).ShortDisplayName(), typeof(IList<>).MakeGenericType(elementComparer.Type.MakeNullable()).ShortDisplayName())); } @@ -120,7 +145,7 @@ private static int GetHashCode(IEnumerable source, ValueComparer source, ValueComparer +/// A for lists of primitive items. The list can be typed as , +/// but can only be used with instances that implement . +/// +/// +/// +/// This comparer should be used when the element of the comparer is typed as . +/// +/// +/// See EF Core value comparers for more information and examples. +/// +/// +/// The element type. +public sealed class ObjectListComparer : ValueComparer> +{ + /// + /// Creates a new instance of the list comparer. + /// + /// The comparer to use for comparing elements. + public ObjectListComparer(ValueComparer elementComparer) + : base( + (a, b) => Compare(a, b, elementComparer), + o => GetHashCode(o, elementComparer), + source => Snapshot(source, elementComparer)) + { + ElementComparer = elementComparer; + } + + /// + /// The comparer to use for comparing elements. + /// + public ValueComparer ElementComparer { get; } + + /// + /// 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. + /// + [EntityFrameworkInternal] + public static bool Compare(IEnumerable? a, IEnumerable? b, ValueComparer elementComparer) + { + if (ReferenceEquals(a, b)) + { + return true; + } + + if (a is null) + { + return b is null; + } + + if (b is null) + { + return false; + } + + if (a is IList aList && b is IList bList) + { + if (aList.Count != bList.Count) + { + return false; + } + + for (var i = 0; i < aList.Count; i++) + { + var (el1, el2) = (aList[i], bList[i]); + if (el1 is null) + { + if (el2 is null) + { + continue; + } + + return false; + } + + if (el2 is null) + { + return false; + } + + if (!elementComparer.Equals(el1, el2)) + { + return false; + } + } + + return true; + } + + throw new InvalidOperationException( + CoreStrings.BadListType( + (a is IList ? b : a).GetType().ShortDisplayName(), + typeof(IList<>).MakeGenericType(elementComparer.Type).ShortDisplayName())); + } + + /// + /// 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. + /// + [EntityFrameworkInternal] + public static int GetHashCode(IEnumerable source, ValueComparer elementComparer) + { + var hash = new HashCode(); + + foreach (var el in source) + { + hash.Add(el == null ? 0 : elementComparer.GetHashCode(el)); + } + + return hash.ToHashCode(); + } + + /// + /// 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. + /// + [EntityFrameworkInternal] + public static IList Snapshot(IEnumerable source, ValueComparer elementComparer) + { + if (source is not IList sourceList) + { + throw new InvalidOperationException( + CoreStrings.BadListType( + source.GetType().ShortDisplayName(), + typeof(IList<>).MakeGenericType(elementComparer.Type).ShortDisplayName())); + } + + if (sourceList.IsReadOnly) + { + var snapshot = new TElement[sourceList.Count]; + + for (var i = 0; i < sourceList.Count; i++) + { + var instance = sourceList[i]; + if (instance != null) + { + snapshot[i] = (TElement)elementComparer.Snapshot(instance); + } + } + + return snapshot; + } + else + { + var snapshot = (source is List || sourceList.IsReadOnly) + ? new List(sourceList.Count) + : (IList)Activator.CreateInstance(source.GetType())!; + + foreach (var e in sourceList) + { + snapshot.Add(e == null ? (TElement)(object?)null! : (TElement)elementComparer.Snapshot(e)); + } + + return snapshot; + } + } +} diff --git a/src/EFCore/Design/Internal/CSharpRuntimeAnnotationCodeGenerator.cs b/src/EFCore/Design/Internal/CSharpRuntimeAnnotationCodeGenerator.cs index 21e0612f98e..50e9c4e8a15 100644 --- a/src/EFCore/Design/Internal/CSharpRuntimeAnnotationCodeGenerator.cs +++ b/src/EFCore/Design/Internal/CSharpRuntimeAnnotationCodeGenerator.cs @@ -358,22 +358,43 @@ public static void Create( ICSharpHelper codeHelper) { var mainBuilder = parameters.MainBuilder; - AddNamespace(typeof(ValueConverter<,>), parameters.Namespaces); - AddNamespace(converter.ModelClrType, parameters.Namespaces); - AddNamespace(converter.ProviderClrType, parameters.Namespaces); + var constructor = converter.GetType().GetDeclaredConstructor(new[] { typeof(JsonValueReaderWriter) }); + var jsonReaderWriterProperty = converter.GetType().GetProperty(nameof(CollectionToJsonStringConverter.JsonReaderWriter)); + if (constructor == null + || jsonReaderWriterProperty == null) + { + AddNamespace(typeof(ValueConverter<,>), parameters.Namespaces); + AddNamespace(converter.ModelClrType, parameters.Namespaces); + AddNamespace(converter.ProviderClrType, parameters.Namespaces); - mainBuilder - .Append("new ValueConverter<") - .Append(codeHelper.Reference(converter.ModelClrType)) - .Append(", ") - .Append(codeHelper.Reference(converter.ProviderClrType)) - .AppendLine(">(") - .IncrementIndent() - .Append(codeHelper.Expression(converter.ConvertToProviderExpression, parameters.Namespaces)) - .AppendLine(",") - .Append(codeHelper.Expression(converter.ConvertFromProviderExpression, parameters.Namespaces)) - .Append(")") - .DecrementIndent(); + mainBuilder + .Append("new ValueConverter<") + .Append(codeHelper.Reference(converter.ModelClrType)) + .Append(", ") + .Append(codeHelper.Reference(converter.ProviderClrType)) + .AppendLine(">(") + .IncrementIndent() + .Append(codeHelper.Expression(converter.ConvertToProviderExpression, parameters.Namespaces)) + .AppendLine(",") + .Append(codeHelper.Expression(converter.ConvertFromProviderExpression, parameters.Namespaces)) + .Append(")") + .DecrementIndent(); + } + else + { + AddNamespace(converter.GetType(), parameters.Namespaces); + + mainBuilder + .Append("new ") + .Append(codeHelper.Reference(converter.GetType())) + .Append("("); + + CreateJsonValueReaderWriter((JsonValueReaderWriter)jsonReaderWriterProperty.GetValue(converter)!, parameters, codeHelper); + + mainBuilder + .Append(")") + .DecrementIndent(); + } } /// @@ -388,21 +409,43 @@ public static void Create( ICSharpHelper codeHelper) { var mainBuilder = parameters.MainBuilder; - AddNamespace(typeof(ValueComparer<>), parameters.Namespaces); - AddNamespace(comparer.Type, parameters.Namespaces); - mainBuilder - .Append("new ValueComparer<") - .Append(codeHelper.Reference(comparer.Type)) - .AppendLine(">(") - .IncrementIndent() - .AppendLines(codeHelper.Expression(comparer.EqualsExpression, parameters.Namespaces), skipFinalNewline: true) - .AppendLine(",") - .AppendLines(codeHelper.Expression(comparer.HashCodeExpression, parameters.Namespaces), skipFinalNewline: true) - .AppendLine(",") - .AppendLines(codeHelper.Expression(comparer.SnapshotExpression, parameters.Namespaces), skipFinalNewline: true) - .Append(")") - .DecrementIndent(); + var constructor = comparer.GetType().GetDeclaredConstructor(new[] { typeof(ValueComparer) }); + var elementComparerProperty = comparer.GetType().GetProperty(nameof(ListComparer.ElementComparer)); + if (constructor == null + || elementComparerProperty == null) + { + AddNamespace(typeof(ValueComparer<>), parameters.Namespaces); + AddNamespace(comparer.Type, parameters.Namespaces); + + mainBuilder + .Append("new ValueComparer<") + .Append(codeHelper.Reference(comparer.Type)) + .AppendLine(">(") + .IncrementIndent() + .AppendLines(codeHelper.Expression(comparer.EqualsExpression, parameters.Namespaces), skipFinalNewline: true) + .AppendLine(",") + .AppendLines(codeHelper.Expression(comparer.HashCodeExpression, parameters.Namespaces), skipFinalNewline: true) + .AppendLine(",") + .AppendLines(codeHelper.Expression(comparer.SnapshotExpression, parameters.Namespaces), skipFinalNewline: true) + .Append(")") + .DecrementIndent(); + } + else + { + AddNamespace(comparer.GetType(), parameters.Namespaces); + + mainBuilder + .Append("new ") + .Append(codeHelper.Reference(comparer.GetType())) + .Append("("); + + Create((ValueComparer)elementComparerProperty.GetValue(comparer)!, parameters, codeHelper); + + mainBuilder + .Append(")") + .DecrementIndent(); + } } /// @@ -435,7 +478,7 @@ public static void CreateJsonValueReaderWriter( .Append(")") .DecrementIndent(); return; - } + } if (jsonValueReaderWriter is ICompositeJsonValueReaderWriter compositeJsonValueReaderWriter) { diff --git a/src/EFCore/Diagnostics/ILoggingOptions.cs b/src/EFCore/Diagnostics/ILoggingOptions.cs index 77dd28e0795..3060c484713 100644 --- a/src/EFCore/Diagnostics/ILoggingOptions.cs +++ b/src/EFCore/Diagnostics/ILoggingOptions.cs @@ -43,7 +43,7 @@ public interface ILoggingOptions : ISingletonOptions /// /// Returns if a warning about string values for the given enum type has not yet been performed. /// - /// The type to check. + /// The type to check. /// Whether or not a warning has been issued. - bool ShouldWarnForEnumType(Type type); + bool ShouldWarnForStringEnumValueInJson(Type enumType); } diff --git a/src/EFCore/Diagnostics/Internal/LoggingOptions.cs b/src/EFCore/Diagnostics/Internal/LoggingOptions.cs index 98b67bd7a9c..1ce5f190765 100644 --- a/src/EFCore/Diagnostics/Internal/LoggingOptions.cs +++ b/src/EFCore/Diagnostics/Internal/LoggingOptions.cs @@ -104,14 +104,14 @@ public virtual void Validate(IDbContextOptions options) public virtual WarningsConfiguration WarningsConfiguration { get; private set; } = null!; /// - public virtual bool ShouldWarnForEnumType(Type type) + public virtual bool ShouldWarnForStringEnumValueInJson(Type enumType) { - if (_warnedForStringEnums.ContainsKey(type)) + if (_warnedForStringEnums.ContainsKey(enumType)) { return false; } - _warnedForStringEnums[type] = true; + _warnedForStringEnums[enumType] = true; return true; } } diff --git a/src/EFCore/EF.cs b/src/EFCore/EF.cs index 60f05cddaca..76b7c5b4880 100644 --- a/src/EFCore/EF.cs +++ b/src/EFCore/EF.cs @@ -41,7 +41,7 @@ internal static MethodInfo MakePropertyMethod(Type type) public static bool IsDesignTime { get; set; } /// - /// References a given property or navigation on an entity instance. This is useful for shadow state properties, for + /// References a given property or navigation on an entity or complex type instance. This is useful for shadow state properties, for /// which no CLR property exists. Currently this method can only be used in LINQ queries and can not be used to /// access the value assigned to a property in other scenarios. /// @@ -54,11 +54,11 @@ internal static MethodInfo MakePropertyMethod(Type type) /// /// /// The type of the property being referenced. - /// The entity to access the property on. + /// The entity or complex type to access the property on. /// The name of the property. /// The value assigned to the property. public static TProperty Property( - object entity, + object instance, [NotParameterized] string propertyName) => throw new InvalidOperationException(CoreStrings.PropertyMethodInvoked); diff --git a/src/EFCore/Infrastructure/ExpressionExtensions.cs b/src/EFCore/Infrastructure/ExpressionExtensions.cs index e2764778894..8d47e09064d 100644 --- a/src/EFCore/Infrastructure/ExpressionExtensions.cs +++ b/src/EFCore/Infrastructure/ExpressionExtensions.cs @@ -384,6 +384,13 @@ private static Expression CreateEFPropertyExpression( propertyType = propertyType.MakeNullable(); } + // EF.Property expects an object as its first argument. If the target is a struct (complex type), we need an explicit up-cast to + // object. + if (target.Type.IsValueType) + { + target = Expression.Convert(target, typeof(object)); + } + return Expression.Call( EF.MakePropertyMethod(propertyType), target, diff --git a/src/EFCore/Infrastructure/IDbContextOptionsExtension.cs b/src/EFCore/Infrastructure/IDbContextOptionsExtension.cs index 0d1fc3122d6..afd8d89700e 100644 --- a/src/EFCore/Infrastructure/IDbContextOptionsExtension.cs +++ b/src/EFCore/Infrastructure/IDbContextOptionsExtension.cs @@ -32,6 +32,14 @@ public interface IDbContextOptionsExtension /// The collection to add services to. void ApplyServices(IServiceCollection services); + /// + /// Gives the extension a chance to configure defaults based on other options. + /// Most extensions do not have dynamic defaults and so this will be a no-op. + /// + /// The options being validated. + IDbContextOptionsExtension ApplyDefaults(IDbContextOptions options) + => this; + /// /// Gives the extension a chance to validate that all options in the extension are valid. /// Most extensions do not have invalid combinations and so this will be a no-op. diff --git a/src/EFCore/Infrastructure/ModelValidator.cs b/src/EFCore/Infrastructure/ModelValidator.cs index 76eeb462189..36a139e12d8 100644 --- a/src/EFCore/Infrastructure/ModelValidator.cs +++ b/src/EFCore/Infrastructure/ModelValidator.cs @@ -60,6 +60,7 @@ public virtual void Validate(IModel model, IDiagnosticsLogger + /// Validates the mapping of primitive collection properties the model. + /// + /// The model to validate. + /// The logger to use. + protected virtual void ValidatePrimitiveCollections( + IModel model, + IDiagnosticsLogger logger) + { + foreach (var entityType in model.GetEntityTypes()) + { + Validate(entityType, logger); + } + + static void Validate(ITypeBase typeBase, IDiagnosticsLogger logger) + { + foreach (var property in typeBase.GetDeclaredProperties()) + { + var elementClrType = property.GetElementType()?.ClrType; + if (property is { IsPrimitiveCollection: true, ClrType.IsArray: false, ClrType.IsSealed: true } + && elementClrType is { IsSealed: true } + && elementClrType.TryGetElementType(typeof(IList<>)) == null) + { + throw new InvalidOperationException( + CoreStrings.BadListType( + property.ClrType.ShortDisplayName(), + typeof(IList<>).MakeGenericType(elementClrType).ShortDisplayName())); + } + } + + foreach (var complexProperty in typeBase.GetDeclaredComplexProperties()) + { + Validate(complexProperty.ComplexType, logger); + } + } + } + /// /// Validates the mapping/configuration of query filters in the model. /// diff --git a/src/EFCore/Metadata/Builders/IConventionPropertyBuilder.cs b/src/EFCore/Metadata/Builders/IConventionPropertyBuilder.cs index f1a68be7ef6..ae3e3d683d4 100644 --- a/src/EFCore/Metadata/Builders/IConventionPropertyBuilder.cs +++ b/src/EFCore/Metadata/Builders/IConventionPropertyBuilder.cs @@ -545,7 +545,7 @@ bool CanSetProviderValueComparer( /// 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. /// - IConventionElementTypeBuilder? ElementType(bool elementType, bool fromDataAnnotation = false); + IConventionElementTypeBuilder? SetElementType(Type? elementType, bool fromDataAnnotation = false); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -553,5 +553,5 @@ bool CanSetProviderValueComparer( /// 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. /// - bool CanSetElementType(bool elementType, bool fromDataAnnotation = false); + bool CanSetElementType(Type? elementType, bool fromDataAnnotation = false); } diff --git a/src/EFCore/Metadata/Builders/PrimitiveCollectionBuilder.cs b/src/EFCore/Metadata/Builders/PrimitiveCollectionBuilder.cs index 82d8e475714..7e2152b87ae 100644 --- a/src/EFCore/Metadata/Builders/PrimitiveCollectionBuilder.cs +++ b/src/EFCore/Metadata/Builders/PrimitiveCollectionBuilder.cs @@ -365,7 +365,7 @@ public virtual PrimitiveCollectionBuilder HasField(string fieldName) /// /// A builder to configure the collection element type. public virtual ElementTypeBuilder ElementType() - => new((IMutableElementType)Builder.Metadata.GetElementType()!); + => new(Builder.Metadata.GetElementType()!); /// /// Configures the elements of this collection. diff --git a/src/EFCore/Metadata/Conventions/ElementMappingConvention.cs b/src/EFCore/Metadata/Conventions/ElementMappingConvention.cs new file mode 100644 index 00000000000..d9b1f765868 --- /dev/null +++ b/src/EFCore/Metadata/Conventions/ElementMappingConvention.cs @@ -0,0 +1,55 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Metadata.Conventions; + +/// +/// A convention that ensures property mappings have any ElementMapping discovered by the type mapper. +/// +/// +/// +/// See Model building conventions for more information and examples. +/// +/// +public class ElementMappingConvention : IModelFinalizingConvention +{ + /// + /// Creates a new instance of . + /// + /// Parameter object containing dependencies for this convention. + public ElementMappingConvention(ProviderConventionSetBuilderDependencies dependencies) + { + Dependencies = dependencies; + } + + /// + /// Dependencies for this service. + /// + protected virtual ProviderConventionSetBuilderDependencies Dependencies { get; } + + /// + public void ProcessModelFinalizing(IConventionModelBuilder modelBuilder, IConventionContext context) + { + foreach (var entityType in modelBuilder.Metadata.GetEntityTypes()) + { + Validate(entityType); + } + + void Validate(IConventionTypeBase typeBase) + { + foreach (var property in typeBase.GetDeclaredProperties()) + { + var typeMapping = Dependencies.TypeMappingSource.FindMapping((IProperty)property); + if (typeMapping is { ElementTypeMapping: not null }) + { + property.SetElementType(property.ClrType.TryGetElementType(typeof(IEnumerable<>))); + } + } + + foreach (var complexProperty in typeBase.GetDeclaredComplexProperties()) + { + Validate(complexProperty.ComplexType); + } + } + } +} diff --git a/src/EFCore/Metadata/Conventions/Infrastructure/ProviderConventionSetBuilder.cs b/src/EFCore/Metadata/Conventions/Infrastructure/ProviderConventionSetBuilder.cs index 4a356d038d8..43b3ec9adaa 100644 --- a/src/EFCore/Metadata/Conventions/Infrastructure/ProviderConventionSetBuilder.cs +++ b/src/EFCore/Metadata/Conventions/Infrastructure/ProviderConventionSetBuilder.cs @@ -100,6 +100,7 @@ public virtual ConventionSet CreateConventionSet() conventionSet.Add(new BackingFieldConvention(Dependencies)); conventionSet.Add(new QueryFilterRewritingConvention(Dependencies)); conventionSet.Add(new RuntimeModelConvention(Dependencies)); + conventionSet.Add(new ElementMappingConvention(Dependencies)); return conventionSet; } diff --git a/src/EFCore/Metadata/Conventions/PropertyDiscoveryConvention.cs b/src/EFCore/Metadata/Conventions/PropertyDiscoveryConvention.cs index 8cfb7acfd50..fc3cd2dd6aa 100644 --- a/src/EFCore/Metadata/Conventions/PropertyDiscoveryConvention.cs +++ b/src/EFCore/Metadata/Conventions/PropertyDiscoveryConvention.cs @@ -94,7 +94,11 @@ private void Process(IConventionEntityTypeBuilder entityTypeBuilder) var propertyBuilder = entityTypeBuilder.Property(propertyInfo); if (mapping?.ElementTypeMapping != null) { - propertyBuilder?.ElementType(true); + var elementType = propertyInfo.PropertyType.TryGetElementType(typeof(IEnumerable<>)); + if (elementType != null) + { + propertyBuilder?.SetElementType(elementType); + } } } } diff --git a/src/EFCore/Metadata/Conventions/RuntimeModelConvention.cs b/src/EFCore/Metadata/Conventions/RuntimeModelConvention.cs index ca406251699..5c8b33830d0 100644 --- a/src/EFCore/Metadata/Conventions/RuntimeModelConvention.cs +++ b/src/EFCore/Metadata/Conventions/RuntimeModelConvention.cs @@ -62,7 +62,7 @@ protected virtual RuntimeModel Create(IModel model) var elementType = property.GetElementType(); if (elementType != null) { - var runtimeElementType = Create(runtimeProperty, elementType); + var runtimeElementType = Create(runtimeProperty, elementType, property.IsPrimitiveCollection); CreateAnnotations( elementType, runtimeElementType, static (convention, annotations, source, target, runtime) => convention.ProcessElementTypeAnnotations(annotations, source, target, runtime)); @@ -398,7 +398,7 @@ private static RuntimeProperty Create(IProperty property, RuntimeTypeBase runtim property.GetJsonValueReaderWriter(), property.GetTypeMapping()); - private static RuntimeElementType Create(RuntimeProperty runtimeProperty, IElementType element) + private static RuntimeElementType Create(RuntimeProperty runtimeProperty, IElementType element, bool primitiveCollection) => runtimeProperty.SetElementType( element.ClrType, element.IsNullable, @@ -410,7 +410,8 @@ private static RuntimeElementType Create(RuntimeProperty runtimeProperty, IEleme element.GetValueConverter(), element.GetValueComparer(), element.GetJsonValueReaderWriter(), - element.GetTypeMapping()); + element.GetTypeMapping(), + primitiveCollection); /// /// Updates the property annotations that will be set on the read-only object. @@ -524,7 +525,7 @@ private RuntimeComplexProperty Create(IComplexProperty complexProperty, RuntimeE var elementType = property.GetElementType(); if (elementType != null) { - var runtimeElementType = Create(runtimeProperty, elementType); + var runtimeElementType = Create(runtimeProperty, elementType, property.IsPrimitiveCollection); CreateAnnotations( elementType, runtimeElementType, static (convention, annotations, source, target, runtime) => convention.ProcessElementTypeAnnotations(annotations, source, target, runtime)); @@ -571,7 +572,7 @@ private RuntimeComplexProperty Create(IComplexProperty complexProperty, RuntimeC var elementType = property.GetElementType(); if (elementType != null) { - var runtimeElementType = Create(runtimeProperty, elementType); + var runtimeElementType = Create(runtimeProperty, elementType, property.IsPrimitiveCollection); CreateAnnotations( elementType, runtimeElementType, static (convention, annotations, source, target, runtime) => convention.ProcessElementTypeAnnotations(annotations, source, target, runtime)); diff --git a/src/EFCore/Metadata/IClrPropertyGetter.cs b/src/EFCore/Metadata/IClrPropertyGetter.cs index 5121716081a..2b6a2838ee2 100644 --- a/src/EFCore/Metadata/IClrPropertyGetter.cs +++ b/src/EFCore/Metadata/IClrPropertyGetter.cs @@ -24,26 +24,26 @@ public interface IClrPropertyGetter /// /// The entity instance. /// The property value. - object? GetClrValue(object entity); + object? GetClrValueUsingContainingEntity(object entity); /// /// Checks whether or not the property is set to the CLR default for its type. /// /// The entity instance. /// if the property value is the CLR default; it is any other value. - bool HasSentinel(object entity); + bool HasSentinelUsingContainingEntity(object entity); /// /// Gets the property value from the declaring type. /// - /// The complex type instance instance. + /// The entity or complex type instance. /// The property value. - object? GetStructuralTypeClrValue(object complexObject); + object? GetClrValue(object structuralObject); /// /// Checks whether or not the property is set to the CLR default for its type. /// - /// The complex type instance instance. + /// The entity or complex type instance. /// if the property value is the CLR default; it is any other value. - bool HasStructuralTypeSentinelValue(object complexObject); + bool HasSentinel(object structuralObject); } diff --git a/src/EFCore/Metadata/IConventionProperty.cs b/src/EFCore/Metadata/IConventionProperty.cs index 4bc664c3527..39b407f9120 100644 --- a/src/EFCore/Metadata/IConventionProperty.cs +++ b/src/EFCore/Metadata/IConventionProperty.cs @@ -460,17 +460,27 @@ bool IsImplicitlyCreated() /// The configuration source for . ConfigurationSource? GetJsonValueReaderWriterTypeConfigurationSource(); + /// + /// Gets the configuration for elements of the primitive collection represented by this property. + /// + /// The configuration for the elements. + new IConventionElementType? GetElementType(); + /// /// Sets the configuration for elements of the primitive collection represented by this property. /// - /// If , then this is a collection of primitive elements. + /// If , then the type mapping has an element type, otherwise it is removed. /// Indicates whether the configuration was specified using a data annotation. /// The configuration for the elements. - IElementType? ElementType(bool primitiveCollection, bool fromDataAnnotation = false); + IConventionElementType? SetElementType(Type? elementType, bool fromDataAnnotation = false); /// /// Returns the configuration source for . /// /// The configuration source for . ConfigurationSource? GetElementTypeConfigurationSource(); + + /// + IReadOnlyElementType? IReadOnlyProperty.GetElementType() + => GetElementType(); } diff --git a/src/EFCore/Metadata/IMutableProperty.cs b/src/EFCore/Metadata/IMutableProperty.cs index 5b9ea2539c8..c8e04a37669 100644 --- a/src/EFCore/Metadata/IMutableProperty.cs +++ b/src/EFCore/Metadata/IMutableProperty.cs @@ -273,11 +273,17 @@ void SetProviderValueComparer( /// void SetJsonValueReaderWriterType(Type? readerWriterType); + /// + /// Gets the configuration for elements of the primitive collection represented by this property. + /// + /// The configuration for the elements. + new IMutableElementType? GetElementType(); + /// /// Sets the configuration for elements of the primitive collection represented by this property. /// /// If , then this is a collection of primitive elements. - void ElementType(bool elementType); + void SetElementType(Type? elementType); /// bool IReadOnlyProperty.IsNullable @@ -290,4 +296,8 @@ ValueGenerated IReadOnlyProperty.ValueGenerated /// bool IReadOnlyProperty.IsConcurrencyToken => IsConcurrencyToken; + + /// + IReadOnlyElementType? IReadOnlyProperty.GetElementType() + => GetElementType(); } diff --git a/src/EFCore/Metadata/INavigationBase.cs b/src/EFCore/Metadata/INavigationBase.cs index 27664c247ef..2eec136e42b 100644 --- a/src/EFCore/Metadata/INavigationBase.cs +++ b/src/EFCore/Metadata/INavigationBase.cs @@ -62,7 +62,7 @@ void SetIsLoadedWhenNoTracking(object entity) foreach (var serviceProperty in serviceProperties) { - ((ILazyLoader?)serviceProperty.GetGetter().GetClrValue(entity))?.SetLoaded(entity, Name); + ((ILazyLoader?)serviceProperty.GetGetter().GetClrValueUsingContainingEntity(entity))?.SetLoaded(entity, Name); } } } diff --git a/src/EFCore/Metadata/IProperty.cs b/src/EFCore/Metadata/IProperty.cs index cbf7f7c3429..82eb39d9283 100644 --- a/src/EFCore/Metadata/IProperty.cs +++ b/src/EFCore/Metadata/IProperty.cs @@ -100,6 +100,12 @@ IEqualityComparer CreateKeyEqualityComparer() /// The comparer. new ValueComparer GetProviderValueComparer(); + /// + /// Gets the configuration for elements of the primitive collection represented by this property. + /// + /// The configuration for the elements. + new IElementType? GetElementType(); + internal const DynamicallyAccessedMemberTypes DynamicallyAccessedMemberTypes = System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicConstructors diff --git a/src/EFCore/Metadata/IReadOnlyProperty.cs b/src/EFCore/Metadata/IReadOnlyProperty.cs index 708ea5f2e46..533e771bf01 100644 --- a/src/EFCore/Metadata/IReadOnlyProperty.cs +++ b/src/EFCore/Metadata/IReadOnlyProperty.cs @@ -177,7 +177,13 @@ CoreTypeMapping GetTypeMapping() /// Gets the configuration for elements of the primitive collection represented by this property. /// /// The configuration for the elements. - IElementType? GetElementType(); + IReadOnlyElementType? GetElementType(); + + /// + /// A property is a primitive collection if it has an element type that matches the element type of the CLR type. + /// + /// if the property represents a primitive collection. + bool IsPrimitiveCollection { get; } /// /// Finds the first principal property that the given property is constrained by diff --git a/src/EFCore/Metadata/ITypeBase.cs b/src/EFCore/Metadata/ITypeBase.cs index dd5666c2ec3..4da2cbb1434 100644 --- a/src/EFCore/Metadata/ITypeBase.cs +++ b/src/EFCore/Metadata/ITypeBase.cs @@ -212,6 +212,12 @@ public interface ITypeBase : IReadOnlyTypeBase, IAnnotatable /// The properties. IEnumerable GetFlattenedProperties(); + /// + /// Returns all properties that implement , including those on complex types. + /// + /// The properties. + IEnumerable GetFlattenedComplexProperties(); + /// /// Returns all declared properties that implement , including those on complex types. /// diff --git a/src/EFCore/Metadata/Internal/ClrPropertyGetter.cs b/src/EFCore/Metadata/Internal/ClrPropertyGetter.cs index a7f74c546ae..19018008d92 100644 --- a/src/EFCore/Metadata/Internal/ClrPropertyGetter.cs +++ b/src/EFCore/Metadata/Internal/ClrPropertyGetter.cs @@ -45,7 +45,7 @@ public ClrPropertyGetter( /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public object? GetClrValue(object entity) + public object? GetClrValueUsingContainingEntity(object entity) => _getter((TEntity)entity); /// @@ -55,7 +55,7 @@ public ClrPropertyGetter( /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool HasSentinel(object entity) + public bool HasSentinelUsingContainingEntity(object entity) => _hasSentinelValue((TEntity)entity); /// @@ -65,8 +65,8 @@ public bool HasSentinel(object entity) /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public object? GetStructuralTypeClrValue(object complexObject) - => _structuralTypeGetter((TStructuralType)complexObject); + public object? GetClrValue(object structuralObject) + => _structuralTypeGetter((TStructuralType)structuralObject); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -75,6 +75,6 @@ public bool HasSentinel(object entity) /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool HasStructuralTypeSentinelValue(object complexObject) - => _hasStructuralTypeSentinelValue((TStructuralType)complexObject); + public bool HasSentinel(object structuralObject) + => _hasStructuralTypeSentinelValue((TStructuralType)structuralObject); } diff --git a/src/EFCore/Metadata/Internal/ClrPropertySetterFactory.cs b/src/EFCore/Metadata/Internal/ClrPropertySetterFactory.cs index 92dddfd55be..f562e9660cc 100644 --- a/src/EFCore/Metadata/Internal/ClrPropertySetterFactory.cs +++ b/src/EFCore/Metadata/Internal/ClrPropertySetterFactory.cs @@ -74,24 +74,73 @@ protected override IClrPropertySetter CreateGeneric(setter) : new ClrPropertySetter(setter); - Expression CreateMemberAssignment(IPropertyBase? property, Expression typeParameter) + Expression CreateMemberAssignment(IPropertyBase? property, Expression instanceParameter) { - var targetStructuralType = typeParameter; if (property?.DeclaringType is IComplexType complexType) { - targetStructuralType = PropertyBase.CreateMemberAccess( - complexType.ComplexProperty, - typeParameter, - complexType.ComplexProperty.GetMemberInfo(forMaterialization: false, forSet: false), - fromContainingType: false); + // The idea here is to create something like this: + // + // $level1 = $entity.k__BackingField; + // $level2 = $level1.k__BackingField; + // $level3 = $level2.k__BackingField; + // $level3.k__BackingField = $value; + // $level2.k__BackingField = $level3; + // $level1.k__BackingField = $level2; + // $entity.k__BackingField = $level1 + // + // That is, we create copies of value types, make the assignment, and then copy the value back. + + var chain = complexType.ComplexProperty.GetChainToComplexProperty().ToList(); + var previousLevel = instanceParameter; + + var variables = new List(); + var assignments = new List(); + var chainCount = chain.Count; + for (var i = 1; i <= chainCount; i++) + { + var currentProperty = chain[chainCount - i]; + var complexMemberInfo = currentProperty.GetMemberInfo(forMaterialization: false, forSet: false); + var complexPropertyType = complexMemberInfo.GetMemberType(); + var currentLevel = Expression.Variable(complexPropertyType, $"level{i}"); + variables.Add(currentLevel); + assignments.Add( + Expression.Assign( + currentLevel, PropertyBase.CreateMemberAccess( + currentProperty, + previousLevel, + complexMemberInfo, + fromContainingType: true))); + previousLevel = currentLevel; + } + + var propertyMemberInfo = property.GetMemberInfo(forMaterialization: false, forSet: true); + assignments.Add(Expression.MakeMemberAccess(previousLevel, propertyMemberInfo).Assign(convertedParameter)); + + for (var i = chainCount - 1; i >= 0; i--) + { + var currentProperty = chain[chainCount - 1 - i]; + var complexMemberInfo = currentProperty.GetMemberInfo(forMaterialization: false, forSet: true); + if (complexMemberInfo.GetMemberType().IsValueType) + { + var memberExpression = (MemberExpression)PropertyBase.CreateMemberAccess( + currentProperty, + i == 0 ? instanceParameter : variables[i - 1], + complexMemberInfo, + fromContainingType: true); + + assignments.Add(memberExpression.Assign(variables[i])); + } + } + + return Expression.Block(variables, assignments); } return propertyBase?.IsIndexerProperty() == true ? Expression.Assign( Expression.MakeIndex( - targetStructuralType, (PropertyInfo)memberInfo, new List { Expression.Constant(propertyBase.Name) }), + instanceParameter, (PropertyInfo)memberInfo, new List { Expression.Constant(propertyBase.Name) }), convertedParameter) - : Expression.MakeMemberAccess(targetStructuralType, memberInfo).Assign(convertedParameter); + : Expression.MakeMemberAccess(instanceParameter, memberInfo).Assign(convertedParameter); } } } diff --git a/src/EFCore/Metadata/Internal/ComplexPropertyExtensions.cs b/src/EFCore/Metadata/Internal/ComplexPropertyExtensions.cs new file mode 100644 index 00000000000..91bc2a30871 --- /dev/null +++ b/src/EFCore/Metadata/Internal/ComplexPropertyExtensions.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Metadata.Internal; + +/// +/// 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. +/// +public static class ComplexPropertyExtensions +{ + /// + /// 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. + /// + public static IEnumerable GetChainToComplexProperty(this IComplexProperty property) + { + yield return property; + + if (property.DeclaringType is IComplexType complexType) + { + foreach (var nestedProperty in complexType.ComplexProperty.GetChainToComplexProperty()) + { + yield return nestedProperty; + } + } + } +} diff --git a/src/EFCore/Metadata/Internal/EntityType.cs b/src/EFCore/Metadata/Internal/EntityType.cs index e9b012c5066..83807c1bea7 100644 --- a/src/EFCore/Metadata/Internal/EntityType.cs +++ b/src/EFCore/Metadata/Internal/EntityType.cs @@ -61,8 +61,8 @@ private readonly SortedDictionary _triggers private InstantiationBinding? _serviceOnlyConstructorBinding; private Func? _relationshipSnapshotFactory; - private Func? _originalValuesFactory; - private Func? _temporaryValuesFactory; + private Func? _originalValuesFactory; + private Func? _temporaryValuesFactory; private Func? _storeGeneratedValuesFactory; private Func? _shadowValuesFactory; private Func? _emptyShadowValuesFactory; @@ -2285,7 +2285,7 @@ public virtual Func RelationshipSnapshotFactory /// 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. /// - public virtual Func OriginalValuesFactory + public virtual Func OriginalValuesFactory => NonCapturingLazyInitializer.EnsureInitialized( ref _originalValuesFactory, this, static entityType => @@ -2315,7 +2315,7 @@ public virtual Func StoreGeneratedValuesFactory /// 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. /// - public virtual Func TemporaryValuesFactory + public virtual Func TemporaryValuesFactory => NonCapturingLazyInitializer.EnsureInitialized( ref _temporaryValuesFactory, this, static entityType => diff --git a/src/EFCore/Metadata/Internal/IRuntimeEntityType.cs b/src/EFCore/Metadata/Internal/IRuntimeEntityType.cs index cd2c5449b96..30c960c2dfe 100644 --- a/src/EFCore/Metadata/Internal/IRuntimeEntityType.cs +++ b/src/EFCore/Metadata/Internal/IRuntimeEntityType.cs @@ -105,7 +105,7 @@ int ComplexPropertyCount /// 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. /// - Func OriginalValuesFactory { get; } + Func OriginalValuesFactory { get; } /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -121,7 +121,7 @@ int ComplexPropertyCount /// 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. /// - Func TemporaryValuesFactory { get; } + Func TemporaryValuesFactory { get; } /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to diff --git a/src/EFCore/Metadata/Internal/InternalPropertyBuilder.cs b/src/EFCore/Metadata/Internal/InternalPropertyBuilder.cs index 9339a4940c6..8ab768d6725 100644 --- a/src/EFCore/Metadata/Internal/InternalPropertyBuilder.cs +++ b/src/EFCore/Metadata/Internal/InternalPropertyBuilder.cs @@ -496,6 +496,7 @@ public virtual bool CanSetValueGeneratorFactory( { if (CanSetConversion(converter, configurationSource)) { + Metadata.SetElementType(null, configurationSource); Metadata.SetProviderClrType(null, configurationSource); Metadata.SetValueConverter(converter, configurationSource); @@ -531,6 +532,7 @@ public virtual bool CanSetConversion( { if (CanSetConversion(providerClrType, configurationSource)) { + Metadata.SetElementType(null, configurationSource); Metadata.SetValueConverter((ValueConverter?)null, configurationSource); Metadata.SetProviderClrType(providerClrType, configurationSource); @@ -564,7 +566,7 @@ public virtual bool CanSetConversion(Type? providerClrType, ConfigurationSource? { if (CanSetConverter(converterType, configurationSource)) { - Metadata.ElementType(false, configurationSource); + Metadata.SetElementType(null, configurationSource); Metadata.SetProviderClrType(null, configurationSource); Metadata.SetValueConverter(converterType, configurationSource); @@ -776,13 +778,13 @@ public virtual bool CanSetProviderValueComparer( /// 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. /// - public virtual InternalElementTypeBuilder? ElementType(bool elementType, ConfigurationSource configurationSource) + public virtual InternalElementTypeBuilder? SetElementType(Type? elementType, ConfigurationSource configurationSource) { if (CanSetElementType(elementType, configurationSource)) { - Metadata.ElementType(elementType, configurationSource); + Metadata.SetElementType(elementType, configurationSource); Metadata.SetValueConverter((Type?)null, configurationSource); - return new InternalElementTypeBuilder((ElementType)Metadata.GetElementType()!, ModelBuilder); + return new InternalElementTypeBuilder(Metadata.GetElementType()!, ModelBuilder); } return null; @@ -794,9 +796,9 @@ public virtual bool CanSetProviderValueComparer( /// 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. /// - public virtual bool CanSetElementType(bool elementType, ConfigurationSource? configurationSource) + public virtual bool CanSetElementType(Type? elementType, ConfigurationSource? configurationSource) => configurationSource.Overrides(Metadata.GetElementTypeConfigurationSource()) - && (elementType != (Metadata.GetElementType() != null)); + && (elementType != Metadata.GetElementType()?.ClrType); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -1523,8 +1525,8 @@ bool IConventionPropertyBuilder.CanSetProviderValueComparer( /// 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. /// - IConventionElementTypeBuilder? IConventionPropertyBuilder.ElementType(bool elementType, bool fromDataAnnotation) - => ElementType(elementType, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); + IConventionElementTypeBuilder? IConventionPropertyBuilder.SetElementType(Type? elementType, bool fromDataAnnotation) + => SetElementType(elementType, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -1532,6 +1534,6 @@ bool IConventionPropertyBuilder.CanSetProviderValueComparer( /// 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. /// - bool IConventionPropertyBuilder.CanSetElementType(bool elementType, bool fromDataAnnotation) + bool IConventionPropertyBuilder.CanSetElementType(Type? elementType, bool fromDataAnnotation) => CanSetElementType(elementType, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); } diff --git a/src/EFCore/Metadata/Internal/InternalTypeBaseBuilder.cs b/src/EFCore/Metadata/Internal/InternalTypeBaseBuilder.cs index aa4a3b435e3..6f19dd76331 100644 --- a/src/EFCore/Metadata/Internal/InternalTypeBaseBuilder.cs +++ b/src/EFCore/Metadata/Internal/InternalTypeBaseBuilder.cs @@ -333,7 +333,16 @@ public static bool IsCompatible(MemberInfo? newMemberInfo, PropertyBase existing { var builder = Property(propertyType, propertyName, memberInfo, typeConfigurationSource, configurationSource); - builder?.ElementType(true, configurationSource!.Value); + if (builder != null) + { + var elementClrType = builder.Metadata.ClrType.TryGetElementType(typeof(IEnumerable<>)); + if (elementClrType == null) + { + throw new InvalidOperationException(CoreStrings.NotCollection(builder.Metadata.ClrType.ShortDisplayName(), propertyName)); + } + + builder.SetElementType(elementClrType, configurationSource!.Value); + } return builder; } diff --git a/src/EFCore/Metadata/Internal/Property.cs b/src/EFCore/Metadata/Internal/Property.cs index c7bd85acb51..72d94ba293b 100644 --- a/src/EFCore/Metadata/Internal/Property.cs +++ b/src/EFCore/Metadata/Internal/Property.cs @@ -1205,8 +1205,8 @@ public virtual CoreTypeMapping? TypeMapping /// 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. /// - public virtual IElementType? GetElementType() - => (IElementType?)this[CoreAnnotationNames.ElementType]; + public virtual ElementType? GetElementType() + => (ElementType?)this[CoreAnnotationNames.ElementType]; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -1214,28 +1214,41 @@ public virtual CoreTypeMapping? TypeMapping /// 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. /// - public virtual IElementType? ElementType( - bool elementType, + public virtual bool IsPrimitiveCollection + { + get + { + var elementType = GetElementType(); + return elementType != null + && ClrType.TryGetElementType(typeof(IEnumerable<>))?.UnwrapNullableType() + .IsAssignableFrom(elementType.ClrType.UnwrapNullableType()) == true; + } + } + + /// + /// 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. + /// + public virtual ElementType? SetElementType( + Type? elementType, ConfigurationSource configurationSource) { var existingElementType = GetElementType(); - if (existingElementType == null - && elementType) + if (elementType != null + && elementType != existingElementType?.ClrType) { - var elementClrType = ClrType.TryGetElementType(typeof(IEnumerable<>)); - if (elementClrType == null) - { - throw new InvalidOperationException(CoreStrings.NotCollection(ClrType.ShortDisplayName(), Name)); - } - var newElementType = new ElementType(elementClrType, this, configurationSource); + var newElementType = new ElementType(elementType, this, configurationSource); SetAnnotation(CoreAnnotationNames.ElementType, newElementType, configurationSource); OnElementTypeSet(newElementType, null); return newElementType; } - if (existingElementType != null && !elementType) + if (elementType == null + && existingElementType != null) { - ((ElementType)existingElementType).SetRemovedFromModel(); + existingElementType.SetRemovedFromModel(); RemoveAnnotation(CoreAnnotationNames.ElementType); OnElementTypeSet(null, existingElementType); return null; @@ -1393,6 +1406,16 @@ IConventionPropertyBuilder IConventionProperty.Builder get => Builder; } + /// + /// 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. + /// + [DebuggerStepThrough] + IReadOnlyElementType? IReadOnlyProperty.GetElementType() + => GetElementType(); + /// /// 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 @@ -2007,8 +2030,8 @@ void IMutableProperty.SetJsonValueReaderWriterType(Type? readerWriterType) /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [DebuggerStepThrough] - IElementType? IConventionProperty.ElementType(bool elementType, bool fromDataAnnotation) - => ElementType( + IConventionElementType? IConventionProperty.SetElementType(Type? elementType, bool fromDataAnnotation) + => SetElementType( elementType, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); @@ -2019,6 +2042,36 @@ void IMutableProperty.SetJsonValueReaderWriterType(Type? readerWriterType) /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [DebuggerStepThrough] - void IMutableProperty.ElementType(bool elementType) - => ElementType(elementType, ConfigurationSource.Explicit); + void IMutableProperty.SetElementType(Type? elementType) + => SetElementType(elementType, ConfigurationSource.Explicit); + + /// + /// 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. + /// + [DebuggerStepThrough] + IMutableElementType? IMutableProperty.GetElementType() + => GetElementType(); + + /// + /// 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. + /// + [DebuggerStepThrough] + IElementType? IProperty.GetElementType() + => GetElementType(); + + /// + /// 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. + /// + [DebuggerStepThrough] + IConventionElementType? IConventionProperty.GetElementType() + => GetElementType(); } diff --git a/src/EFCore/Metadata/Internal/PropertyAccessorsFactory.cs b/src/EFCore/Metadata/Internal/PropertyAccessorsFactory.cs index 6abbbd08aec..2c2686a453e 100644 --- a/src/EFCore/Metadata/Internal/PropertyAccessorsFactory.cs +++ b/src/EFCore/Metadata/Internal/PropertyAccessorsFactory.cs @@ -42,12 +42,12 @@ private static PropertyAccessors CreateGeneric(IPropertyBase property property == null ? null : CreateValueBufferGetter(property)); } - private static Func CreateCurrentValueGetter( + private static Func CreateCurrentValueGetter( IPropertyBase propertyBase, bool useStoreGeneratedValues) { var entityClrType = propertyBase.DeclaringType.ContainingEntityType.ClrType; - var entryParameter = Expression.Parameter(typeof(IInternalEntry), "entry"); + var entryParameter = Expression.Parameter(typeof(InternalEntityEntry), "entry"); var propertyIndex = propertyBase.GetIndex(); var shadowIndex = propertyBase.GetShadowIndex(); var storeGeneratedIndex = propertyBase.GetStoreGeneratedIndex(); @@ -66,7 +66,7 @@ private static Func CreateCurrentValueGetter CreateCurrentValueGetter>( + return Expression.Lambda>( currentValueExpression, entryParameter) .Compile(); } - private static Func CreateOriginalValueGetter(IProperty property) + private static Func CreateOriginalValueGetter(IProperty property) { - var entryParameter = Expression.Parameter(typeof(IInternalEntry), "entry"); + var entryParameter = Expression.Parameter(typeof(InternalEntityEntry), "entry"); var originalValuesIndex = property.GetOriginalValueIndex(); - return Expression.Lambda>( + return Expression.Lambda>( originalValuesIndex >= 0 ? Expression.Call( entryParameter, diff --git a/src/EFCore/Metadata/Internal/PropertyBase.cs b/src/EFCore/Metadata/Internal/PropertyBase.cs index 48fb9799099..ac81bb041fa 100644 --- a/src/EFCore/Metadata/Internal/PropertyBase.cs +++ b/src/EFCore/Metadata/Internal/PropertyBase.cs @@ -455,30 +455,18 @@ public static Expression CreateMemberAccess( complexType.ComplexProperty.GetMemberInfo(forMaterialization: false, forSet: false), fromContainingType); - // TODO: Handle null/default complex types #31376 - // if (!instanceExpression.Type.IsValueType) - // { - // var instanceVariable = Expression.Variable(instanceExpression.Type, "instance"); - // return Expression.Block( - // new[] { instanceVariable }, - // Expression.Assign(instanceVariable, instanceExpression), - // Expression.Condition( - // Expression.Equal(instanceVariable, Expression.Constant(null)), - // forWrite - // ? Expression.Block( - // Expression.Throw(Expression.Constant(new InvalidOperationException())), - // Expression.Default(memberInfo.GetMemberType())) - // : Expression.Default(memberInfo.GetMemberType()), - // Expression.MakeMemberAccess(instanceExpression, memberInfo))); - // } - - // if (instanceExpression.Type.IsNullableValueType()) - // { - // return Expression.Condition( - // Expression.Equal(instanceExpression, Expression.Constant(null)), - // Expression.Default(memberInfo.GetMemberType()), - // Expression.MakeMemberAccess(instanceExpression, memberInfo)); - // } + if (!instanceExpression.Type.IsValueType + || instanceExpression.Type.IsNullableValueType()) + { + var instanceVariable = Expression.Variable(instanceExpression.Type, "instance"); + return Expression.Block( + new[] { instanceVariable }, + Expression.Assign(instanceVariable, instanceExpression), + Expression.Condition( + Expression.Equal(instanceVariable, Expression.Constant(null)), + Expression.Default(memberInfo.GetMemberType()), + Expression.MakeMemberAccess(instanceExpression, memberInfo))); + } } return Expression.MakeMemberAccess(instanceExpression, memberInfo); diff --git a/src/EFCore/Metadata/Internal/TypeBase.cs b/src/EFCore/Metadata/Internal/TypeBase.cs index 1fe07fef630..29d232cbff0 100644 --- a/src/EFCore/Metadata/Internal/TypeBase.cs +++ b/src/EFCore/Metadata/Internal/TypeBase.cs @@ -1326,6 +1326,23 @@ public virtual IEnumerable GetFlattenedProperties() } } + /// + /// Returns all members from this type and all nested complex types, if any. + /// + /// The properties. + public virtual IEnumerable GetFlattenedComplexProperties() + { + foreach (var complexProperty in GetComplexProperties()) + { + yield return complexProperty; + + foreach (var nestedComplexProperty in complexProperty.ComplexType.GetFlattenedComplexProperties()) + { + yield return nestedComplexProperty; + } + } + } + /// /// Returns all members from this type and all nested complex types, if any. /// @@ -2205,6 +2222,13 @@ IEnumerable ITypeBase.GetSnapshottableMembers() IEnumerable ITypeBase.GetFlattenedProperties() => GetFlattenedProperties(); + /// + /// Returns all properties that implement , including those on complex types. + /// + /// The properties. + IEnumerable ITypeBase.GetFlattenedComplexProperties() + => GetFlattenedComplexProperties(); + /// /// Returns all properties declared properties that implement , including those on complex types. /// diff --git a/src/EFCore/Metadata/ParameterBindingInfo.cs b/src/EFCore/Metadata/ParameterBindingInfo.cs index c2a011455c0..034a02f5eed 100644 --- a/src/EFCore/Metadata/ParameterBindingInfo.cs +++ b/src/EFCore/Metadata/ParameterBindingInfo.cs @@ -17,7 +17,7 @@ public readonly struct ParameterBindingInfo /// The entity or complex type for this binding. /// The expression tree from which the parameter value will come. public ParameterBindingInfo( - IEntityType structuralType, + ITypeBase structuralType, Expression materializationContextExpression) { Check.NotNull(structuralType, nameof(structuralType)); diff --git a/src/EFCore/Metadata/RuntimeEntityType.cs b/src/EFCore/Metadata/RuntimeEntityType.cs index bf51b042def..23d85a2b61b 100644 --- a/src/EFCore/Metadata/RuntimeEntityType.cs +++ b/src/EFCore/Metadata/RuntimeEntityType.cs @@ -53,8 +53,8 @@ private readonly SortedDictionary _triggers private Func? _relationshipSnapshotFactory; private IProperty[]? _foreignKeyProperties; private IProperty[]? _valueGeneratingProperties; - private Func? _originalValuesFactory; - private Func? _temporaryValuesFactory; + private Func? _originalValuesFactory; + private Func? _temporaryValuesFactory; private Func? _storeGeneratedValuesFactory; private Func? _shadowValuesFactory; private Func? _emptyShadowValuesFactory; @@ -1293,7 +1293,7 @@ PropertyAccessMode IReadOnlyEntityType.GetNavigationAccessMode() /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [EntityFrameworkInternal] - public virtual void SetOriginalValuesFactory(Func factory) + public virtual void SetOriginalValuesFactory(Func factory) => _originalValuesFactory = factory; /// @@ -1313,7 +1313,7 @@ public virtual void SetStoreGeneratedValuesFactory(Func factory) /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [EntityFrameworkInternal] - public virtual void SetTemporaryValuesFactory(Func factory) + public virtual void SetTemporaryValuesFactory(Func factory) => _temporaryValuesFactory = factory; /// @@ -1343,7 +1343,7 @@ public virtual void SetEmptyShadowValuesFactory(Func factory) /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [EntityFrameworkInternal] - public virtual Func OriginalValuesFactory + public virtual Func OriginalValuesFactory => NonCapturingLazyInitializer.EnsureInitialized( ref _originalValuesFactory, this, static complexType => RuntimeFeature.IsDynamicCodeSupported @@ -1371,7 +1371,7 @@ public virtual Func StoreGeneratedValuesFactory /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [EntityFrameworkInternal] - public virtual Func TemporaryValuesFactory + public virtual Func TemporaryValuesFactory => NonCapturingLazyInitializer.EnsureInitialized( ref _temporaryValuesFactory, this, static complexType => RuntimeFeature.IsDynamicCodeSupported diff --git a/src/EFCore/Metadata/RuntimeProperty.cs b/src/EFCore/Metadata/RuntimeProperty.cs index 6bdcfcc03cc..2d3c3aa122a 100644 --- a/src/EFCore/Metadata/RuntimeProperty.cs +++ b/src/EFCore/Metadata/RuntimeProperty.cs @@ -125,6 +125,7 @@ public RuntimeProperty( /// The for this property. /// The for this property. /// The for this property. + /// A value indicating whether this property represents a primitive collection. /// The newly created property. public virtual RuntimeElementType SetElementType( Type clrType, @@ -137,7 +138,8 @@ public virtual RuntimeElementType SetElementType( ValueConverter? valueConverter = null, ValueComparer? valueComparer = null, JsonValueReaderWriter? jsonValueReaderWriter = null, - CoreTypeMapping? typeMapping = null) + CoreTypeMapping? typeMapping = null, + bool primitiveCollection = false) { var elementType = new RuntimeElementType( clrType, @@ -155,6 +157,8 @@ public virtual RuntimeElementType SetElementType( SetAnnotation(CoreAnnotationNames.ElementType, elementType); + IsPrimitiveCollection = primitiveCollection; + return elementType; } @@ -305,6 +309,14 @@ public override object? Sentinel public virtual IElementType? GetElementType() => (IElementType?)this[CoreAnnotationNames.ElementType]; + /// + /// 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. + /// + public virtual bool IsPrimitiveCollection { get; private set; } + /// /// 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 @@ -325,6 +337,10 @@ public virtual DebugView DebugView public override string ToString() => ((IReadOnlyProperty)this).ToDebugString(MetadataDebugStringOptions.SingleLineDefault); + /// + IReadOnlyElementType? IReadOnlyProperty.GetElementType() + => GetElementType(); + /// bool IReadOnlyProperty.IsNullable { diff --git a/src/EFCore/Metadata/RuntimeTypeBase.cs b/src/EFCore/Metadata/RuntimeTypeBase.cs index d10237f7ea2..d62351e936f 100644 --- a/src/EFCore/Metadata/RuntimeTypeBase.cs +++ b/src/EFCore/Metadata/RuntimeTypeBase.cs @@ -30,6 +30,7 @@ public abstract class RuntimeTypeBase : AnnotatableBase, IRuntimeTypeBase // Warning: Never access these fields directly as access needs to be thread-safe private RuntimeProperty[]? _flattenedProperties; private RuntimeProperty[]? _flattenedDeclaredProperties; + private RuntimeComplexProperty[]? _flattenedComplexProperties; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -505,6 +506,30 @@ static IEnumerable Create(RuntimeTypeBase type) } } + /// + /// Returns all members from this type and all nested complex types, if any. + /// + /// The properties. + public virtual IEnumerable GetFlattenedComplexProperties() + { + return NonCapturingLazyInitializer.EnsureInitialized( + ref _flattenedComplexProperties, this, + static type => Create(type).ToArray()); + + static IEnumerable Create(RuntimeTypeBase type) + { + foreach (var complexProperty in type.GetComplexProperties()) + { + yield return complexProperty; + + foreach (var nestedComplexProperty in complexProperty.ComplexType.GetFlattenedComplexProperties()) + { + yield return nestedComplexProperty; + } + } + } + } + /// /// Returns all members from this type and all nested complex types, if any. /// @@ -741,6 +766,13 @@ IEnumerable ITypeBase.GetSnapshottableMembers() IEnumerable ITypeBase.GetFlattenedProperties() => GetFlattenedProperties(); + /// + /// Returns all properties that implement , including those on complex types. + /// + /// The properties. + IEnumerable ITypeBase.GetFlattenedComplexProperties() + => GetFlattenedComplexProperties(); + /// /// Returns all properties declared properties that implement , including those on complex types. /// diff --git a/src/EFCore/Properties/CoreStrings.Designer.cs b/src/EFCore/Properties/CoreStrings.Designer.cs index 1d6d20162be..be1e60cf391 100644 --- a/src/EFCore/Properties/CoreStrings.Designer.cs +++ b/src/EFCore/Properties/CoreStrings.Designer.cs @@ -203,12 +203,12 @@ public static string BadJsonValueReaderWriterType(object? givenType) givenType); /// - /// The type '{givenType}' cannot be used with '{comparerType}' because it does not implement '{listType}'. Collections of primitive types must be ordered lists. + /// The type '{givenType}' cannot be used as a primitive collection because it is not an array and does not implement '{listType}'. Collections of primitive types must be arrays or ordered lists. /// - public static string BadListType(object? givenType, object? comparerType, object? listType) + public static string BadListType(object? givenType, object? listType) => string.Format( - GetString("BadListType", nameof(givenType), nameof(comparerType), nameof(listType)), - givenType, comparerType, listType); + GetString("BadListType", nameof(givenType), nameof(listType)), + givenType, listType); /// /// The type '{givenType}' cannot be used as a value comparer because it does not inherit from '{expectedType}'. Make sure to inherit value comparers from '{expectedType}'. @@ -2159,6 +2159,14 @@ public static string NullableKey(object? entityType, object? property) GetString("NullableKey", nameof(entityType), nameof(property)), entityType, property); + /// + /// The complex type property '{type}.{property}' is configured as required (non-nullable) but has a null value when saving changes. Only non-null complex properties are supported by EF Core 8. + /// + public static string NullRequiredComplexProperty(object? type, object? property) + => string.Format( + GetString("NullRequiredComplexProperty", nameof(type), nameof(property)), + type, property); + /// /// The primitive collection property '{type}.{property}' is configured as required (non-nullable) but has a null value when saving changes. Either mark the property as optional (nullable) or set a non-null value. /// diff --git a/src/EFCore/Properties/CoreStrings.resx b/src/EFCore/Properties/CoreStrings.resx index 9ba2559fe40..4b08beee106 100644 --- a/src/EFCore/Properties/CoreStrings.resx +++ b/src/EFCore/Properties/CoreStrings.resx @@ -184,7 +184,7 @@ The type '{givenType}' cannot be used as a 'JsonValueReaderWriter' because it does not inherit from the generic 'JsonValueReaderWriter<TValue>'. Make sure to inherit json reader/writers from 'JsonValueReaderWriter<TValue>'. - The type '{givenType}' cannot be used with '{comparerType}' because it does not implement '{listType}'. Collections of primitive types must be ordered lists. + The type '{givenType}' cannot be used as a primitive collection because it is not an array and does not implement '{listType}'. Collections of primitive types must be arrays or ordered lists. The type '{givenType}' cannot be used as a value comparer because it does not inherit from '{expectedType}'. Make sure to inherit value comparers from '{expectedType}'. @@ -1246,6 +1246,9 @@ A key on entity type '{entityType}' cannot contain the property '{property}' because it is nullable/optional. All properties on which a key is declared must be marked as non-nullable/required. + + The complex type property '{type}.{property}' is configured as required (non-nullable) but has a null value when saving changes. Only non-null complex properties are supported by EF Core 8. + The primitive collection property '{type}.{property}' is configured as required (non-nullable) but has a null value when saving changes. Either mark the property as optional (nullable) or set a non-null value. diff --git a/src/EFCore/Query/Internal/EntityMaterializerSource.cs b/src/EFCore/Query/Internal/EntityMaterializerSource.cs index 894186f1df0..138986831a5 100644 --- a/src/EFCore/Query/Internal/EntityMaterializerSource.cs +++ b/src/EFCore/Query/Internal/EntityMaterializerSource.cs @@ -163,9 +163,7 @@ IComplexProperty complexProperty static Expression CreateMemberAssignment(Expression parameter, MemberInfo memberInfo, IPropertyBase property, Expression value) { - if (property is IProperty prop - && prop.GetTypeMapping().ElementTypeMapping != null - && !prop.ClrType.IsArray) + if (property is IProperty { IsPrimitiveCollection: true, ClrType.IsArray: false }) { var currentVariable = Expression.Variable(property.ClrType); return Expression.Block( diff --git a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs index 285c4dd5bf1..26388a01c89 100644 --- a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs +++ b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs @@ -129,7 +129,7 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp : memberIdentity.Name is not null ? entityType.FindProperty(memberIdentity.Name) : null; - if (property?.GetTypeMapping().ElementTypeMapping != null) + if (property?.IsPrimitiveCollection == true) { return new PrimitiveCollectionReference(root, property); } diff --git a/src/EFCore/Storage/CoreTypeMapping.cs b/src/EFCore/Storage/CoreTypeMapping.cs index f84fa623bab..8553c639604 100644 --- a/src/EFCore/Storage/CoreTypeMapping.cs +++ b/src/EFCore/Storage/CoreTypeMapping.cs @@ -111,12 +111,14 @@ public CoreTypeMappingParameters( /// /// The converter. /// The comparer. + /// The key comparer. /// The element mapping, or for non-collection mappings. /// The JSON reader/writer, or to leave unchanged. /// The new parameter object. public CoreTypeMappingParameters WithComposedConverter( ValueConverter? converter, ValueComparer? comparer, + ValueComparer? keyComparer, CoreTypeMapping? elementMapping, JsonValueReaderWriter? jsonValueReaderWriter) { @@ -126,7 +128,7 @@ public CoreTypeMappingParameters WithComposedConverter( ClrType, converter ?? Converter, comparer ?? Comparer, - KeyComparer, + keyComparer?? KeyComparer, ProviderValueComparer, ValueGeneratorFactory, elementMapping ?? ElementTypeMapping, @@ -266,12 +268,14 @@ public virtual ValueComparer ProviderValueComparer /// /// The converter to use. /// The comparer to use, or for to keep the default. + /// The comparer to use when the value is a key, or for to keep the default. /// The element mapping, or for non-collection mappings. /// The JSON reader/writer, or to leave unchanged. /// A new type mapping - public abstract CoreTypeMapping Clone( + public abstract CoreTypeMapping WithComposedConverter( ValueConverter? converter, ValueComparer? comparer = null, + ValueComparer? keyComparer = null, CoreTypeMapping? elementMapping = null, JsonValueReaderWriter? jsonValueReaderWriter = null); diff --git a/src/EFCore/Storage/ExecutionStrategy.cs b/src/EFCore/Storage/ExecutionStrategy.cs index 038ad521bd2..0aed4c64ed4 100644 --- a/src/EFCore/Storage/ExecutionStrategy.cs +++ b/src/EFCore/Storage/ExecutionStrategy.cs @@ -123,7 +123,7 @@ protected ExecutionStrategy( /// See Connection resiliency and database retries /// for more information and examples. /// - protected virtual int MaxRetryCount { get; } + public virtual int MaxRetryCount { get; } /// /// The maximum delay between retries. @@ -132,7 +132,7 @@ protected ExecutionStrategy( /// See Connection resiliency and database retries /// for more information and examples. /// - protected virtual TimeSpan MaxRetryDelay { get; } + public virtual TimeSpan MaxRetryDelay { get; } /// /// Dependencies for this service. diff --git a/src/EFCore/Storage/Json/JsonWarningEnumReaderWriter.cs b/src/EFCore/Storage/Json/JsonWarningEnumReaderWriter.cs index 3e38ae9d11c..aed1c3e9cc9 100644 --- a/src/EFCore/Storage/Json/JsonWarningEnumReaderWriter.cs +++ b/src/EFCore/Storage/Json/JsonWarningEnumReaderWriter.cs @@ -29,7 +29,7 @@ public override TEnum FromJsonTyped(ref Utf8JsonReaderManager manager, object? e { if (manager.CurrentReader.TokenType == JsonTokenType.String) { - if (manager.QueryLogger?.Options.ShouldWarnForEnumType(typeof(TEnum)) == true) + if (manager.QueryLogger?.Options.ShouldWarnForStringEnumValueInJson(typeof(TEnum)) == true) { manager.QueryLogger.StringEnumValueInJson(typeof(TEnum)); } diff --git a/src/EFCore/Storage/TypeMappingInfo.cs b/src/EFCore/Storage/TypeMappingInfo.cs index 7407af2d4b5..5cec9557220 100644 --- a/src/EFCore/Storage/TypeMappingInfo.cs +++ b/src/EFCore/Storage/TypeMappingInfo.cs @@ -181,6 +181,7 @@ public TypeMappingInfo( var mappingHints = customConverter?.MappingHints; var property = principals[0]; + ElementTypeMapping = property.GetElementType()?.FindTypeMapping(); IsKeyOrIndex = property.IsKey() || property.IsForeignKey() || property.IsIndex(); Size = fallbackSize ?? mappingHints?.Size; IsUnicode = fallbackUnicode ?? mappingHints?.IsUnicode; @@ -195,17 +196,19 @@ public TypeMappingInfo( /// Creates a new instance of . /// /// The property or field for which mapping is needed. + /// The type mapping for elements, if known. /// Specifies Unicode or ANSI mapping, or for default. /// Specifies a size for the mapping, or for default. /// Specifies a precision for the mapping, or for default. /// Specifies a scale for the mapping, or for default. public TypeMappingInfo( MemberInfo member, + CoreTypeMapping? elementTypeMapping = null, bool? unicode = null, int? size = null, int? precision = null, int? scale = null) - : this(member.GetMemberType()) + : this(member.GetMemberType(), elementTypeMapping) { IsUnicode = unicode; Size = size; @@ -217,6 +220,7 @@ public TypeMappingInfo( /// Creates a new instance of . /// /// The CLR type in the model for which mapping is needed. + /// The type mapping for elements, if known. /// If , then a special mapping for a key or index may be returned. /// Specifies Unicode or ANSI mapping, or for default. /// Specifies a size for the mapping, or for default. @@ -225,6 +229,7 @@ public TypeMappingInfo( /// Specifies a scale for the mapping, or for default. public TypeMappingInfo( Type? type = null, + CoreTypeMapping? elementTypeMapping = null, bool keyOrIndex = false, bool? unicode = null, int? size = null, @@ -233,6 +238,7 @@ public TypeMappingInfo( int? scale = null) { ClrType = type?.UnwrapNullableType(); + ElementTypeMapping = elementTypeMapping; IsKeyOrIndex = keyOrIndex; Size = size; @@ -272,8 +278,14 @@ public TypeMappingInfo( ClrType = converter.ProviderClrType.UnwrapNullableType(); JsonValueReaderWriter = source.JsonValueReaderWriter; + ElementTypeMapping = source.ElementTypeMapping; } + /// + /// The element type mapping of the mapping, if any. + /// + public CoreTypeMapping? ElementTypeMapping { get; init; } + /// /// Returns a new with the given converter applied. /// diff --git a/src/EFCore/Storage/TypeMappingSource.cs b/src/EFCore/Storage/TypeMappingSource.cs index 407f991bc5c..5ba3d7e2d7d 100644 --- a/src/EFCore/Storage/TypeMappingSource.cs +++ b/src/EFCore/Storage/TypeMappingSource.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Concurrent; +using Microsoft.EntityFrameworkCore.ChangeTracking.Internal; namespace Microsoft.EntityFrameworkCore.Storage; @@ -41,7 +42,7 @@ protected TypeMappingSource(TypeMappingSourceDependencies dependencies) } private CoreTypeMapping? FindMappingWithConversion( - in TypeMappingInfo mappingInfo, + TypeMappingInfo mappingInfo, IReadOnlyList? principals) { Type? providerClrType = null; @@ -70,15 +71,19 @@ protected TypeMappingSource(TypeMappingSourceDependencies dependencies) } } - var element = principal.GetElementType(); - if (element != null) + if (elementMapping == null) { - elementMapping = FindMapping(element); + var element = principal.GetElementType(); + if (element != null) + { + elementMapping = FindMapping(element); + mappingInfo = mappingInfo with { ElementTypeMapping = elementMapping }; + } } } } - var resolvedMapping = FindMappingWithConversion(mappingInfo, providerClrType, customConverter, elementMapping); + var resolvedMapping = FindMappingWithConversion(mappingInfo, providerClrType, customConverter); ValidateMapping(resolvedMapping, principals?[0]); @@ -88,26 +93,23 @@ protected TypeMappingSource(TypeMappingSourceDependencies dependencies) private CoreTypeMapping? FindMappingWithConversion( TypeMappingInfo mappingInfo, Type? providerClrType, - ValueConverter? customConverter, - CoreTypeMapping? elementMapping) + ValueConverter? customConverter) => _explicitMappings.GetOrAdd( - (mappingInfo, providerClrType, customConverter, elementMapping), + (mappingInfo, providerClrType, customConverter, mappingInfo.ElementTypeMapping), static (k, self) => { var (mappingInfo, providerClrType, customConverter, elementMapping) = k; var sourceType = mappingInfo.ClrType; - CoreTypeMapping? mapping = null; + var mapping = providerClrType == null + || providerClrType == mappingInfo.ClrType + ? self.FindMapping(mappingInfo) + : null; - if (elementMapping == null - || customConverter != null) + if (mapping == null) { - mapping = providerClrType == null - || providerClrType == mappingInfo.ClrType - ? self.FindMapping(mappingInfo) - : null; - - if (mapping == null) + if (elementMapping == null + || customConverter != null) { if (sourceType != null) { @@ -129,7 +131,7 @@ protected TypeMappingSource(TypeMappingSourceDependencies dependencies) if (mapping != null) { - mapping = mapping.Clone( + mapping = mapping.WithComposedConverter( secondConverterInfo.Create(), jsonValueReaderWriter: mappingInfoUsed.JsonValueReaderWriter); break; @@ -139,7 +141,7 @@ protected TypeMappingSource(TypeMappingSourceDependencies dependencies) if (mapping != null) { - mapping = mapping.Clone( + mapping = mapping.WithComposedConverter( converterInfo.Create(), jsonValueReaderWriter: mappingInfo.JsonValueReaderWriter); break; @@ -149,16 +151,16 @@ protected TypeMappingSource(TypeMappingSourceDependencies dependencies) mapping ??= self.FindCollectionMapping(mappingInfo, sourceType, providerClrType, elementMapping); } } - } - else if (sourceType != null) - { - mapping = self.FindCollectionMapping(mappingInfo, sourceType, providerClrType, elementMapping); + else if (sourceType != null) + { + mapping = self.FindCollectionMapping(mappingInfo, sourceType, providerClrType, elementMapping); + } } if (mapping != null && customConverter != null) { - mapping = mapping.Clone( + mapping = mapping.WithComposedConverter( customConverter, jsonValueReaderWriter: mappingInfo.JsonValueReaderWriter); } @@ -175,32 +177,27 @@ protected TypeMappingSource(TypeMappingSourceDependencies dependencies) /// The provider type. /// The element mapping, if known. /// The type mapping, or if none was found. + [EntityFrameworkInternal] protected virtual CoreTypeMapping? FindCollectionMapping( TypeMappingInfo info, Type modelType, Type? providerType, CoreTypeMapping? elementMapping) - { - var elementType = modelType.TryGetElementType(typeof(IEnumerable<>))!; - - return TryFindJsonCollectionMapping( - info, modelType, providerType, ref elementMapping, out var collectionReaderWriter) + => TryFindJsonCollectionMapping( + info, modelType, providerType, ref elementMapping, out var comparer, out var collectionReaderWriter) ? FindMapping( info.WithConverter( // Note that the converter info is only used temporarily here and never creates an instance. new ValueConverterInfo(modelType, typeof(string), _ => null!)))! - .Clone( + .WithComposedConverter( (ValueConverter)Activator.CreateInstance( - typeof(CollectionToJsonStringConverter<>).MakeGenericType(elementType), collectionReaderWriter!)!, - (ValueComparer?)Activator.CreateInstance( - elementType.IsNullableValueType() - ? typeof(NullableValueTypeListComparer<>).MakeGenericType(elementType.UnwrapNullableType()) - : typeof(ListComparer<>).MakeGenericType(elementMapping!.Comparer.Type), - elementMapping!.Comparer), + typeof(CollectionToJsonStringConverter<>).MakeGenericType(modelType.TryGetElementType(typeof(IEnumerable<>))!), + collectionReaderWriter!)!, + comparer, + comparer, elementMapping, collectionReaderWriter) : null; - } /// /// Finds the type mapping for a given . @@ -232,7 +229,7 @@ protected TypeMappingSource(TypeMappingSourceDependencies dependencies) var resolvedMapping = FindMappingWithConversion( new TypeMappingInfo( elementType, elementType.IsUnicode(), elementType.GetMaxLength(), elementType.GetPrecision(), elementType.GetScale()), - providerClrType, customConverter, null); + providerClrType, customConverter); ValidateMapping(resolvedMapping, null); @@ -277,7 +274,7 @@ protected TypeMappingSource(TypeMappingSourceDependencies dependencies) ValueConverter? customConverter = null; if (typeConfiguration == null) { - mappingInfo = new TypeMappingInfo(type); + mappingInfo = new TypeMappingInfo(type, elementMapping); } else { @@ -285,13 +282,14 @@ protected TypeMappingSource(TypeMappingSourceDependencies dependencies) customConverter = typeConfiguration.GetValueConverter(); mappingInfo = new TypeMappingInfo( customConverter?.ProviderClrType ?? type, + elementMapping, unicode: typeConfiguration.IsUnicode(), size: typeConfiguration.GetMaxLength(), precision: typeConfiguration.GetPrecision(), scale: typeConfiguration.GetScale()); } - return FindMappingWithConversion(mappingInfo, providerClrType, customConverter, elementMapping); + return FindMappingWithConversion(mappingInfo, providerClrType, customConverter); } /// diff --git a/src/EFCore/Storage/TypeMappingSourceBase.cs b/src/EFCore/Storage/TypeMappingSourceBase.cs index 0d4632d6507..c4ec9fba4e3 100644 --- a/src/EFCore/Storage/TypeMappingSourceBase.cs +++ b/src/EFCore/Storage/TypeMappingSourceBase.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using Microsoft.EntityFrameworkCore.ChangeTracking.Internal; using Microsoft.EntityFrameworkCore.Storage.Json; namespace Microsoft.EntityFrameworkCore.Storage; @@ -143,6 +144,7 @@ protected virtual void ValidateMapping( /// The model CLR type. /// The provider CLR type. /// The type mapping for elements of the collection. + /// The element comparer. /// The reader/writer for the collection. /// if a collection mapping was found; otherwise. protected virtual bool TryFindJsonCollectionMapping( @@ -150,6 +152,7 @@ protected virtual bool TryFindJsonCollectionMapping( Type modelClrType, Type? providerClrType, ref CoreTypeMapping? elementMapping, + out ValueComparer? elementComparer, out JsonValueReaderWriter? collectionReaderWriter) { if ((providerClrType == null || providerClrType == typeof(string)) @@ -163,7 +166,8 @@ protected virtual bool TryFindJsonCollectionMapping( { var elementReader = elementMapping.JsonValueReaderWriter!; - if (!elementReader.ValueType.IsAssignableFrom(elementType.UnwrapNullableType())) + if (elementReader.ValueType.IsNullableValueType() + || !elementReader.ValueType.IsAssignableFrom(elementType.UnwrapNullableType())) { elementReader = (JsonValueReaderWriter)Activator.CreateInstance( typeof(JsonCastValueReaderWriter<>).MakeGenericType(elementType.UnwrapNullableType()), elementReader)!; @@ -179,6 +183,14 @@ protected virtual bool TryFindJsonCollectionMapping( .MakeGenericType(modelClrType, typeToInstantiate, elementType.UnwrapNullableType()), elementReader); + elementComparer = (ValueComparer?)Activator.CreateInstance( + elementType.IsNullableValueType() + ? typeof(NullableValueTypeListComparer<>).MakeGenericType(elementType.UnwrapNullableType()) + : elementMapping.Comparer.Type.IsAssignableFrom(elementType) + ? typeof(ListComparer<>).MakeGenericType(elementType) + : typeof(ObjectListComparer<>).MakeGenericType(elementType), + elementMapping.Comparer.ToNullableComparer(elementType)!); + return true; Type FindTypeToInstantiate() @@ -205,6 +217,7 @@ Type FindTypeToInstantiate() } elementMapping = null; + elementComparer = null; collectionReaderWriter = null; return false; } diff --git a/src/EFCore/Storage/ValueConversion/CollectionToJsonStringConverter.cs b/src/EFCore/Storage/ValueConversion/CollectionToJsonStringConverter.cs index 445873a834a..7742f1aa29e 100644 --- a/src/EFCore/Storage/ValueConversion/CollectionToJsonStringConverter.cs +++ b/src/EFCore/Storage/ValueConversion/CollectionToJsonStringConverter.cs @@ -22,5 +22,11 @@ public CollectionToJsonStringConverter(JsonValueReaderWriter collectionJsonReade v => collectionJsonReaderWriter.ToJsonString(v), v => (IEnumerable)collectionJsonReaderWriter.FromJsonString(v, null)) { + JsonReaderWriter = collectionJsonReaderWriter; } + + /// + /// The reader/writer to use. + /// + public virtual JsonValueReaderWriter JsonReaderWriter { get; } } diff --git a/src/EFCore/Storage/ValueConversion/ConverterMappingHints.cs b/src/EFCore/Storage/ValueConversion/ConverterMappingHints.cs index 9076f4c0eb3..829ddbda7a0 100644 --- a/src/EFCore/Storage/ValueConversion/ConverterMappingHints.cs +++ b/src/EFCore/Storage/ValueConversion/ConverterMappingHints.cs @@ -59,7 +59,7 @@ public virtual ConverterMappingHints With(ConverterMappingHints? hints) #pragma warning disable CS0612 // Type or member is obsolete hints.ValueGeneratorFactory ?? ValueGeneratorFactory) #pragma warning restore CS0612 // Type or member is obsolete - : hints.Override(this); + : hints.OverrideWith(this); /// /// Adds hints from the given object to this one. Hints that are already specified are overridden. @@ -69,7 +69,7 @@ public virtual ConverterMappingHints With(ConverterMappingHints? hints) /// /// The hints to add. /// The combined hints. - public virtual ConverterMappingHints Override(ConverterMappingHints? hints) + public virtual ConverterMappingHints OverrideWith(ConverterMappingHints? hints) => hints == null ? this : GetType().IsAssignableFrom(hints.GetType()) diff --git a/src/EFCore/Update/UpdateEntryExtensions.cs b/src/EFCore/Update/UpdateEntryExtensions.cs index 9e06e370a2d..99dcb989903 100644 --- a/src/EFCore/Update/UpdateEntryExtensions.cs +++ b/src/EFCore/Update/UpdateEntryExtensions.cs @@ -25,25 +25,6 @@ public static class UpdateEntryExtensions /// The property to get the value for. /// The value for the property. public static object? GetCurrentProviderValue(this IUpdateEntry updateEntry, IProperty property) - => GetCurrentProviderValue((IInternalEntry)updateEntry, property); - - /// - /// Gets the original value that was assigned to the property and converts it to the provider-expected value. - /// - /// The entry. - /// The property to get the value for. - /// The value for the property. - public static object? GetOriginalProviderValue(this IUpdateEntry updateEntry, IProperty property) - => GetOriginalProviderValue((IInternalEntry)updateEntry, property); - - /// - /// 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. - /// - [EntityFrameworkInternal] - public static object? GetCurrentProviderValue(this IInternalEntry updateEntry, IProperty property) { var value = updateEntry.GetCurrentValue(property); var typeMapping = property.GetTypeMapping(); @@ -61,13 +42,12 @@ public static class UpdateEntryExtensions } /// - /// 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. + /// Gets the original value that was assigned to the property and converts it to the provider-expected value. /// - [EntityFrameworkInternal] - public static object? GetOriginalProviderValue(this IInternalEntry updateEntry, IProperty property) + /// The entry. + /// The property to get the value for. + /// The value for the property. + public static object? GetOriginalProviderValue(this IUpdateEntry updateEntry, IProperty property) { var value = updateEntry.GetOriginalValue(property); var typeMapping = property.GetTypeMapping(); @@ -324,31 +304,6 @@ void AppendRelatedKey(IEntityType targetType, object value) public static string BuildCurrentValuesString( this IUpdateEntry entry, IEnumerable properties) - => BuildCurrentValuesString((IInternalEntry)entry, properties); - - /// - /// Creates a formatted string representation of the given properties and their original - /// values such as is useful when throwing exceptions about keys, indexes, etc. that use - /// the properties. - /// - /// The entry from which values will be obtained. - /// The properties to format. - /// The string representation. - public static string BuildOriginalValuesString( - this IUpdateEntry entry, - IEnumerable properties) - => BuildOriginalValuesString((IInternalEntry)entry, properties); - - /// - /// 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. - /// - [EntityFrameworkInternal] - public static string BuildCurrentValuesString( - this IInternalEntry entry, - IEnumerable properties) => "{" + string.Join( ", ", properties.Select( @@ -364,14 +319,15 @@ public static string BuildCurrentValuesString( + "}"; /// - /// 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. + /// Creates a formatted string representation of the given properties and their original + /// values such as is useful when throwing exceptions about keys, indexes, etc. that use + /// the properties. /// - [EntityFrameworkInternal] + /// The entry from which values will be obtained. + /// The properties to format. + /// The string representation. public static string BuildOriginalValuesString( - this IInternalEntry entry, + this IUpdateEntry entry, IEnumerable properties) => "{" + string.Join( diff --git a/src/Microsoft.Data.Sqlite.Core/Microsoft.Data.Sqlite.Core.csproj b/src/Microsoft.Data.Sqlite.Core/Microsoft.Data.Sqlite.Core.csproj index a92c09d9c57..525245c8847 100644 --- a/src/Microsoft.Data.Sqlite.Core/Microsoft.Data.Sqlite.Core.csproj +++ b/src/Microsoft.Data.Sqlite.Core/Microsoft.Data.Sqlite.Core.csproj @@ -40,7 +40,7 @@ Microsoft.Data.Sqlite.SqliteTransaction - + diff --git a/src/Microsoft.Data.Sqlite.Core/SqliteConnection.cs b/src/Microsoft.Data.Sqlite.Core/SqliteConnection.cs index 228845c0d18..bd368b2370a 100644 --- a/src/Microsoft.Data.Sqlite.Core/SqliteConnection.cs +++ b/src/Microsoft.Data.Sqlite.Core/SqliteConnection.cs @@ -7,7 +7,9 @@ using System.Data.Common; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.IO; using System.Reflection; +using System.Runtime.InteropServices; using Microsoft.Data.Sqlite.Properties; using SQLitePCL; using static SQLitePCL.raw; @@ -52,38 +54,50 @@ static SqliteConnection() ?.GetRuntimeMethod("Init", Type.EmptyTypes) ?.Invoke(null, null); - var appDataType = Type.GetType("Windows.Storage.ApplicationData, Windows, ContentType=WindowsRuntime") - ?? Type.GetType("Windows.Storage.ApplicationData, Microsoft.Windows.SDK.NET"); - - var storageFolderType = Type.GetType("Windows.Storage.StorageFolder, Windows, ContentType=WindowsRuntime") - ?? Type.GetType("Windows.Storage.StorageFolder, Microsoft.Windows.SDK.NET"); - - object? currentAppData = null; - try + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - currentAppData = appDataType?.GetRuntimeProperty("Current")?.GetValue(null); - } - catch (TargetInvocationException) - { - // Ignore "The process has no package identity." - } + Type? appDataType = null; + Type? storageFolderType = null; + try + { + appDataType = Type.GetType("Windows.Storage.ApplicationData, Windows, ContentType=WindowsRuntime") + ?? Type.GetType("Windows.Storage.ApplicationData, Microsoft.Windows.SDK.NET"); - if (currentAppData != null) - { - var localFolder = appDataType?.GetRuntimeProperty("LocalFolder")?.GetValue(currentAppData); - var localFolderPath = (string?)storageFolderType?.GetRuntimeProperty("Path")?.GetValue(localFolder); - if (localFolderPath != null) + storageFolderType = Type.GetType("Windows.Storage.StorageFolder, Windows, ContentType=WindowsRuntime") + ?? Type.GetType("Windows.Storage.StorageFolder, Microsoft.Windows.SDK.NET"); + } + catch (FileLoadException) { - var rc = sqlite3_win32_set_directory(SQLITE_WIN32_DATA_DIRECTORY_TYPE, localFolderPath); - Debug.Assert(rc == SQLITE_OK); + // Ignore "Could not load assembly." } - var tempFolder = appDataType?.GetRuntimeProperty("TemporaryFolder")?.GetValue(currentAppData); - var tempFolderPath = (string?)storageFolderType?.GetRuntimeProperty("Path")?.GetValue(tempFolder); - if (tempFolderPath != null) + object? currentAppData = null; + try + { + currentAppData = appDataType?.GetRuntimeProperty("Current")?.GetValue(null); + } + catch (TargetInvocationException) { - var rc = sqlite3_win32_set_directory(SQLITE_WIN32_TEMP_DIRECTORY_TYPE, tempFolderPath); - Debug.Assert(rc == SQLITE_OK); + // Ignore "The process has no package identity." + } + + if (currentAppData != null) + { + var localFolder = appDataType?.GetRuntimeProperty("LocalFolder")?.GetValue(currentAppData); + var localFolderPath = (string?)storageFolderType?.GetRuntimeProperty("Path")?.GetValue(localFolder); + if (localFolderPath != null) + { + var rc = sqlite3_win32_set_directory(SQLITE_WIN32_DATA_DIRECTORY_TYPE, localFolderPath); + Debug.Assert(rc == SQLITE_OK); + } + + var tempFolder = appDataType?.GetRuntimeProperty("TemporaryFolder")?.GetValue(currentAppData); + var tempFolderPath = (string?)storageFolderType?.GetRuntimeProperty("Path")?.GetValue(tempFolder); + if (tempFolderPath != null) + { + var rc = sqlite3_win32_set_directory(SQLITE_WIN32_TEMP_DIRECTORY_TYPE, tempFolderPath); + Debug.Assert(rc == SQLITE_OK); + } } } } @@ -821,22 +835,24 @@ private void CreateAggregateCore( delegate_function_aggregate_step? func_step = null; if (func != null) { - func_step = (ctx, user_data, args) => + func_step = static (ctx, user_data, args) => { - var context = (AggregateContext)user_data; + var definition = (AggregateDefinition)user_data; + ctx.state ??= new AggregateContext(definition.Seed); + + var context = (AggregateContext)ctx.state; if (context.Exception != null) { return; } // TODO: Avoid allocation when niladic - var reader = new SqliteParameterReader(name, args); + var reader = new SqliteParameterReader(definition.Name, args); try { - // TODO: Avoid closure by passing func via user_data // NB: No need to set ctx.state since we just mutate the instance - context.Accumulate = func(context.Accumulate, reader); + context.Accumulate = definition.Func!(context.Accumulate, reader); } catch (Exception ex) { @@ -848,16 +864,18 @@ private void CreateAggregateCore( delegate_function_aggregate_final? func_final = null; if (resultSelector != null) { - func_final = (ctx, user_data) => + func_final = static (ctx, user_data) => { - var context = (AggregateContext)user_data; + var definition = (AggregateDefinition)user_data; + ctx.state ??= new AggregateContext(definition.Seed); + + var context = (AggregateContext)ctx.state; if (context.Exception == null) { try { - // TODO: Avoid closure by passing resultSelector via user_data - var result = resultSelector(context.Accumulate); + var result = definition.ResultSelector!(context.Accumulate); new SqliteResultBinder(ctx, result).Bind(); } @@ -881,7 +899,7 @@ private void CreateAggregateCore( } var flags = isDeterministic ? SQLITE_DETERMINISTIC : 0; - var state = new AggregateContext(seed); + var state = new AggregateDefinition(name, seed, func, resultSelector); if (State == ConnectionState.Open) { @@ -915,6 +933,22 @@ private void CreateAggregateCore( return values; } + private sealed class AggregateDefinition + { + public AggregateDefinition(string name, TAccumulate seed, Func? func, Func? resultSelector) + { + Name = name; + Seed = seed; + Func = func; + ResultSelector = resultSelector; + } + + public string Name { get; } + public TAccumulate Seed { get; } + public Func? Func { get; } + public Func? ResultSelector { get; } + } + private sealed class AggregateContext { public AggregateContext(T seed) diff --git a/src/Microsoft.Data.Sqlite/Microsoft.Data.Sqlite.csproj b/src/Microsoft.Data.Sqlite/Microsoft.Data.Sqlite.csproj index 1b00ec7bae8..34cd41f43ec 100644 --- a/src/Microsoft.Data.Sqlite/Microsoft.Data.Sqlite.csproj +++ b/src/Microsoft.Data.Sqlite/Microsoft.Data.Sqlite.csproj @@ -24,7 +24,7 @@ Microsoft.Data.Sqlite.SqliteTransaction - + diff --git a/src/ef/Commands/MigrationsHasPendingModelChangesCommand.cs b/src/ef/Commands/MigrationsHasPendingModelChangesCommand.cs index bdbd2db8b6a..f552a82ce37 100644 --- a/src/ef/Commands/MigrationsHasPendingModelChangesCommand.cs +++ b/src/ef/Commands/MigrationsHasPendingModelChangesCommand.cs @@ -1,12 +1,20 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using Microsoft.EntityFrameworkCore.Internal; +using Microsoft.EntityFrameworkCore.Tools.Properties; + namespace Microsoft.EntityFrameworkCore.Tools.Commands; internal partial class MigrationsHasPendingModelChangesCommand { protected override int Execute(string[] args) { + if (new SemanticVersionComparer().Compare(EFCoreVersion, "8.0.0") < 0) + { + throw new CommandException(Resources.VersionRequired("8.0.0")); + } + using var executor = CreateExecutor(args); executor.HasPendingModelChanges(Context!.Value()); diff --git a/test/EFCore.Cosmos.FunctionalTests/ConfigPatternsCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/ConfigPatternsCosmosTest.cs index 95601921d87..daaa5d8b6fb 100644 --- a/test/EFCore.Cosmos.FunctionalTests/ConfigPatternsCosmosTest.cs +++ b/test/EFCore.Cosmos.FunctionalTests/ConfigPatternsCosmosTest.cs @@ -88,7 +88,7 @@ public async Task Should_throw_if_specified_region_is_wrong() exception.Message); } - [ConditionalFact(Skip = "Issue #runtime/issues/89118")] + [ConditionalFact] public async Task Should_not_throw_if_specified_connection_mode_is_right() { var connectionMode = ConnectionMode.Direct; diff --git a/test/EFCore.Cosmos.FunctionalTests/EndToEndCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/EndToEndCosmosTest.cs index b2b9d76b638..3f26733c3b2 100644 --- a/test/EFCore.Cosmos.FunctionalTests/EndToEndCosmosTest.cs +++ b/test/EFCore.Cosmos.FunctionalTests/EndToEndCosmosTest.cs @@ -1256,7 +1256,7 @@ public async Task Can_read_with_find_with_PK_partition_key() { var options = Fixture.CreateOptions(); - var customer = new Customer { Id = 42, Name = "Theon" }; + var customer = new CustomerGuid { Id = Guid.NewGuid(), Name = "Theon" }; await using (var context = new PartitionKeyContextPrimaryKey(options)) { @@ -1269,11 +1269,11 @@ public async Task Can_read_with_find_with_PK_partition_key() await using (var context = new PartitionKeyContextPrimaryKey(options)) { - var customerFromStore = context.Set().Find(42); + var customerFromStore = context.Set().Find(customer.Id); - Assert.Equal(42, customerFromStore.Id); + Assert.Equal(customer.Id, customerFromStore.Id); Assert.Equal("Theon", customerFromStore.Name); - AssertSql(context, @"ReadItem(42, 42)"); + AssertSql(context, @$"ReadItem({customer.Id}, {customer.Id})"); } } @@ -1400,11 +1400,10 @@ public PartitionKeyContextPrimaryKey(DbContextOptions dbContextOptions) } protected override void OnModelCreating(ModelBuilder modelBuilder) - => modelBuilder.Entity( + => modelBuilder.Entity( cb => { - cb.HasNoDiscriminator(); - cb.Property(c => c.Id).HasConversion(); + cb.Property(c => c.Id).ToJsonProperty("id"); cb.HasPartitionKey(c => c.Id); }); } diff --git a/test/EFCore.Cosmos.FunctionalTests/MaterializationInterceptionCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/MaterializationInterceptionCosmosTest.cs index b98e8456eea..9ef9d4e4be1 100644 --- a/test/EFCore.Cosmos.FunctionalTests/MaterializationInterceptionCosmosTest.cs +++ b/test/EFCore.Cosmos.FunctionalTests/MaterializationInterceptionCosmosTest.cs @@ -11,6 +11,9 @@ public MaterializationInterceptionCosmosTest(MaterializationInterceptionCosmosFi { } + public override Task Intercept_query_materialization_with_owned_types_projecting_collection(bool async) + => Task.CompletedTask; + public class CosmosLibraryContext : LibraryContext { public CosmosLibraryContext(DbContextOptions options) diff --git a/test/EFCore.Cosmos.FunctionalTests/TestUtilities/CosmosTestStore.cs b/test/EFCore.Cosmos.FunctionalTests/TestUtilities/CosmosTestStore.cs index 6544f0744e3..d738654b5d6 100644 --- a/test/EFCore.Cosmos.FunctionalTests/TestUtilities/CosmosTestStore.cs +++ b/test/EFCore.Cosmos.FunctionalTests/TestUtilities/CosmosTestStore.cs @@ -715,6 +715,9 @@ public IEnumerable GetSnapshottableMembers() public IEnumerable GetFlattenedProperties() => throw new NotImplementedException(); + public IEnumerable GetFlattenedComplexProperties() + => throw new NotImplementedException(); + public IEnumerable GetFlattenedDeclaredProperties() => throw new NotImplementedException(); diff --git a/test/EFCore.Cosmos.Tests/Extensions/CosmosDbContextOptionsExtensionsTests.cs b/test/EFCore.Cosmos.Tests/Extensions/CosmosDbContextOptionsExtensionsTests.cs index 963951c2409..708056c3270 100644 --- a/test/EFCore.Cosmos.Tests/Extensions/CosmosDbContextOptionsExtensionsTests.cs +++ b/test/EFCore.Cosmos.Tests/Extensions/CosmosDbContextOptionsExtensionsTests.cs @@ -71,6 +71,8 @@ public void Can_create_options_with_valid_values() Test(o => o.Region(Regions.EastAsia), o => Assert.Equal(Regions.EastAsia, o.Region)); // The region will be validated by the Cosmos SDK, because the region list is not constant Test(o => o.Region("FakeRegion"), o => Assert.Equal("FakeRegion", o.Region)); + Test(o => o.PreferredRegions(new[] { Regions.AustraliaCentral, Regions.EastAsia }), + o => Assert.Equal(new[] { Regions.AustraliaCentral, Regions.EastAsia }, o.PreferredRegions)); Test(o => o.ConnectionMode(ConnectionMode.Direct), o => Assert.Equal(ConnectionMode.Direct, o.ConnectionMode)); Test(o => o.GatewayModeMaxConnectionLimit(3), o => Assert.Equal(3, o.GatewayModeMaxConnectionLimit)); Test(o => o.MaxRequestsPerTcpConnection(3), o => Assert.Equal(3, o.MaxRequestsPerTcpConnection)); diff --git a/test/EFCore.Cosmos.Tests/ModelBuilding/CosmosModelBuilderGenericTest.cs b/test/EFCore.Cosmos.Tests/ModelBuilding/CosmosModelBuilderGenericTest.cs index 4b04a27d661..1f32b625230 100644 --- a/test/EFCore.Cosmos.Tests/ModelBuilding/CosmosModelBuilderGenericTest.cs +++ b/test/EFCore.Cosmos.Tests/ModelBuilding/CosmosModelBuilderGenericTest.cs @@ -333,6 +333,35 @@ protected override TestModelBuilder CreateModelBuilder(Action() + .Entity() + .ComplexProperty(e => e.Customer) + .HasTypeAnnotation("foo", "bar") + .HasPropertyAnnotation("foo2", "bar2") + .Ignore(c => c.Details) + .Ignore(c => c.Orders); + + var model = modelBuilder.FinalizeModel(); + var complexProperty = model.FindEntityType(typeof(ComplexProperties)).GetComplexProperties().Single(); + + Assert.Equal("bar", complexProperty.ComplexType["foo"]); + Assert.Equal("bar2", complexProperty["foo2"]); + Assert.Equal(typeof(Customer).Name, complexProperty.Name); + Assert.Equal( + @"Customer (Customer) Required + ComplexType: ComplexProperties.Customer#Customer + Properties: " + @" + AlternateKey (Guid) Required + Id (int) Required + Name (string) + Notes (List)", complexProperty.ToDebugString(), ignoreLineEndingDifferences: true); + } + public override void Properties_can_have_provider_type_set_for_type() { var modelBuilder = CreateModelBuilder(c => c.Properties().HaveConversion()); diff --git a/test/EFCore.Design.Tests/Design/Internal/CSharpHelperTest.cs b/test/EFCore.Design.Tests/Design/Internal/CSharpHelperTest.cs index 24ddf62a94b..4c29fc5b471 100644 --- a/test/EFCore.Design.Tests/Design/Internal/CSharpHelperTest.cs +++ b/test/EFCore.Design.Tests/Design/Internal/CSharpHelperTest.cs @@ -827,8 +827,7 @@ private static SqlServerTypeMappingSource CreateTypeMappingSource( params IRelationalTypeMappingSourcePlugin[] plugins) => new( TestServiceFactory.Instance.Create(), - new RelationalTypeMappingSourceDependencies(plugins), - new SqlServerSingletonOptions()); + new RelationalTypeMappingSourceDependencies(plugins)); private class TestTypeMappingPlugin : IRelationalTypeMappingSourcePlugin { diff --git a/test/EFCore.Design.Tests/EFCore.Design.Tests.csproj b/test/EFCore.Design.Tests/EFCore.Design.Tests.csproj index e594b843934..9029f099eb5 100644 --- a/test/EFCore.Design.Tests/EFCore.Design.Tests.csproj +++ b/test/EFCore.Design.Tests/EFCore.Design.Tests.csproj @@ -57,7 +57,7 @@ - + diff --git a/test/EFCore.Design.Tests/Extensions/MethodCallCodeFragmentExtensionsTest.cs b/test/EFCore.Design.Tests/Extensions/MethodCallCodeFragmentExtensionsTest.cs new file mode 100644 index 00000000000..87f6a27819b --- /dev/null +++ b/test/EFCore.Design.Tests/Extensions/MethodCallCodeFragmentExtensionsTest.cs @@ -0,0 +1,75 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Extensions +{ + public class MethodCallCodeFragmentExtensionsTest + { + [ConditionalFact] + public void GetRequiredUsings_works() + { + var methodCall = new MethodCallCodeFragment( + typeof(Namespace1.TestExtensions1) + .GetRuntimeMethod( + nameof(Namespace1.TestExtensions1.Extension1), + new[] + { + typeof(MethodCallCodeFragmentExtensionsTest), + typeof(Action) + }), + new NestedClosureCodeFragment( + "x", + new MethodCallCodeFragment( + typeof(Namespace2.TestExtensions2) + .GetRuntimeMethod( + nameof(Namespace2.TestExtensions2.Extension2), + new[] + { + typeof(MethodCallCodeFragmentExtensionsTest), + typeof(Namespace3.TestArgument) + }), + new Namespace3.TestArgument()))); + + + var usings = methodCall.GetRequiredUsings(); + + Assert.Equal( + new[] + { + "Microsoft.EntityFrameworkCore.Extensions.Namespace1", + "Microsoft.EntityFrameworkCore.Extensions.Namespace2", + "Microsoft.EntityFrameworkCore.Extensions.Namespace3" + }, + usings); + } + } + + namespace Namespace1 + { + internal static class TestExtensions1 + { + public static void Extension1( + this MethodCallCodeFragmentExtensionsTest extendedObject, + Action closure) + => throw new NotImplementedException(); + } + } + + namespace Namespace2 + { + internal static class TestExtensions2 + { + public static void Extension2( + this MethodCallCodeFragmentExtensionsTest extendedObject, + Namespace3.TestArgument argument) + => throw new NotImplementedException(); + } + } + + namespace Namespace3 + { + internal class TestArgument + { + } + } +} diff --git a/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationOperationGeneratorTest.cs b/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationOperationGeneratorTest.cs index ff59d0d87d8..765ff612e6a 100644 --- a/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationOperationGeneratorTest.cs +++ b/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationOperationGeneratorTest.cs @@ -20,8 +20,7 @@ public void Generate_separates_operations_by_a_blank_line() new CSharpHelper( new SqlServerTypeMappingSource( TestServiceFactory.Instance.Create(), - TestServiceFactory.Instance.Create(), - new SqlServerSingletonOptions())))); + TestServiceFactory.Instance.Create())))); var builder = new IndentedStringBuilder(); @@ -3160,8 +3159,7 @@ private void Test(T operation, string expectedCode, Action assert) new IRelationalTypeMappingSourcePlugin[] { new SqlServerNetTopologySuiteTypeMappingSourcePlugin(NtsGeometryServices.Instance) - }), - new SqlServerSingletonOptions())))); + }))))); var builder = new IndentedStringBuilder(); generator.Generate("mb", new[] { operation }, builder); diff --git a/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.cs b/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.cs index d798e071d2a..3153973338f 100644 --- a/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.cs +++ b/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.cs @@ -349,8 +349,7 @@ private static void MissingAnnotationCheck( { var sqlServerTypeMappingSource = new SqlServerTypeMappingSource( TestServiceFactory.Instance.Create(), - TestServiceFactory.Instance.Create(), - new SqlServerSingletonOptions()); + TestServiceFactory.Instance.Create()); var sqlServerAnnotationCodeGenerator = new SqlServerAnnotationCodeGenerator( new AnnotationCodeGeneratorDependencies(sqlServerTypeMappingSource)); @@ -452,8 +451,7 @@ public void Snapshot_with_enum_discriminator_uses_converted_values() { var sqlServerTypeMappingSource = new SqlServerTypeMappingSource( TestServiceFactory.Instance.Create(), - TestServiceFactory.Instance.Create(), - new SqlServerSingletonOptions()); + TestServiceFactory.Instance.Create()); var codeHelper = new CSharpHelper( sqlServerTypeMappingSource); @@ -510,8 +508,7 @@ private static void AssertConverter(ValueConverter valueConverter, string expect var sqlServerTypeMappingSource = new SqlServerTypeMappingSource( TestServiceFactory.Instance.Create(), - TestServiceFactory.Instance.Create(), - new SqlServerSingletonOptions()); + TestServiceFactory.Instance.Create()); var codeHelper = new CSharpHelper(sqlServerTypeMappingSource); diff --git a/test/EFCore.Design.Tests/Migrations/Design/MigrationScaffolderTest.cs b/test/EFCore.Design.Tests/Migrations/Design/MigrationScaffolderTest.cs index efc9abacced..18c50430198 100644 --- a/test/EFCore.Design.Tests/Migrations/Design/MigrationScaffolderTest.cs +++ b/test/EFCore.Design.Tests/Migrations/Design/MigrationScaffolderTest.cs @@ -58,8 +58,7 @@ private IMigrationsScaffolder CreateMigrationScaffolder() var idGenerator = new MigrationsIdGenerator(); var sqlServerTypeMappingSource = new SqlServerTypeMappingSource( TestServiceFactory.Instance.Create(), - TestServiceFactory.Instance.Create(), - new SqlServerSingletonOptions()); + TestServiceFactory.Instance.Create()); var sqlServerAnnotationCodeGenerator = new SqlServerAnnotationCodeGenerator( new AnnotationCodeGeneratorDependencies(sqlServerTypeMappingSource)); var code = new CSharpHelper(sqlServerTypeMappingSource); diff --git a/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs b/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs index 381b4adbb79..3a8caf1b3fe 100644 --- a/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs +++ b/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs @@ -7,7 +7,6 @@ using Microsoft.EntityFrameworkCore.Metadata.Internal; using Microsoft.EntityFrameworkCore.Migrations.Internal; using Microsoft.EntityFrameworkCore.SqlServer.Design.Internal; -using Microsoft.EntityFrameworkCore.SqlServer.Infrastructure.Internal; using Microsoft.EntityFrameworkCore.SqlServer.Metadata.Internal; using Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal; using NetTopologySuite; @@ -66,6 +65,12 @@ private class EntityWithManyProperties public MultiPolygon SpatialCMultiPolygon { get; set; } public Point SpatialCPoint { get; set; } public Polygon SpatialCPolygon { get; set; } + public int[] Int32Collection { get; set; } + public double[] DoubleCollection { get; set; } + public string[] StringCollection { get; set; } + public DateTime[] DateTimeCollection { get; set; } + public bool[] BoolCollection { get; set; } + public byte[][] BytesCollection { get; set; } } private enum Enum64 : long @@ -5452,8 +5457,11 @@ public virtual void Complex_properties_are_stored_in_snapshot() { b.ComplexProperty(eo => eo.EntityWithTwoProperties, eb => { + eb.IsRequired(); eb.Property(e => e.AlternateId).HasColumnOrder(1); - eb.ComplexProperty(e => e.EntityWithStringKey); + eb.ComplexProperty(e => e.EntityWithStringKey).IsRequired(); + eb.HasPropertyAnnotation("PropertyAnnotation", 1); + eb.HasTypeAnnotation("TypeAnnotation", 2); }); }); }, @@ -5470,6 +5478,8 @@ public virtual void Complex_properties_are_stored_in_snapshot() b.ComplexProperty>("EntityWithTwoProperties", "Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithOneProperty.EntityWithTwoProperties#EntityWithTwoProperties", b1 => { + b1.IsRequired(); + b1.Property("AlternateId") .HasColumnType("int") .HasColumnOrder(1); @@ -5479,9 +5489,15 @@ public virtual void Complex_properties_are_stored_in_snapshot() b1.ComplexProperty>("EntityWithStringKey", "Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithOneProperty.EntityWithTwoProperties#EntityWithTwoProperties.EntityWithStringKey#EntityWithStringKey", b2 => { + b2.IsRequired(); + b2.Property("Id") .HasColumnType("nvarchar(max)"); }); + + b1.HasPropertyAnnotation("PropertyAnnotation", 1); + + b1.HasTypeAnnotation("TypeAnnotation", 2); }); b.HasKey("Id"); @@ -5496,17 +5512,19 @@ public virtual void Complex_properties_are_stored_in_snapshot() var complexProperty = entityWithOneProperty.FindComplexProperty(nameof(EntityWithOneProperty.EntityWithTwoProperties)); Assert.False(complexProperty.IsCollection); - Assert.True(complexProperty.IsNullable); + Assert.False(complexProperty.IsNullable); var complexType = complexProperty.ComplexType; Assert.Equal("Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithOneProperty.EntityWithTwoProperties#EntityWithTwoProperties", complexType.Name); Assert.Equal("EntityWithOneProperty.EntityWithTwoProperties#EntityWithTwoProperties", complexType.DisplayName()); Assert.Equal(nameof(EntityWithOneProperty), complexType.GetTableName()); var alternateIdProperty = complexType.FindProperty(nameof(EntityWithTwoProperties.AlternateId)); Assert.Equal(1, alternateIdProperty.GetColumnOrder()); + Assert.Equal(1, complexProperty["PropertyAnnotation"]); + Assert.Equal(2, complexProperty.ComplexType["TypeAnnotation"]); var nestedComplexProperty = complexType.FindComplexProperty(nameof(EntityWithTwoProperties.EntityWithStringKey)); Assert.False(nestedComplexProperty.IsCollection); - Assert.True(nestedComplexProperty.IsNullable); + Assert.False(nestedComplexProperty.IsNullable); var nestedComplexType = nestedComplexProperty.ComplexType; Assert.Equal("Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithOneProperty.EntityWithTwoProperties#EntityWithTwoProperties.EntityWithStringKey#EntityWithStringKey", nestedComplexType.Name); Assert.Equal("EntityWithOneProperty.EntityWithTwoProperties#EntityWithTwoProperties.EntityWithStringKey#EntityWithStringKey", nestedComplexType.DisplayName()); @@ -6191,6 +6209,117 @@ public virtual void IndexAttribute_IncludeProperties_generated_without_fluent_ap Assert.Equal("Name", Assert.Single(index.GetIncludeProperties())); }); + [ConditionalFact] + public virtual void IndexAttribute_HasFillFactor_is_stored_in_snapshot() + => Test( + builder => builder.Entity( + x => + { + x.HasIndex(e => e.Id).HasFillFactor(29); + }), + AddBoilerPlate( + GetHeading() + +""" + modelBuilder.Entity("Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithStringProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("Id"); + + SqlServerIndexBuilderExtensions.HasFillFactor(b.HasIndex("Id"), 29); + + b.ToTable("EntityWithStringProperty", "DefaultSchema"); + }); +"""), + model => + { + var index = model.GetEntityTypes().First().GetIndexes().First(); + Assert.Equal(29, index.GetFillFactor()); + }); + + [ConditionalFact] + public virtual void IndexAttribute_UseDataCompression_is_stored_in_snapshot() + => Test( + builder => builder.Entity( + x => + { + x.HasIndex(e => e.Id).UseDataCompression(DataCompressionType.Row); + }), + AddBoilerPlate( + GetHeading() + +""" + modelBuilder.Entity("Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithStringProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("Id"); + + SqlServerIndexBuilderExtensions.UseDataCompression(b.HasIndex("Id"), DataCompressionType.Row); + + b.ToTable("EntityWithStringProperty", "DefaultSchema"); + }); +"""), + model => + { + var index = model.GetEntityTypes().First().GetIndexes().First(); + Assert.Equal(DataCompressionType.Row, index.GetDataCompression()); + }); + + [ConditionalFact] + public virtual void IndexAttribute_SortInTempDb_is_stored_in_snapshot() + => Test( + builder => builder.Entity( + x => + { + x.HasIndex(e => e.Id).SortInTempDb(true); + }), + AddBoilerPlate( + GetHeading() + +""" + modelBuilder.Entity("Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithStringProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("Id"); + + SqlServerIndexBuilderExtensions.SortInTempDb(b.HasIndex("Id"), true); + + b.ToTable("EntityWithStringProperty", "DefaultSchema"); + }); +"""), + model => + { + var index = model.GetEntityTypes().First().GetIndexes().First(); + Assert.True(index.GetSortInTempDb()); + }); + #endregion #region ForeignKey @@ -7220,7 +7349,13 @@ static List getAllProperties(IModel model) SpatialCMultiPoint = multiPoint, SpatialCMultiPolygon = multiPolygon, SpatialCPoint = point1, - SpatialCPolygon = polygon1 + SpatialCPolygon = polygon1, + Int32Collection = new[] { 1, 2, 3, 4 }, + DoubleCollection = new[] { 1.2, 3.4 }, + StringCollection = new[] { "AB", "CD" }, + DateTimeCollection = new[] { new DateTime(2023, 9, 7), new DateTime(2023, 11, 14) }, + BoolCollection = new[] { true, false }, + BytesCollection = new[] { new byte[] { 1, 2 }, new byte[] { 3, 4 } } }, new { @@ -7288,6 +7423,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + b.Property("BoolCollection") + .HasColumnType("nvarchar(max)"); + b.Property("Boolean") .HasColumnType("bit"); @@ -7297,6 +7435,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Bytes") .HasColumnType("varbinary(max)"); + b.Property("BytesCollection") + .HasColumnType("nvarchar(max)"); + b.Property("Character") .IsRequired() .HasColumnType("nvarchar(1)"); @@ -7304,6 +7445,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("DateTime") .HasColumnType("datetime2"); + b.Property("DateTimeCollection") + .HasColumnType("nvarchar(max)"); + b.Property("DateTimeOffset") .HasColumnType("datetimeoffset"); @@ -7313,6 +7457,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Double") .HasColumnType("float"); + b.Property("DoubleCollection") + .HasColumnType("nvarchar(max)"); + b.Property("Enum16") .HasColumnType("smallint"); @@ -7343,6 +7490,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Int32") .HasColumnType("int"); + b.Property("Int32Collection") + .HasColumnType("nvarchar(max)"); + b.Property("Int64") .HasColumnType("bigint"); @@ -7400,6 +7550,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("String") .HasColumnType("nvarchar(max)"); + b.Property("StringCollection") + .HasColumnType("nvarchar(max)"); + b.Property("TimeSpan") .HasColumnType("time"); @@ -7420,14 +7573,18 @@ protected override void BuildModel(ModelBuilder modelBuilder) new { Id = 42, + BoolCollection = "[true,false]", Boolean = true, Byte = (byte)55, Bytes = new byte[] { 44, 45 }, + BytesCollection = "[\"AQI=\",\"AwQ=\"]", Character = "9", DateTime = new DateTime(1973, 9, 3, 12, 10, 42, 344, DateTimeKind.Utc), + DateTimeCollection = "[\"2023-09-07T00:00:00\",\"2023-11-14T00:00:00\"]", DateTimeOffset = new DateTimeOffset(new DateTime(1973, 9, 3, 12, 10, 42, 344, DateTimeKind.Unspecified), new TimeSpan(0, 1, 0, 0, 0)), Decimal = 50.0m, Double = 49.0, + DoubleCollection = "[1.2,3.4]", Enum16 = (short)1, Enum32 = 1, Enum64 = 1L, @@ -7438,6 +7595,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) EnumU64 = 1234567890123456789m, Int16 = (short)46, Int32 = 47, + Int32Collection = "[1,2,3,4]", Int64 = 48L, SignedByte = (short)60, Single = 54f, @@ -7456,6 +7614,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) SpatialCPoint = (NetTopologySuite.Geometries.Point)new NetTopologySuite.IO.WKTReader().Read("SRID=4326;POINT Z(1.1 2.2 3.3)"), SpatialCPolygon = (NetTopologySuite.Geometries.Polygon)new NetTopologySuite.IO.WKTReader().Read("SRID=4326;POLYGON ((1.1 2.2, 2.2 2.2, 2.2 1.1, 1.1 2.2))"), String = "FortyThree", + StringCollection = "[\"AB\",\"CD\"]", TimeSpan = new TimeSpan(2, 3, 52, 53, 0), UnsignedInt16 = 56, UnsignedInt32 = 57L, @@ -7583,6 +7742,13 @@ protected override void BuildModel(ModelBuilder modelBuilder) Assert.Equal(4326, ((Geometry)seed["SpatialCMultiPolygon"]).SRID); Assert.Equal(4326, ((Geometry)seed["SpatialCPoint"]).SRID); Assert.Equal(4326, ((Geometry)seed["SpatialCPolygon"]).SRID); + + Assert.Equal("[1,2,3,4]", seed["Int32Collection"]); + Assert.Equal("[1.2,3.4]", seed["DoubleCollection"]); + Assert.Equal("[\"AB\",\"CD\"]", seed["StringCollection"]); + Assert.Equal("[\"2023-09-07T00:00:00\",\"2023-11-14T00:00:00\"]", seed["DateTimeCollection"]); + Assert.Equal("[true,false]", seed["BoolCollection"]); + Assert.Equal("[\"AQI=\",\"AwQ=\"]", seed["BytesCollection"]); }, seed => { @@ -7769,8 +7935,7 @@ protected CSharpMigrationsGenerator CreateMigrationsGenerator() new IRelationalTypeMappingSourcePlugin[] { new SqlServerNetTopologySuiteTypeMappingSourcePlugin(NtsGeometryServices.Instance) - }), - new SqlServerSingletonOptions()); + })); var codeHelper = new CSharpHelper(sqlServerTypeMappingSource); diff --git a/test/EFCore.Design.Tests/Query/LinqToCSharpTranslatorTest.cs b/test/EFCore.Design.Tests/Query/LinqToCSharpTranslatorTest.cs index e517ac61147..f814818f942 100644 --- a/test/EFCore.Design.Tests/Query/LinqToCSharpTranslatorTest.cs +++ b/test/EFCore.Design.Tests/Query/LinqToCSharpTranslatorTest.cs @@ -1835,8 +1835,7 @@ private void AssertCore(Expression expression, bool isStatement, string expected { var typeMappingSource = new SqlServerTypeMappingSource( TestServiceFactory.Instance.Create(), - new RelationalTypeMappingSourceDependencies(new IRelationalTypeMappingSourcePlugin[0]), - new SqlServerSingletonOptions()); + new RelationalTypeMappingSourceDependencies(new IRelationalTypeMappingSourcePlugin[0])); var translator = new CSharpHelper(typeMappingSource); var namespaces = new HashSet(); diff --git a/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpRuntimeModelCodeGeneratorTest.cs b/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpRuntimeModelCodeGeneratorTest.cs index 64a4194747a..a4449fc1e31 100644 --- a/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpRuntimeModelCodeGeneratorTest.cs +++ b/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpRuntimeModelCodeGeneratorTest.cs @@ -5,6 +5,7 @@ using System.ComponentModel; using System.ComponentModel.DataAnnotations.Schema; using System.Data; +using System.Net; using System.Text.Json; using Microsoft.EntityFrameworkCore.Cosmos.ValueGeneration.Internal; using Microsoft.EntityFrameworkCore.Design.Internal; @@ -2280,6 +2281,46 @@ private IRelationalModel CreateRelationalModel() IsNullable = true }; microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("Point", pointColumnBase); + var refTypeArrayColumnBase = new ColumnBase("RefTypeArray", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("RefTypeArray", refTypeArrayColumnBase); + var refTypeEnumerableColumnBase = new ColumnBase("RefTypeEnumerable", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("RefTypeEnumerable", refTypeEnumerableColumnBase); + var refTypeIListColumnBase = new ColumnBase("RefTypeIList", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("RefTypeIList", refTypeIListColumnBase); + var refTypeListColumnBase = new ColumnBase("RefTypeList", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("RefTypeList", refTypeListColumnBase); + var valueTypeArrayColumnBase = new ColumnBase("ValueTypeArray", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("ValueTypeArray", valueTypeArrayColumnBase); + var valueTypeEnumerableColumnBase = new ColumnBase("ValueTypeEnumerable", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("ValueTypeEnumerable", valueTypeEnumerableColumnBase); + var valueTypeIListColumnBase = new ColumnBase("ValueTypeIList", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("ValueTypeIList", valueTypeIListColumnBase); + var valueTypeListColumnBase = new ColumnBase("ValueTypeList", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("ValueTypeList", valueTypeListColumnBase); relationalModel.DefaultTables.Add("Microsoft.EntityFrameworkCore.Scaffolding.Internal.CSharpRuntimeModelCodeGeneratorTest+PrincipalBase", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase); var microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase = new TableMappingBase(principalBase, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase, true); microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.AddTypeMapping(microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase, false); @@ -2291,6 +2332,14 @@ private IRelationalModel CreateRelationalModel() RelationalModel.CreateColumnMapping((ColumnBase)flagsEnum1ColumnBase, principalBase.FindProperty("FlagsEnum1")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); RelationalModel.CreateColumnMapping((ColumnBase)flagsEnum2ColumnBase, principalBase.FindProperty("FlagsEnum2")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); RelationalModel.CreateColumnMapping((ColumnBase)pointColumnBase, principalBase.FindProperty("Point")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)refTypeArrayColumnBase, principalBase.FindProperty("RefTypeArray")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)refTypeEnumerableColumnBase, principalBase.FindProperty("RefTypeEnumerable")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)refTypeIListColumnBase, principalBase.FindProperty("RefTypeIList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)refTypeListColumnBase, principalBase.FindProperty("RefTypeList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)valueTypeArrayColumnBase, principalBase.FindProperty("ValueTypeArray")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)valueTypeEnumerableColumnBase, principalBase.FindProperty("ValueTypeEnumerable")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)valueTypeIListColumnBase, principalBase.FindProperty("ValueTypeIList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)valueTypeListColumnBase, principalBase.FindProperty("ValueTypeList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); var tableMappings1 = new List(); principalBase.SetRuntimeAnnotation("Relational:TableMappings", tableMappings1); @@ -2312,11 +2361,91 @@ private IRelationalModel CreateRelationalModel() principalBaseTable.Columns.Add("FlagsEnum2", flagsEnum2Column); var owned_NumberColumn = new Column("Owned_Number", "int", principalBaseTable); principalBaseTable.Columns.Add("Owned_Number", owned_NumberColumn); + var owned_RefTypeArrayColumn = new Column("Owned_RefTypeArray", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("Owned_RefTypeArray", owned_RefTypeArrayColumn); + var owned_RefTypeEnumerableColumn = new Column("Owned_RefTypeEnumerable", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("Owned_RefTypeEnumerable", owned_RefTypeEnumerableColumn); + var owned_RefTypeIListColumn = new Column("Owned_RefTypeIList", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("Owned_RefTypeIList", owned_RefTypeIListColumn); + var owned_RefTypeListColumn = new Column("Owned_RefTypeList", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("Owned_RefTypeList", owned_RefTypeListColumn); + var owned_ValueTypeArrayColumn = new Column("Owned_ValueTypeArray", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("Owned_ValueTypeArray", owned_ValueTypeArrayColumn); + var owned_ValueTypeEnumerableColumn = new Column("Owned_ValueTypeEnumerable", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("Owned_ValueTypeEnumerable", owned_ValueTypeEnumerableColumn); + var owned_ValueTypeIListColumn = new Column("Owned_ValueTypeIList", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("Owned_ValueTypeIList", owned_ValueTypeIListColumn); + var owned_ValueTypeListColumn = new Column("Owned_ValueTypeList", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("Owned_ValueTypeList", owned_ValueTypeListColumn); var pointColumn = new Column("Point", "geometry", principalBaseTable) { IsNullable = true }; principalBaseTable.Columns.Add("Point", pointColumn); + var refTypeArrayColumn = new Column("RefTypeArray", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("RefTypeArray", refTypeArrayColumn); + var refTypeEnumerableColumn = new Column("RefTypeEnumerable", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("RefTypeEnumerable", refTypeEnumerableColumn); + var refTypeIListColumn = new Column("RefTypeIList", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("RefTypeIList", refTypeIListColumn); + var refTypeListColumn = new Column("RefTypeList", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("RefTypeList", refTypeListColumn); + var valueTypeArrayColumn = new Column("ValueTypeArray", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("ValueTypeArray", valueTypeArrayColumn); + var valueTypeEnumerableColumn = new Column("ValueTypeEnumerable", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("ValueTypeEnumerable", valueTypeEnumerableColumn); + var valueTypeIListColumn = new Column("ValueTypeIList", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("ValueTypeIList", valueTypeIListColumn); + var valueTypeListColumn = new Column("ValueTypeList", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("ValueTypeList", valueTypeListColumn); var aK_PrincipalBase_Id = new UniqueConstraint("AK_PrincipalBase_Id", principalBaseTable, new[] { idColumn0 }); var aK_PrincipalBase_IdUc = RelationalModel.GetKey(this, "Microsoft.EntityFrameworkCore.Scaffolding.Internal.CSharpRuntimeModelCodeGeneratorTest+PrincipalBase", @@ -2359,6 +2488,14 @@ private IRelationalModel CreateRelationalModel() RelationalModel.CreateColumnMapping(flagsEnum1Column, principalBase.FindProperty("FlagsEnum1")!, principalBaseTableMapping); RelationalModel.CreateColumnMapping(flagsEnum2Column, principalBase.FindProperty("FlagsEnum2")!, principalBaseTableMapping); RelationalModel.CreateColumnMapping(pointColumn, principalBase.FindProperty("Point")!, principalBaseTableMapping); + RelationalModel.CreateColumnMapping(refTypeArrayColumn, principalBase.FindProperty("RefTypeArray")!, principalBaseTableMapping); + RelationalModel.CreateColumnMapping(refTypeEnumerableColumn, principalBase.FindProperty("RefTypeEnumerable")!, principalBaseTableMapping); + RelationalModel.CreateColumnMapping(refTypeIListColumn, principalBase.FindProperty("RefTypeIList")!, principalBaseTableMapping); + RelationalModel.CreateColumnMapping(refTypeListColumn, principalBase.FindProperty("RefTypeList")!, principalBaseTableMapping); + RelationalModel.CreateColumnMapping(valueTypeArrayColumn, principalBase.FindProperty("ValueTypeArray")!, principalBaseTableMapping); + RelationalModel.CreateColumnMapping(valueTypeEnumerableColumn, principalBase.FindProperty("ValueTypeEnumerable")!, principalBaseTableMapping); + RelationalModel.CreateColumnMapping(valueTypeIListColumn, principalBase.FindProperty("ValueTypeIList")!, principalBaseTableMapping); + RelationalModel.CreateColumnMapping(valueTypeListColumn, principalBase.FindProperty("ValueTypeList")!, principalBaseTableMapping); var ownedType = FindEntityType("Microsoft.EntityFrameworkCore.Scaffolding.Internal.CSharpRuntimeModelCodeGeneratorTest+PrincipalBase.Owned#OwnedType")!; @@ -2376,6 +2513,46 @@ private IRelationalModel CreateRelationalModel() microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseOwnedOwnedTypeTableBase.Columns.Add("PrincipalBaseAlternateId", principalBaseAlternateIdColumnBase); var principalBaseIdColumnBase = new ColumnBase("PrincipalBaseId", "bigint", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseOwnedOwnedTypeTableBase); microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseOwnedOwnedTypeTableBase.Columns.Add("PrincipalBaseId", principalBaseIdColumnBase); + var refTypeArrayColumnBase0 = new ColumnBase("RefTypeArray", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseOwnedOwnedTypeTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseOwnedOwnedTypeTableBase.Columns.Add("RefTypeArray", refTypeArrayColumnBase0); + var refTypeEnumerableColumnBase0 = new ColumnBase("RefTypeEnumerable", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseOwnedOwnedTypeTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseOwnedOwnedTypeTableBase.Columns.Add("RefTypeEnumerable", refTypeEnumerableColumnBase0); + var refTypeIListColumnBase0 = new ColumnBase("RefTypeIList", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseOwnedOwnedTypeTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseOwnedOwnedTypeTableBase.Columns.Add("RefTypeIList", refTypeIListColumnBase0); + var refTypeListColumnBase0 = new ColumnBase("RefTypeList", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseOwnedOwnedTypeTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseOwnedOwnedTypeTableBase.Columns.Add("RefTypeList", refTypeListColumnBase0); + var valueTypeArrayColumnBase0 = new ColumnBase("ValueTypeArray", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseOwnedOwnedTypeTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseOwnedOwnedTypeTableBase.Columns.Add("ValueTypeArray", valueTypeArrayColumnBase0); + var valueTypeEnumerableColumnBase0 = new ColumnBase("ValueTypeEnumerable", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseOwnedOwnedTypeTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseOwnedOwnedTypeTableBase.Columns.Add("ValueTypeEnumerable", valueTypeEnumerableColumnBase0); + var valueTypeIListColumnBase0 = new ColumnBase("ValueTypeIList", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseOwnedOwnedTypeTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseOwnedOwnedTypeTableBase.Columns.Add("ValueTypeIList", valueTypeIListColumnBase0); + var valueTypeListColumnBase0 = new ColumnBase("ValueTypeList", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseOwnedOwnedTypeTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseOwnedOwnedTypeTableBase.Columns.Add("ValueTypeList", valueTypeListColumnBase0); relationalModel.DefaultTables.Add("Microsoft.EntityFrameworkCore.Scaffolding.Internal.CSharpRuntimeModelCodeGeneratorTest+PrincipalBase.Owned#OwnedType", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseOwnedOwnedTypeTableBase); var microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseOwnedOwnedTypeMappingBase = new TableMappingBase(ownedType, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseOwnedOwnedTypeTableBase, true); microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseOwnedOwnedTypeTableBase.AddTypeMapping(microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseOwnedOwnedTypeMappingBase, false); @@ -2384,6 +2561,14 @@ private IRelationalModel CreateRelationalModel() RelationalModel.CreateColumnMapping((ColumnBase)principalBaseIdColumnBase, ownedType.FindProperty("PrincipalBaseId")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseOwnedOwnedTypeMappingBase); RelationalModel.CreateColumnMapping((ColumnBase)detailsColumnBase, ownedType.FindProperty("Details")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseOwnedOwnedTypeMappingBase); RelationalModel.CreateColumnMapping((ColumnBase)numberColumnBase, ownedType.FindProperty("Number")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseOwnedOwnedTypeMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)refTypeArrayColumnBase0, ownedType.FindProperty("RefTypeArray")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseOwnedOwnedTypeMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)refTypeEnumerableColumnBase0, ownedType.FindProperty("RefTypeEnumerable")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseOwnedOwnedTypeMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)refTypeIListColumnBase0, ownedType.FindProperty("RefTypeIList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseOwnedOwnedTypeMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)refTypeListColumnBase0, ownedType.FindProperty("RefTypeList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseOwnedOwnedTypeMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)valueTypeArrayColumnBase0, ownedType.FindProperty("ValueTypeArray")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseOwnedOwnedTypeMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)valueTypeEnumerableColumnBase0, ownedType.FindProperty("ValueTypeEnumerable")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseOwnedOwnedTypeMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)valueTypeIListColumnBase0, ownedType.FindProperty("ValueTypeIList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseOwnedOwnedTypeMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)valueTypeListColumnBase0, ownedType.FindProperty("ValueTypeList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseOwnedOwnedTypeMappingBase); var tableMappings2 = new List(); ownedType.SetRuntimeAnnotation("Relational:TableMappings", tableMappings2); @@ -2402,6 +2587,14 @@ private IRelationalModel CreateRelationalModel() RelationalModel.CreateColumnMapping(alternateIdColumn, ownedType.FindProperty("PrincipalBaseAlternateId")!, principalBaseTableMapping0); RelationalModel.CreateColumnMapping(idColumn0, ownedType.FindProperty("PrincipalBaseId")!, principalBaseTableMapping0); RelationalModel.CreateColumnMapping(owned_NumberColumn, ownedType.FindProperty("Number")!, principalBaseTableMapping0); + RelationalModel.CreateColumnMapping(owned_RefTypeArrayColumn, ownedType.FindProperty("RefTypeArray")!, principalBaseTableMapping0); + RelationalModel.CreateColumnMapping(owned_RefTypeEnumerableColumn, ownedType.FindProperty("RefTypeEnumerable")!, principalBaseTableMapping0); + RelationalModel.CreateColumnMapping(owned_RefTypeIListColumn, ownedType.FindProperty("RefTypeIList")!, principalBaseTableMapping0); + RelationalModel.CreateColumnMapping(owned_RefTypeListColumn, ownedType.FindProperty("RefTypeList")!, principalBaseTableMapping0); + RelationalModel.CreateColumnMapping(owned_ValueTypeArrayColumn, ownedType.FindProperty("ValueTypeArray")!, principalBaseTableMapping0); + RelationalModel.CreateColumnMapping(owned_ValueTypeEnumerableColumn, ownedType.FindProperty("ValueTypeEnumerable")!, principalBaseTableMapping0); + RelationalModel.CreateColumnMapping(owned_ValueTypeIListColumn, ownedType.FindProperty("ValueTypeIList")!, principalBaseTableMapping0); + RelationalModel.CreateColumnMapping(owned_ValueTypeListColumn, ownedType.FindProperty("ValueTypeList")!, principalBaseTableMapping0); var detailsTable = new Table("Details", null, relationalModel); var principalBaseIdColumn = new Column("PrincipalBaseId", "bigint", detailsTable); detailsTable.Columns.Add("PrincipalBaseId", principalBaseIdColumn); @@ -2445,6 +2638,14 @@ private IRelationalModel CreateRelationalModel() RelationalModel.CreateColumnMapping((ColumnBase)flagsEnum1ColumnBase, principalDerived.FindProperty("FlagsEnum1")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0); RelationalModel.CreateColumnMapping((ColumnBase)flagsEnum2ColumnBase, principalDerived.FindProperty("FlagsEnum2")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0); RelationalModel.CreateColumnMapping((ColumnBase)pointColumnBase, principalDerived.FindProperty("Point")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0); + RelationalModel.CreateColumnMapping((ColumnBase)refTypeArrayColumnBase, principalDerived.FindProperty("RefTypeArray")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0); + RelationalModel.CreateColumnMapping((ColumnBase)refTypeEnumerableColumnBase, principalDerived.FindProperty("RefTypeEnumerable")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0); + RelationalModel.CreateColumnMapping((ColumnBase)refTypeIListColumnBase, principalDerived.FindProperty("RefTypeIList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0); + RelationalModel.CreateColumnMapping((ColumnBase)refTypeListColumnBase, principalDerived.FindProperty("RefTypeList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0); + RelationalModel.CreateColumnMapping((ColumnBase)valueTypeArrayColumnBase, principalDerived.FindProperty("ValueTypeArray")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0); + RelationalModel.CreateColumnMapping((ColumnBase)valueTypeEnumerableColumnBase, principalDerived.FindProperty("ValueTypeEnumerable")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0); + RelationalModel.CreateColumnMapping((ColumnBase)valueTypeIListColumnBase, principalDerived.FindProperty("ValueTypeIList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0); + RelationalModel.CreateColumnMapping((ColumnBase)valueTypeListColumnBase, principalDerived.FindProperty("ValueTypeList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0); var defaultTableMappings4 = new List>(); principalDerived.SetRuntimeAnnotation("Relational:DefaultMappings", defaultTableMappings4); @@ -2475,6 +2676,14 @@ private IRelationalModel CreateRelationalModel() RelationalModel.CreateColumnMapping(flagsEnum1Column, principalDerived.FindProperty("FlagsEnum1")!, principalBaseTableMapping1); RelationalModel.CreateColumnMapping(flagsEnum2Column, principalDerived.FindProperty("FlagsEnum2")!, principalBaseTableMapping1); RelationalModel.CreateColumnMapping(pointColumn, principalDerived.FindProperty("Point")!, principalBaseTableMapping1); + RelationalModel.CreateColumnMapping(refTypeArrayColumn, principalDerived.FindProperty("RefTypeArray")!, principalBaseTableMapping1); + RelationalModel.CreateColumnMapping(refTypeEnumerableColumn, principalDerived.FindProperty("RefTypeEnumerable")!, principalBaseTableMapping1); + RelationalModel.CreateColumnMapping(refTypeIListColumn, principalDerived.FindProperty("RefTypeIList")!, principalBaseTableMapping1); + RelationalModel.CreateColumnMapping(refTypeListColumn, principalDerived.FindProperty("RefTypeList")!, principalBaseTableMapping1); + RelationalModel.CreateColumnMapping(valueTypeArrayColumn, principalDerived.FindProperty("ValueTypeArray")!, principalBaseTableMapping1); + RelationalModel.CreateColumnMapping(valueTypeEnumerableColumn, principalDerived.FindProperty("ValueTypeEnumerable")!, principalBaseTableMapping1); + RelationalModel.CreateColumnMapping(valueTypeIListColumn, principalDerived.FindProperty("ValueTypeIList")!, principalBaseTableMapping1); + RelationalModel.CreateColumnMapping(valueTypeListColumn, principalDerived.FindProperty("ValueTypeList")!, principalBaseTableMapping1); var principalDerivedTable = new Table("PrincipalDerived", null, relationalModel); var derivedIdColumn = new Column("DerivedId", "bigint", principalDerivedTable); principalDerivedTable.Columns.Add("DerivedId", derivedIdColumn); @@ -2528,6 +2737,46 @@ private IRelationalModel CreateRelationalModel() microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteManyOwnedOwnedTypeTableBase.Columns.Add("PrincipalDerived>AlternateId", principalDerivedDependentBasebyteAlternateIdColumnBase); var principalDerivedDependentBasebyteIdColumnBase = new ColumnBase("PrincipalDerived>Id", "bigint", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteManyOwnedOwnedTypeTableBase); microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteManyOwnedOwnedTypeTableBase.Columns.Add("PrincipalDerived>Id", principalDerivedDependentBasebyteIdColumnBase); + var refTypeArrayColumnBase1 = new ColumnBase("RefTypeArray", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteManyOwnedOwnedTypeTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteManyOwnedOwnedTypeTableBase.Columns.Add("RefTypeArray", refTypeArrayColumnBase1); + var refTypeEnumerableColumnBase1 = new ColumnBase("RefTypeEnumerable", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteManyOwnedOwnedTypeTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteManyOwnedOwnedTypeTableBase.Columns.Add("RefTypeEnumerable", refTypeEnumerableColumnBase1); + var refTypeIListColumnBase1 = new ColumnBase("RefTypeIList", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteManyOwnedOwnedTypeTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteManyOwnedOwnedTypeTableBase.Columns.Add("RefTypeIList", refTypeIListColumnBase1); + var refTypeListColumnBase1 = new ColumnBase("RefTypeList", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteManyOwnedOwnedTypeTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteManyOwnedOwnedTypeTableBase.Columns.Add("RefTypeList", refTypeListColumnBase1); + var valueTypeArrayColumnBase1 = new ColumnBase("ValueTypeArray", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteManyOwnedOwnedTypeTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteManyOwnedOwnedTypeTableBase.Columns.Add("ValueTypeArray", valueTypeArrayColumnBase1); + var valueTypeEnumerableColumnBase1 = new ColumnBase("ValueTypeEnumerable", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteManyOwnedOwnedTypeTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteManyOwnedOwnedTypeTableBase.Columns.Add("ValueTypeEnumerable", valueTypeEnumerableColumnBase1); + var valueTypeIListColumnBase1 = new ColumnBase("ValueTypeIList", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteManyOwnedOwnedTypeTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteManyOwnedOwnedTypeTableBase.Columns.Add("ValueTypeIList", valueTypeIListColumnBase1); + var valueTypeListColumnBase1 = new ColumnBase("ValueTypeList", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteManyOwnedOwnedTypeTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteManyOwnedOwnedTypeTableBase.Columns.Add("ValueTypeList", valueTypeListColumnBase1); relationalModel.DefaultTables.Add("Microsoft.EntityFrameworkCore.Scaffolding.Internal.CSharpRuntimeModelCodeGeneratorTest+PrincipalDerived>.ManyOwned#OwnedType", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteManyOwnedOwnedTypeTableBase); var microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteManyOwnedOwnedTypeMappingBase = new TableMappingBase(ownedType0, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteManyOwnedOwnedTypeTableBase, true); microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteManyOwnedOwnedTypeTableBase.AddTypeMapping(microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteManyOwnedOwnedTypeMappingBase, false); @@ -2537,6 +2786,14 @@ private IRelationalModel CreateRelationalModel() RelationalModel.CreateColumnMapping((ColumnBase)principalDerivedDependentBasebyteIdColumnBase, ownedType0.FindProperty("PrincipalDerivedId")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteManyOwnedOwnedTypeMappingBase); RelationalModel.CreateColumnMapping((ColumnBase)detailsColumnBase0, ownedType0.FindProperty("Details")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteManyOwnedOwnedTypeMappingBase); RelationalModel.CreateColumnMapping((ColumnBase)numberColumnBase0, ownedType0.FindProperty("Number")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteManyOwnedOwnedTypeMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)refTypeArrayColumnBase1, ownedType0.FindProperty("RefTypeArray")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteManyOwnedOwnedTypeMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)refTypeEnumerableColumnBase1, ownedType0.FindProperty("RefTypeEnumerable")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteManyOwnedOwnedTypeMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)refTypeIListColumnBase1, ownedType0.FindProperty("RefTypeIList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteManyOwnedOwnedTypeMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)refTypeListColumnBase1, ownedType0.FindProperty("RefTypeList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteManyOwnedOwnedTypeMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)valueTypeArrayColumnBase1, ownedType0.FindProperty("ValueTypeArray")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteManyOwnedOwnedTypeMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)valueTypeEnumerableColumnBase1, ownedType0.FindProperty("ValueTypeEnumerable")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteManyOwnedOwnedTypeMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)valueTypeIListColumnBase1, ownedType0.FindProperty("ValueTypeIList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteManyOwnedOwnedTypeMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)valueTypeListColumnBase1, ownedType0.FindProperty("ValueTypeList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteManyOwnedOwnedTypeMappingBase); var tableMappings4 = new List(); ownedType0.SetRuntimeAnnotation("Relational:TableMappings", tableMappings4); @@ -2554,6 +2811,46 @@ private IRelationalModel CreateRelationalModel() manyOwnedTable.Columns.Add("Details", detailsColumn0); var numberColumn = new Column("Number", "int", manyOwnedTable); manyOwnedTable.Columns.Add("Number", numberColumn); + var refTypeArrayColumn0 = new Column("RefTypeArray", "nvarchar(max)", manyOwnedTable) + { + IsNullable = true + }; + manyOwnedTable.Columns.Add("RefTypeArray", refTypeArrayColumn0); + var refTypeEnumerableColumn0 = new Column("RefTypeEnumerable", "nvarchar(max)", manyOwnedTable) + { + IsNullable = true + }; + manyOwnedTable.Columns.Add("RefTypeEnumerable", refTypeEnumerableColumn0); + var refTypeIListColumn0 = new Column("RefTypeIList", "nvarchar(max)", manyOwnedTable) + { + IsNullable = true + }; + manyOwnedTable.Columns.Add("RefTypeIList", refTypeIListColumn0); + var refTypeListColumn0 = new Column("RefTypeList", "nvarchar(max)", manyOwnedTable) + { + IsNullable = true + }; + manyOwnedTable.Columns.Add("RefTypeList", refTypeListColumn0); + var valueTypeArrayColumn0 = new Column("ValueTypeArray", "nvarchar(max)", manyOwnedTable) + { + IsNullable = true + }; + manyOwnedTable.Columns.Add("ValueTypeArray", valueTypeArrayColumn0); + var valueTypeEnumerableColumn0 = new Column("ValueTypeEnumerable", "nvarchar(max)", manyOwnedTable) + { + IsNullable = true + }; + manyOwnedTable.Columns.Add("ValueTypeEnumerable", valueTypeEnumerableColumn0); + var valueTypeIListColumn0 = new Column("ValueTypeIList", "nvarchar(max)", manyOwnedTable) + { + IsNullable = true + }; + manyOwnedTable.Columns.Add("ValueTypeIList", valueTypeIListColumn0); + var valueTypeListColumn0 = new Column("ValueTypeList", "nvarchar(max)", manyOwnedTable) + { + IsNullable = true + }; + manyOwnedTable.Columns.Add("ValueTypeList", valueTypeListColumn0); var pK_ManyOwned = new UniqueConstraint("PK_ManyOwned", manyOwnedTable, new[] { principalDerivedDependentBasebyteIdColumn, principalDerivedDependentBasebyteAlternateIdColumn, idColumn1 }); manyOwnedTable.PrimaryKey = pK_ManyOwned; var pK_ManyOwnedUc = RelationalModel.GetKey(this, @@ -2571,6 +2868,14 @@ private IRelationalModel CreateRelationalModel() RelationalModel.CreateColumnMapping(principalDerivedDependentBasebyteIdColumn, ownedType0.FindProperty("PrincipalDerivedId")!, manyOwnedTableMapping); RelationalModel.CreateColumnMapping(detailsColumn0, ownedType0.FindProperty("Details")!, manyOwnedTableMapping); RelationalModel.CreateColumnMapping(numberColumn, ownedType0.FindProperty("Number")!, manyOwnedTableMapping); + RelationalModel.CreateColumnMapping(refTypeArrayColumn0, ownedType0.FindProperty("RefTypeArray")!, manyOwnedTableMapping); + RelationalModel.CreateColumnMapping(refTypeEnumerableColumn0, ownedType0.FindProperty("RefTypeEnumerable")!, manyOwnedTableMapping); + RelationalModel.CreateColumnMapping(refTypeIListColumn0, ownedType0.FindProperty("RefTypeIList")!, manyOwnedTableMapping); + RelationalModel.CreateColumnMapping(refTypeListColumn0, ownedType0.FindProperty("RefTypeList")!, manyOwnedTableMapping); + RelationalModel.CreateColumnMapping(valueTypeArrayColumn0, ownedType0.FindProperty("ValueTypeArray")!, manyOwnedTableMapping); + RelationalModel.CreateColumnMapping(valueTypeEnumerableColumn0, ownedType0.FindProperty("ValueTypeEnumerable")!, manyOwnedTableMapping); + RelationalModel.CreateColumnMapping(valueTypeIListColumn0, ownedType0.FindProperty("ValueTypeIList")!, manyOwnedTableMapping); + RelationalModel.CreateColumnMapping(valueTypeListColumn0, ownedType0.FindProperty("ValueTypeList")!, manyOwnedTableMapping); var principalBasePrincipalDerivedDependentBasebyte = FindEntityType("PrincipalBasePrincipalDerived>")!; @@ -2937,6 +3242,7 @@ public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) // using System; using System.Collections.Generic; +using System.Net; using System.Reflection; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.ChangeTracking; @@ -3149,60 +3455,444 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType bas point.AddAnnotation("Relational:DefaultValue", (NetTopologySuite.Geometries.Point)new NetTopologySuite.IO.WKTReader().Read("SRID=0;POINT Z(0 0 0)")); point.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); - var key = runtimeEntityType.AddKey( - new[] { id }); - - var key0 = runtimeEntityType.AddKey( - new[] { id, alternateId }); - runtimeEntityType.SetPrimaryKey(key0); - key0.AddAnnotation("Relational:Name", "PK"); - - var index = runtimeEntityType.AddIndex( - new[] { alternateId, id }); - - return runtimeEntityType; - } - - public static RuntimeSkipNavigation CreateSkipNavigation1(RuntimeEntityType declaringEntityType, RuntimeEntityType targetEntityType, RuntimeEntityType joinEntityType) - { - var skipNavigation = declaringEntityType.AddSkipNavigation( - "Deriveds", - targetEntityType, - joinEntityType.FindForeignKey( - new[] { joinEntityType.FindProperty("PrincipalsId"), joinEntityType.FindProperty("PrincipalsAlternateId") }, - declaringEntityType.FindKey(new[] { declaringEntityType.FindProperty("Id"), declaringEntityType.FindProperty("AlternateId") }), - declaringEntityType), - true, - false, - typeof(ICollection), - propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("Deriveds", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), - fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); - - var inverse = targetEntityType.FindSkipNavigation("Principals"); - if (inverse != null) - { - skipNavigation.Inverse = inverse; - inverse.Inverse = skipNavigation; - } + var refTypeArray = runtimeEntityType.AddProperty( + "RefTypeArray", + typeof(IPAddress[]), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("RefTypeArray", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); + refTypeArray.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v)), + keyComparer: new ListComparer(new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v)), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), + converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter( + new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v))))), + storeTypePostfix: StoreTypePostfix.None, + jsonValueReaderWriter: new JsonCollectionReaderWriter( + new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v)))), + elementMapping: SqlServerStringTypeMapping.Default.Clone( + comparer: new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v), + keyComparer: new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(45)", + size: 45, + dbType: System.Data.DbType.String), + converter: new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v)), + jsonValueReaderWriter: new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v))))); +refTypeArray.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var refTypeEnumerable = runtimeEntityType.AddProperty( + "RefTypeEnumerable", + typeof(IEnumerable), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("RefTypeEnumerable", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +refTypeEnumerable.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, string>( + JsonStringReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, string>( + JsonStringReaderWriter.Instance), +elementMapping: SqlServerStringTypeMapping.Default.Clone( + comparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + keyComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), + storeTypePostfix: StoreTypePostfix.None)); +refTypeEnumerable.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var refTypeIList = runtimeEntityType.AddProperty( + "RefTypeIList", + typeof(IList), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("RefTypeIList", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +refTypeIList.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, string>( + JsonStringReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, string>( + JsonStringReaderWriter.Instance), +elementMapping: SqlServerStringTypeMapping.Default.Clone( + comparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + keyComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), + storeTypePostfix: StoreTypePostfix.None)); +refTypeIList.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var refTypeList = runtimeEntityType.AddProperty( + "RefTypeList", + typeof(List), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("RefTypeList", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +refTypeList.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, IPAddress>( + new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v))))), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, IPAddress>( + new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v)))), +elementMapping: SqlServerStringTypeMapping.Default.Clone( + comparer: new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v), + keyComparer: new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(45)", + size: 45, + dbType: System.Data.DbType.String), + converter: new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v)), + jsonValueReaderWriter: new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v))))); +refTypeList.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var valueTypeArray = runtimeEntityType.AddProperty( + "ValueTypeArray", + typeof(DateTime[]), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("ValueTypeArray", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +valueTypeArray.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter( + JsonDateTimeReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter( + JsonDateTimeReaderWriter.Instance), +elementMapping: SqlServerDateTimeTypeMapping.Default.Clone( + comparer: new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v), + keyComparer: new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v), + providerValueComparer: new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v))); +valueTypeArray.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var valueTypeEnumerable = runtimeEntityType.AddProperty( + "ValueTypeEnumerable", + typeof(IEnumerable), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("ValueTypeEnumerable", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +valueTypeEnumerable.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, byte>( + JsonByteReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, byte>( + JsonByteReaderWriter.Instance), +elementMapping: SqlServerByteTypeMapping.Default.Clone( + comparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v), + keyComparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v), + providerValueComparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v))); +valueTypeEnumerable.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var valueTypeIList = runtimeEntityType.AddProperty( + "ValueTypeIList", + typeof(IList), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("ValueTypeIList", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +valueTypeIList.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, byte>( + JsonByteReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, byte>( + JsonByteReaderWriter.Instance), +elementMapping: SqlServerByteTypeMapping.Default.Clone( + comparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v), + keyComparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v), + providerValueComparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v))); +valueTypeIList.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var valueTypeList = runtimeEntityType.AddProperty( + "ValueTypeList", + typeof(List), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("ValueTypeList", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +valueTypeList.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, short>( + JsonInt16ReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, short>( + JsonInt16ReaderWriter.Instance), +elementMapping: SqlServerShortTypeMapping.Default.Clone( + comparer: new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v), + keyComparer: new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v), + providerValueComparer: new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v))); +valueTypeList.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var key = runtimeEntityType.AddKey( + new[] { id }); + +var key0 = runtimeEntityType.AddKey( + new[] { id, alternateId }); +runtimeEntityType.SetPrimaryKey(key0); +key0.AddAnnotation("Relational:Name", "PK"); + +var index = runtimeEntityType.AddIndex( + new[] { alternateId, id }); + +return runtimeEntityType; +} - return skipNavigation; - } +public static RuntimeSkipNavigation CreateSkipNavigation1(RuntimeEntityType declaringEntityType, RuntimeEntityType targetEntityType, RuntimeEntityType joinEntityType) +{ + var skipNavigation = declaringEntityType.AddSkipNavigation( + "Deriveds", + targetEntityType, + joinEntityType.FindForeignKey( + new[] { joinEntityType.FindProperty("PrincipalsId"), joinEntityType.FindProperty("PrincipalsAlternateId") }, + declaringEntityType.FindKey(new[] { declaringEntityType.FindProperty("Id"), declaringEntityType.FindProperty("AlternateId") }), + declaringEntityType), + true, + false, + typeof(ICollection), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("Deriveds", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); + + var inverse = targetEntityType.FindSkipNavigation("Principals"); + if (inverse != null) + { + skipNavigation.Inverse = inverse; + inverse.Inverse = skipNavigation; + } - public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) - { - runtimeEntityType.AddAnnotation("Relational:FunctionName", null); - runtimeEntityType.AddAnnotation("Relational:MappingStrategy", "TPT"); - runtimeEntityType.AddAnnotation("Relational:Schema", "mySchema"); - runtimeEntityType.AddAnnotation("Relational:SqlQuery", null); - runtimeEntityType.AddAnnotation("Relational:TableName", "PrincipalBase"); - runtimeEntityType.AddAnnotation("Relational:ViewName", null); - runtimeEntityType.AddAnnotation("Relational:ViewSchema", null); + return skipNavigation; +} - Customize(runtimeEntityType); - } +public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) +{ + runtimeEntityType.AddAnnotation("Relational:FunctionName", null); + runtimeEntityType.AddAnnotation("Relational:MappingStrategy", "TPT"); + runtimeEntityType.AddAnnotation("Relational:Schema", "mySchema"); + runtimeEntityType.AddAnnotation("Relational:SqlQuery", null); + runtimeEntityType.AddAnnotation("Relational:TableName", "PrincipalBase"); + runtimeEntityType.AddAnnotation("Relational:ViewName", null); + runtimeEntityType.AddAnnotation("Relational:ViewSchema", null); + + Customize(runtimeEntityType); +} - static partial void Customize(RuntimeEntityType runtimeEntityType); - } +static partial void Customize(RuntimeEntityType runtimeEntityType); +} } """, c), c => AssertFileContents( @@ -3210,6 +3900,8 @@ public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) """ // using System; +using System.Collections.Generic; +using System.Net; using System.Reflection; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.ChangeTracking; @@ -3217,6 +3909,8 @@ public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) using Microsoft.EntityFrameworkCore.Scaffolding.Internal; using Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal; using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.EntityFrameworkCore.Storage.Json; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; #pragma warning disable 219, 612, 618 #nullable disable @@ -3348,75 +4042,467 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType bas (int v) => v)); number.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); - var context = runtimeEntityType.AddServiceProperty( - "Context", - propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("Context", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), - serviceType: typeof(DbContext)); - - var key = runtimeEntityType.AddKey( - new[] { principalBaseId, principalBaseAlternateId }); - runtimeEntityType.SetPrimaryKey(key); - - return runtimeEntityType; - } - - public static RuntimeForeignKey CreateForeignKey1(RuntimeEntityType declaringEntityType, RuntimeEntityType principalEntityType) - { - var runtimeForeignKey = declaringEntityType.AddForeignKey(new[] { declaringEntityType.FindProperty("PrincipalBaseId"), declaringEntityType.FindProperty("PrincipalBaseAlternateId") }, - principalEntityType.FindKey(new[] { principalEntityType.FindProperty("Id"), principalEntityType.FindProperty("AlternateId") }), - principalEntityType, - deleteBehavior: DeleteBehavior.Cascade, - unique: true, - required: true, - requiredDependent: true, - ownership: true); - - var owned = principalEntityType.AddNavigation("Owned", - runtimeForeignKey, - onDependent: false, - typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType), - propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("Owned", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), - fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("_ownedField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + var refTypeArray = runtimeEntityType.AddProperty( + "RefTypeArray", + typeof(IPAddress[]), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("RefTypeArray", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("_refTypeArray", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), propertyAccessMode: PropertyAccessMode.Field, - eagerLoaded: true); - - return runtimeForeignKey; - } - - public static RuntimeForeignKey CreateForeignKey2(RuntimeEntityType declaringEntityType, RuntimeEntityType principalEntityType) - { - var runtimeForeignKey = declaringEntityType.AddForeignKey(new[] { declaringEntityType.FindProperty("PrincipalBaseId"), declaringEntityType.FindProperty("PrincipalBaseAlternateId") }, - principalEntityType.FindKey(new[] { principalEntityType.FindProperty("PrincipalBaseId"), principalEntityType.FindProperty("PrincipalBaseAlternateId") }), - principalEntityType, - deleteBehavior: DeleteBehavior.Cascade, - unique: true, - required: true, - requiredDependent: true); + nullable: true); + refTypeArray.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v)), + keyComparer: new ListComparer(new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v)), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), + converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter( + new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v))))), + storeTypePostfix: StoreTypePostfix.None, + jsonValueReaderWriter: new JsonCollectionReaderWriter( + new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v)))), + elementMapping: SqlServerStringTypeMapping.Default.Clone( + comparer: new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v), + keyComparer: new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(45)", + size: 45, + dbType: System.Data.DbType.String), + converter: new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v)), + jsonValueReaderWriter: new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v))))); +refTypeArray.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var refTypeEnumerable = runtimeEntityType.AddProperty( + "RefTypeEnumerable", + typeof(IEnumerable), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("RefTypeEnumerable", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("_refTypeEnumerable", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + propertyAccessMode: PropertyAccessMode.Field, + nullable: true); +refTypeEnumerable.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, string>( + JsonStringReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, string>( + JsonStringReaderWriter.Instance), +elementMapping: SqlServerStringTypeMapping.Default.Clone( + comparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + keyComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), + storeTypePostfix: StoreTypePostfix.None)); +refTypeEnumerable.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var refTypeIList = runtimeEntityType.AddProperty( + "RefTypeIList", + typeof(IList), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("RefTypeIList", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("_refTypeIList", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + propertyAccessMode: PropertyAccessMode.Field, + nullable: true); +refTypeIList.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, string>( + JsonStringReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, string>( + JsonStringReaderWriter.Instance), +elementMapping: SqlServerStringTypeMapping.Default.Clone( + comparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + keyComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), + storeTypePostfix: StoreTypePostfix.None)); +refTypeIList.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var refTypeList = runtimeEntityType.AddProperty( + "RefTypeList", + typeof(List), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("RefTypeList", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("_refTypeList", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + propertyAccessMode: PropertyAccessMode.Field, + nullable: true); +refTypeList.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, IPAddress>( + new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v))))), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, IPAddress>( + new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v)))), +elementMapping: SqlServerStringTypeMapping.Default.Clone( + comparer: new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v), + keyComparer: new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(45)", + size: 45, + dbType: System.Data.DbType.String), + converter: new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v)), + jsonValueReaderWriter: new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v))))); +refTypeList.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var valueTypeArray = runtimeEntityType.AddProperty( + "ValueTypeArray", + typeof(DateTime[]), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("ValueTypeArray", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("_valueTypeArray", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + propertyAccessMode: PropertyAccessMode.Field, + nullable: true); +valueTypeArray.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter( + JsonDateTimeReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter( + JsonDateTimeReaderWriter.Instance), +elementMapping: SqlServerDateTimeTypeMapping.Default.Clone( + comparer: new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v), + keyComparer: new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v), + providerValueComparer: new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v))); +valueTypeArray.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var valueTypeEnumerable = runtimeEntityType.AddProperty( + "ValueTypeEnumerable", + typeof(IEnumerable), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("ValueTypeEnumerable", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("_valueTypeEnumerable", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + propertyAccessMode: PropertyAccessMode.Field, + nullable: true); +valueTypeEnumerable.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, byte>( + JsonByteReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, byte>( + JsonByteReaderWriter.Instance), +elementMapping: SqlServerByteTypeMapping.Default.Clone( + comparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v), + keyComparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v), + providerValueComparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v))); +valueTypeEnumerable.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var valueTypeIList = runtimeEntityType.AddProperty( + "ValueTypeIList", + typeof(IList), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("ValueTypeIList", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + propertyAccessMode: PropertyAccessMode.Field, + nullable: true); +valueTypeIList.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, byte>( + JsonByteReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, byte>( + JsonByteReaderWriter.Instance), +elementMapping: SqlServerByteTypeMapping.Default.Clone( + comparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v), + keyComparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v), + providerValueComparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v))); +valueTypeIList.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var valueTypeList = runtimeEntityType.AddProperty( + "ValueTypeList", + typeof(List), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("ValueTypeList", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("_valueTypeList", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + propertyAccessMode: PropertyAccessMode.Field, + nullable: true); +valueTypeList.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, short>( + JsonInt16ReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, short>( + JsonInt16ReaderWriter.Instance), +elementMapping: SqlServerShortTypeMapping.Default.Clone( + comparer: new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v), + keyComparer: new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v), + providerValueComparer: new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v))); +valueTypeList.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var context = runtimeEntityType.AddServiceProperty( + "Context", + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("Context", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + serviceType: typeof(DbContext)); + +var key = runtimeEntityType.AddKey( + new[] { principalBaseId, principalBaseAlternateId }); +runtimeEntityType.SetPrimaryKey(key); + +return runtimeEntityType; +} - return runtimeForeignKey; - } +public static RuntimeForeignKey CreateForeignKey1(RuntimeEntityType declaringEntityType, RuntimeEntityType principalEntityType) +{ + var runtimeForeignKey = declaringEntityType.AddForeignKey(new[] { declaringEntityType.FindProperty("PrincipalBaseId"), declaringEntityType.FindProperty("PrincipalBaseAlternateId") }, + principalEntityType.FindKey(new[] { principalEntityType.FindProperty("Id"), principalEntityType.FindProperty("AlternateId") }), + principalEntityType, + deleteBehavior: DeleteBehavior.Cascade, + unique: true, + required: true, + requiredDependent: true, + ownership: true); + + var owned = principalEntityType.AddNavigation("Owned", + runtimeForeignKey, + onDependent: false, + typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("Owned", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("_ownedField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + propertyAccessMode: PropertyAccessMode.Field, + eagerLoaded: true); + + return runtimeForeignKey; +} - public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) - { - var fragments = new StoreObjectDictionary(); - var detailsFragment = new RuntimeEntityTypeMappingFragment( - runtimeEntityType, - StoreObjectIdentifier.Table("Details", null), - null); - fragments.Add(StoreObjectIdentifier.Table("Details", null), detailsFragment); - runtimeEntityType.AddAnnotation("Relational:MappingFragments", fragments); - runtimeEntityType.AddAnnotation("Relational:FunctionName", null); - runtimeEntityType.AddAnnotation("Relational:Schema", "mySchema"); - runtimeEntityType.AddAnnotation("Relational:SqlQuery", null); - runtimeEntityType.AddAnnotation("Relational:TableName", "PrincipalBase"); - runtimeEntityType.AddAnnotation("Relational:ViewName", null); - runtimeEntityType.AddAnnotation("Relational:ViewSchema", null); +public static RuntimeForeignKey CreateForeignKey2(RuntimeEntityType declaringEntityType, RuntimeEntityType principalEntityType) +{ + var runtimeForeignKey = declaringEntityType.AddForeignKey(new[] { declaringEntityType.FindProperty("PrincipalBaseId"), declaringEntityType.FindProperty("PrincipalBaseAlternateId") }, + principalEntityType.FindKey(new[] { principalEntityType.FindProperty("PrincipalBaseId"), principalEntityType.FindProperty("PrincipalBaseAlternateId") }), + principalEntityType, + deleteBehavior: DeleteBehavior.Cascade, + unique: true, + required: true, + requiredDependent: true); + + return runtimeForeignKey; +} - Customize(runtimeEntityType); - } +public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) +{ + var fragments = new StoreObjectDictionary(); + var detailsFragment = new RuntimeEntityTypeMappingFragment( + runtimeEntityType, + StoreObjectIdentifier.Table("Details", null), + null); + fragments.Add(StoreObjectIdentifier.Table("Details", null), detailsFragment); + runtimeEntityType.AddAnnotation("Relational:MappingFragments", fragments); + runtimeEntityType.AddAnnotation("Relational:FunctionName", null); + runtimeEntityType.AddAnnotation("Relational:Schema", "mySchema"); + runtimeEntityType.AddAnnotation("Relational:SqlQuery", null); + runtimeEntityType.AddAnnotation("Relational:TableName", "PrincipalBase"); + runtimeEntityType.AddAnnotation("Relational:ViewName", null); + runtimeEntityType.AddAnnotation("Relational:ViewSchema", null); + + Customize(runtimeEntityType); +} - static partial void Customize(RuntimeEntityType runtimeEntityType); - } +static partial void Customize(RuntimeEntityType runtimeEntityType); +} } """, c), c => AssertFileContents( @@ -3425,6 +4511,7 @@ public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) // using System; using System.Collections.Generic; +using System.Net; using System.Reflection; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.ChangeTracking; @@ -3432,6 +4519,8 @@ public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) using Microsoft.EntityFrameworkCore.Scaffolding.Internal; using Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal; using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.EntityFrameworkCore.Storage.Json; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; #pragma warning disable 219, 612, 618 #nullable disable @@ -3557,52 +4646,436 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType bas (int v) => v)); number.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); - var context = runtimeEntityType.AddServiceProperty( - "Context", - propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("Context", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), - serviceType: typeof(DbContext)); + var refTypeArray = runtimeEntityType.AddProperty( + "RefTypeArray", + typeof(IPAddress[]), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("RefTypeArray", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("_refTypeArray", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); + refTypeArray.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v)), + keyComparer: new ListComparer(new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v)), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), + converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter( + new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v))))), + storeTypePostfix: StoreTypePostfix.None, + jsonValueReaderWriter: new JsonCollectionReaderWriter( + new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v)))), + elementMapping: SqlServerStringTypeMapping.Default.Clone( + comparer: new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v), + keyComparer: new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(45)", + size: 45, + dbType: System.Data.DbType.String), + converter: new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v)), + jsonValueReaderWriter: new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v))))); +refTypeArray.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var refTypeEnumerable = runtimeEntityType.AddProperty( + "RefTypeEnumerable", + typeof(IEnumerable), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("RefTypeEnumerable", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("_refTypeEnumerable", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +refTypeEnumerable.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, string>( + JsonStringReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, string>( + JsonStringReaderWriter.Instance), +elementMapping: SqlServerStringTypeMapping.Default.Clone( + comparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + keyComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), + storeTypePostfix: StoreTypePostfix.None)); +refTypeEnumerable.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var refTypeIList = runtimeEntityType.AddProperty( + "RefTypeIList", + typeof(IList), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("RefTypeIList", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("_refTypeIList", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +refTypeIList.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, string>( + JsonStringReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, string>( + JsonStringReaderWriter.Instance), +elementMapping: SqlServerStringTypeMapping.Default.Clone( + comparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + keyComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), + storeTypePostfix: StoreTypePostfix.None)); +refTypeIList.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var refTypeList = runtimeEntityType.AddProperty( + "RefTypeList", + typeof(List), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("RefTypeList", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("_refTypeList", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +refTypeList.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, IPAddress>( + new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v))))), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, IPAddress>( + new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v)))), +elementMapping: SqlServerStringTypeMapping.Default.Clone( + comparer: new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v), + keyComparer: new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(45)", + size: 45, + dbType: System.Data.DbType.String), + converter: new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v)), + jsonValueReaderWriter: new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v))))); +refTypeList.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var valueTypeArray = runtimeEntityType.AddProperty( + "ValueTypeArray", + typeof(DateTime[]), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("ValueTypeArray", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("_valueTypeArray", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +valueTypeArray.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter( + JsonDateTimeReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter( + JsonDateTimeReaderWriter.Instance), +elementMapping: SqlServerDateTimeTypeMapping.Default.Clone( + comparer: new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v), + keyComparer: new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v), + providerValueComparer: new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v))); +valueTypeArray.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var valueTypeEnumerable = runtimeEntityType.AddProperty( + "ValueTypeEnumerable", + typeof(IEnumerable), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("ValueTypeEnumerable", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("_valueTypeEnumerable", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +valueTypeEnumerable.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, byte>( + JsonByteReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, byte>( + JsonByteReaderWriter.Instance), +elementMapping: SqlServerByteTypeMapping.Default.Clone( + comparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v), + keyComparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v), + providerValueComparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v))); +valueTypeEnumerable.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var valueTypeIList = runtimeEntityType.AddProperty( + "ValueTypeIList", + typeof(IList), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("ValueTypeIList", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +valueTypeIList.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, byte>( + JsonByteReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, byte>( + JsonByteReaderWriter.Instance), +elementMapping: SqlServerByteTypeMapping.Default.Clone( + comparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v), + keyComparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v), + providerValueComparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v))); +valueTypeIList.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var valueTypeList = runtimeEntityType.AddProperty( + "ValueTypeList", + typeof(List), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("ValueTypeList", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("_valueTypeList", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +valueTypeList.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, short>( + JsonInt16ReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, short>( + JsonInt16ReaderWriter.Instance), +elementMapping: SqlServerShortTypeMapping.Default.Clone( + comparer: new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v), + keyComparer: new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v), + providerValueComparer: new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v))); +valueTypeList.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var context = runtimeEntityType.AddServiceProperty( + "Context", + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("Context", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + serviceType: typeof(DbContext)); + +var key = runtimeEntityType.AddKey( + new[] { principalDerivedId, principalDerivedAlternateId, id }); +runtimeEntityType.SetPrimaryKey(key); + +return runtimeEntityType; +} - var key = runtimeEntityType.AddKey( - new[] { principalDerivedId, principalDerivedAlternateId, id }); - runtimeEntityType.SetPrimaryKey(key); +public static RuntimeForeignKey CreateForeignKey1(RuntimeEntityType declaringEntityType, RuntimeEntityType principalEntityType) +{ + var runtimeForeignKey = declaringEntityType.AddForeignKey(new[] { declaringEntityType.FindProperty("PrincipalDerivedId"), declaringEntityType.FindProperty("PrincipalDerivedAlternateId") }, + principalEntityType.FindKey(new[] { principalEntityType.FindProperty("Id"), principalEntityType.FindProperty("AlternateId") }), + principalEntityType, + deleteBehavior: DeleteBehavior.Cascade, + required: true, + ownership: true); + + var manyOwned = principalEntityType.AddNavigation("ManyOwned", + runtimeForeignKey, + onDependent: false, + typeof(ICollection), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalDerived>).GetField("ManyOwned", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + eagerLoaded: true); + + return runtimeForeignKey; +} - return runtimeEntityType; - } - - public static RuntimeForeignKey CreateForeignKey1(RuntimeEntityType declaringEntityType, RuntimeEntityType principalEntityType) - { - var runtimeForeignKey = declaringEntityType.AddForeignKey(new[] { declaringEntityType.FindProperty("PrincipalDerivedId"), declaringEntityType.FindProperty("PrincipalDerivedAlternateId") }, - principalEntityType.FindKey(new[] { principalEntityType.FindProperty("Id"), principalEntityType.FindProperty("AlternateId") }), - principalEntityType, - deleteBehavior: DeleteBehavior.Cascade, - required: true, - ownership: true); - - var manyOwned = principalEntityType.AddNavigation("ManyOwned", - runtimeForeignKey, - onDependent: false, - typeof(ICollection), - fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalDerived>).GetField("ManyOwned", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), - eagerLoaded: true); - - return runtimeForeignKey; - } - - public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) - { - runtimeEntityType.AddAnnotation("Relational:FunctionName", null); - runtimeEntityType.AddAnnotation("Relational:Schema", null); - runtimeEntityType.AddAnnotation("Relational:SqlQuery", null); - runtimeEntityType.AddAnnotation("Relational:TableName", "ManyOwned"); - runtimeEntityType.AddAnnotation("Relational:ViewName", null); - runtimeEntityType.AddAnnotation("Relational:ViewSchema", null); - runtimeEntityType.AddAnnotation("SqlServer:MemoryOptimized", true); - - Customize(runtimeEntityType); - } +public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) +{ + runtimeEntityType.AddAnnotation("Relational:FunctionName", null); + runtimeEntityType.AddAnnotation("Relational:Schema", null); + runtimeEntityType.AddAnnotation("Relational:SqlQuery", null); + runtimeEntityType.AddAnnotation("Relational:TableName", "ManyOwned"); + runtimeEntityType.AddAnnotation("Relational:ViewName", null); + runtimeEntityType.AddAnnotation("Relational:ViewSchema", null); + runtimeEntityType.AddAnnotation("SqlServer:MemoryOptimized", true); + + Customize(runtimeEntityType); +} - static partial void Customize(RuntimeEntityType runtimeEntityType); - } +static partial void Customize(RuntimeEntityType runtimeEntityType); +} } """, c), c => AssertFileContents( @@ -4673,6 +6146,46 @@ private IRelationalModel CreateRelationalModel() IsNullable = true }; microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("Point", pointColumnBase); + var refTypeArrayColumnBase = new ColumnBase("RefTypeArray", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("RefTypeArray", refTypeArrayColumnBase); + var refTypeEnumerableColumnBase = new ColumnBase("RefTypeEnumerable", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("RefTypeEnumerable", refTypeEnumerableColumnBase); + var refTypeIListColumnBase = new ColumnBase("RefTypeIList", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("RefTypeIList", refTypeIListColumnBase); + var refTypeListColumnBase = new ColumnBase("RefTypeList", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("RefTypeList", refTypeListColumnBase); + var valueTypeArrayColumnBase = new ColumnBase("ValueTypeArray", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("ValueTypeArray", valueTypeArrayColumnBase); + var valueTypeEnumerableColumnBase = new ColumnBase("ValueTypeEnumerable", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("ValueTypeEnumerable", valueTypeEnumerableColumnBase); + var valueTypeIListColumnBase = new ColumnBase("ValueTypeIList", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("ValueTypeIList", valueTypeIListColumnBase); + var valueTypeListColumnBase = new ColumnBase("ValueTypeList", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("ValueTypeList", valueTypeListColumnBase); relationalModel.DefaultTables.Add("Microsoft.EntityFrameworkCore.Scaffolding.Internal.CSharpRuntimeModelCodeGeneratorTest+PrincipalBase", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase); var microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase = new TableMappingBase(principalBase, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase, true); microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.AddTypeMapping(microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase, false); @@ -4685,6 +6198,14 @@ private IRelationalModel CreateRelationalModel() RelationalModel.CreateColumnMapping((ColumnBase)flagsEnum1ColumnBase, principalBase.FindProperty("FlagsEnum1")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); RelationalModel.CreateColumnMapping((ColumnBase)flagsEnum2ColumnBase, principalBase.FindProperty("FlagsEnum2")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); RelationalModel.CreateColumnMapping((ColumnBase)pointColumnBase, principalBase.FindProperty("Point")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)refTypeArrayColumnBase, principalBase.FindProperty("RefTypeArray")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)refTypeEnumerableColumnBase, principalBase.FindProperty("RefTypeEnumerable")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)refTypeIListColumnBase, principalBase.FindProperty("RefTypeIList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)refTypeListColumnBase, principalBase.FindProperty("RefTypeList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)valueTypeArrayColumnBase, principalBase.FindProperty("ValueTypeArray")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)valueTypeEnumerableColumnBase, principalBase.FindProperty("ValueTypeEnumerable")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)valueTypeIListColumnBase, principalBase.FindProperty("ValueTypeIList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)valueTypeListColumnBase, principalBase.FindProperty("ValueTypeList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); var tableMappings1 = new List(); principalBase.SetRuntimeAnnotation("Relational:TableMappings", tableMappings1); @@ -4718,6 +6239,46 @@ private IRelationalModel CreateRelationalModel() IsNullable = true }; principalBaseTable.Columns.Add("Point", pointColumn); + var refTypeArrayColumn = new Column("RefTypeArray", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("RefTypeArray", refTypeArrayColumn); + var refTypeEnumerableColumn = new Column("RefTypeEnumerable", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("RefTypeEnumerable", refTypeEnumerableColumn); + var refTypeIListColumn = new Column("RefTypeIList", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("RefTypeIList", refTypeIListColumn); + var refTypeListColumn = new Column("RefTypeList", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("RefTypeList", refTypeListColumn); + var valueTypeArrayColumn = new Column("ValueTypeArray", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("ValueTypeArray", valueTypeArrayColumn); + var valueTypeEnumerableColumn = new Column("ValueTypeEnumerable", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("ValueTypeEnumerable", valueTypeEnumerableColumn); + var valueTypeIListColumn = new Column("ValueTypeIList", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("ValueTypeIList", valueTypeIListColumn); + var valueTypeListColumn = new Column("ValueTypeList", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("ValueTypeList", valueTypeListColumn); var aK_PrincipalBase_Id = new UniqueConstraint("AK_PrincipalBase_Id", principalBaseTable, new[] { idColumn0 }); var aK_PrincipalBase_IdUc = RelationalModel.GetKey(this, "Microsoft.EntityFrameworkCore.Scaffolding.Internal.CSharpRuntimeModelCodeGeneratorTest+PrincipalBase", @@ -4756,6 +6317,14 @@ private IRelationalModel CreateRelationalModel() RelationalModel.CreateColumnMapping(flagsEnum1Column, principalBase.FindProperty("FlagsEnum1")!, principalBaseTableMapping); RelationalModel.CreateColumnMapping(flagsEnum2Column, principalBase.FindProperty("FlagsEnum2")!, principalBaseTableMapping); RelationalModel.CreateColumnMapping(pointColumn, principalBase.FindProperty("Point")!, principalBaseTableMapping); + RelationalModel.CreateColumnMapping(refTypeArrayColumn, principalBase.FindProperty("RefTypeArray")!, principalBaseTableMapping); + RelationalModel.CreateColumnMapping(refTypeEnumerableColumn, principalBase.FindProperty("RefTypeEnumerable")!, principalBaseTableMapping); + RelationalModel.CreateColumnMapping(refTypeIListColumn, principalBase.FindProperty("RefTypeIList")!, principalBaseTableMapping); + RelationalModel.CreateColumnMapping(refTypeListColumn, principalBase.FindProperty("RefTypeList")!, principalBaseTableMapping); + RelationalModel.CreateColumnMapping(valueTypeArrayColumn, principalBase.FindProperty("ValueTypeArray")!, principalBaseTableMapping); + RelationalModel.CreateColumnMapping(valueTypeEnumerableColumn, principalBase.FindProperty("ValueTypeEnumerable")!, principalBaseTableMapping); + RelationalModel.CreateColumnMapping(valueTypeIListColumn, principalBase.FindProperty("ValueTypeIList")!, principalBaseTableMapping); + RelationalModel.CreateColumnMapping(valueTypeListColumn, principalBase.FindProperty("ValueTypeList")!, principalBaseTableMapping); var ownedType = FindEntityType("Microsoft.EntityFrameworkCore.Scaffolding.Internal.CSharpRuntimeModelCodeGeneratorTest+PrincipalBase.Owned#OwnedType")!; @@ -4798,6 +6367,14 @@ private IRelationalModel CreateRelationalModel() RelationalModel.CreateColumnMapping((ColumnBase)flagsEnum1ColumnBase, principalDerived.FindProperty("FlagsEnum1")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0); RelationalModel.CreateColumnMapping((ColumnBase)flagsEnum2ColumnBase, principalDerived.FindProperty("FlagsEnum2")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0); RelationalModel.CreateColumnMapping((ColumnBase)pointColumnBase, principalDerived.FindProperty("Point")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0); + RelationalModel.CreateColumnMapping((ColumnBase)refTypeArrayColumnBase, principalDerived.FindProperty("RefTypeArray")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0); + RelationalModel.CreateColumnMapping((ColumnBase)refTypeEnumerableColumnBase, principalDerived.FindProperty("RefTypeEnumerable")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0); + RelationalModel.CreateColumnMapping((ColumnBase)refTypeIListColumnBase, principalDerived.FindProperty("RefTypeIList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0); + RelationalModel.CreateColumnMapping((ColumnBase)refTypeListColumnBase, principalDerived.FindProperty("RefTypeList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0); + RelationalModel.CreateColumnMapping((ColumnBase)valueTypeArrayColumnBase, principalDerived.FindProperty("ValueTypeArray")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0); + RelationalModel.CreateColumnMapping((ColumnBase)valueTypeEnumerableColumnBase, principalDerived.FindProperty("ValueTypeEnumerable")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0); + RelationalModel.CreateColumnMapping((ColumnBase)valueTypeIListColumnBase, principalDerived.FindProperty("ValueTypeIList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0); + RelationalModel.CreateColumnMapping((ColumnBase)valueTypeListColumnBase, principalDerived.FindProperty("ValueTypeList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0); var tableMappings3 = new List(); principalDerived.SetRuntimeAnnotation("Relational:TableMappings", tableMappings3); @@ -4815,6 +6392,14 @@ private IRelationalModel CreateRelationalModel() RelationalModel.CreateColumnMapping(flagsEnum1Column, principalDerived.FindProperty("FlagsEnum1")!, principalBaseTableMapping1); RelationalModel.CreateColumnMapping(flagsEnum2Column, principalDerived.FindProperty("FlagsEnum2")!, principalBaseTableMapping1); RelationalModel.CreateColumnMapping(pointColumn, principalDerived.FindProperty("Point")!, principalBaseTableMapping1); + RelationalModel.CreateColumnMapping(refTypeArrayColumn, principalDerived.FindProperty("RefTypeArray")!, principalBaseTableMapping1); + RelationalModel.CreateColumnMapping(refTypeEnumerableColumn, principalDerived.FindProperty("RefTypeEnumerable")!, principalBaseTableMapping1); + RelationalModel.CreateColumnMapping(refTypeIListColumn, principalDerived.FindProperty("RefTypeIList")!, principalBaseTableMapping1); + RelationalModel.CreateColumnMapping(refTypeListColumn, principalDerived.FindProperty("RefTypeList")!, principalBaseTableMapping1); + RelationalModel.CreateColumnMapping(valueTypeArrayColumn, principalDerived.FindProperty("ValueTypeArray")!, principalBaseTableMapping1); + RelationalModel.CreateColumnMapping(valueTypeEnumerableColumn, principalDerived.FindProperty("ValueTypeEnumerable")!, principalBaseTableMapping1); + RelationalModel.CreateColumnMapping(valueTypeIListColumn, principalDerived.FindProperty("ValueTypeIList")!, principalBaseTableMapping1); + RelationalModel.CreateColumnMapping(valueTypeListColumn, principalDerived.FindProperty("ValueTypeList")!, principalBaseTableMapping1); var ownedType0 = FindEntityType("Microsoft.EntityFrameworkCore.Scaffolding.Internal.CSharpRuntimeModelCodeGeneratorTest+PrincipalDerived>.ManyOwned#OwnedType")!; @@ -5171,6 +6756,7 @@ public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) // using System; using System.Collections.Generic; +using System.Net; using System.Reflection; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.ChangeTracking; @@ -5399,60 +6985,444 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType bas point.AddAnnotation("Relational:DefaultValue", (NetTopologySuite.Geometries.Point)new NetTopologySuite.IO.WKTReader().Read("SRID=0;POINT Z(0 0 0)")); point.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); - var key = runtimeEntityType.AddKey( - new[] { id }); - - var key0 = runtimeEntityType.AddKey( - new[] { id, alternateId }); - runtimeEntityType.SetPrimaryKey(key0); - key0.AddAnnotation("Relational:Name", "PK"); - - var index = runtimeEntityType.AddIndex( - new[] { alternateId, id }); - - return runtimeEntityType; - } - - public static RuntimeSkipNavigation CreateSkipNavigation1(RuntimeEntityType declaringEntityType, RuntimeEntityType targetEntityType, RuntimeEntityType joinEntityType) - { - var skipNavigation = declaringEntityType.AddSkipNavigation( - "Deriveds", - targetEntityType, - joinEntityType.FindForeignKey( - new[] { joinEntityType.FindProperty("PrincipalsId"), joinEntityType.FindProperty("PrincipalsAlternateId") }, - declaringEntityType.FindKey(new[] { declaringEntityType.FindProperty("Id"), declaringEntityType.FindProperty("AlternateId") }), - declaringEntityType), - true, - false, - typeof(ICollection), - propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("Deriveds", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), - fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); - - var inverse = targetEntityType.FindSkipNavigation("Principals"); - if (inverse != null) - { - skipNavigation.Inverse = inverse; - inverse.Inverse = skipNavigation; - } + var refTypeArray = runtimeEntityType.AddProperty( + "RefTypeArray", + typeof(IPAddress[]), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("RefTypeArray", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); + refTypeArray.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v)), + keyComparer: new ListComparer(new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v)), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), + converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter( + new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v))))), + storeTypePostfix: StoreTypePostfix.None, + jsonValueReaderWriter: new JsonCollectionReaderWriter( + new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v)))), + elementMapping: SqlServerStringTypeMapping.Default.Clone( + comparer: new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v), + keyComparer: new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(45)", + size: 45, + dbType: System.Data.DbType.String), + converter: new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v)), + jsonValueReaderWriter: new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v))))); +refTypeArray.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var refTypeEnumerable = runtimeEntityType.AddProperty( + "RefTypeEnumerable", + typeof(IEnumerable), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("RefTypeEnumerable", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +refTypeEnumerable.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, string>( + JsonStringReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, string>( + JsonStringReaderWriter.Instance), +elementMapping: SqlServerStringTypeMapping.Default.Clone( + comparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + keyComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), + storeTypePostfix: StoreTypePostfix.None)); +refTypeEnumerable.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var refTypeIList = runtimeEntityType.AddProperty( + "RefTypeIList", + typeof(IList), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("RefTypeIList", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +refTypeIList.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, string>( + JsonStringReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, string>( + JsonStringReaderWriter.Instance), +elementMapping: SqlServerStringTypeMapping.Default.Clone( + comparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + keyComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), + storeTypePostfix: StoreTypePostfix.None)); +refTypeIList.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var refTypeList = runtimeEntityType.AddProperty( + "RefTypeList", + typeof(List), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("RefTypeList", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +refTypeList.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, IPAddress>( + new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v))))), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, IPAddress>( + new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v)))), +elementMapping: SqlServerStringTypeMapping.Default.Clone( + comparer: new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v), + keyComparer: new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(45)", + size: 45, + dbType: System.Data.DbType.String), + converter: new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v)), + jsonValueReaderWriter: new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v))))); +refTypeList.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var valueTypeArray = runtimeEntityType.AddProperty( + "ValueTypeArray", + typeof(DateTime[]), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("ValueTypeArray", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +valueTypeArray.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter( + JsonDateTimeReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter( + JsonDateTimeReaderWriter.Instance), +elementMapping: SqlServerDateTimeTypeMapping.Default.Clone( + comparer: new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v), + keyComparer: new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v), + providerValueComparer: new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v))); +valueTypeArray.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var valueTypeEnumerable = runtimeEntityType.AddProperty( + "ValueTypeEnumerable", + typeof(IEnumerable), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("ValueTypeEnumerable", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +valueTypeEnumerable.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, byte>( + JsonByteReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, byte>( + JsonByteReaderWriter.Instance), +elementMapping: SqlServerByteTypeMapping.Default.Clone( + comparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v), + keyComparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v), + providerValueComparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v))); +valueTypeEnumerable.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var valueTypeIList = runtimeEntityType.AddProperty( + "ValueTypeIList", + typeof(IList), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("ValueTypeIList", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +valueTypeIList.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, byte>( + JsonByteReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, byte>( + JsonByteReaderWriter.Instance), +elementMapping: SqlServerByteTypeMapping.Default.Clone( + comparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v), + keyComparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v), + providerValueComparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v))); +valueTypeIList.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var valueTypeList = runtimeEntityType.AddProperty( + "ValueTypeList", + typeof(List), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("ValueTypeList", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +valueTypeList.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, short>( + JsonInt16ReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, short>( + JsonInt16ReaderWriter.Instance), +elementMapping: SqlServerShortTypeMapping.Default.Clone( + comparer: new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v), + keyComparer: new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v), + providerValueComparer: new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v))); +valueTypeList.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var key = runtimeEntityType.AddKey( + new[] { id }); + +var key0 = runtimeEntityType.AddKey( + new[] { id, alternateId }); +runtimeEntityType.SetPrimaryKey(key0); +key0.AddAnnotation("Relational:Name", "PK"); + +var index = runtimeEntityType.AddIndex( + new[] { alternateId, id }); + +return runtimeEntityType; +} - return skipNavigation; - } +public static RuntimeSkipNavigation CreateSkipNavigation1(RuntimeEntityType declaringEntityType, RuntimeEntityType targetEntityType, RuntimeEntityType joinEntityType) +{ + var skipNavigation = declaringEntityType.AddSkipNavigation( + "Deriveds", + targetEntityType, + joinEntityType.FindForeignKey( + new[] { joinEntityType.FindProperty("PrincipalsId"), joinEntityType.FindProperty("PrincipalsAlternateId") }, + declaringEntityType.FindKey(new[] { declaringEntityType.FindProperty("Id"), declaringEntityType.FindProperty("AlternateId") }), + declaringEntityType), + true, + false, + typeof(ICollection), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("Deriveds", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); + + var inverse = targetEntityType.FindSkipNavigation("Principals"); + if (inverse != null) + { + skipNavigation.Inverse = inverse; + inverse.Inverse = skipNavigation; + } - public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) - { - runtimeEntityType.AddAnnotation("Relational:FunctionName", null); - runtimeEntityType.AddAnnotation("Relational:MappingStrategy", "TPH"); - runtimeEntityType.AddAnnotation("Relational:Schema", null); - runtimeEntityType.AddAnnotation("Relational:SqlQuery", null); - runtimeEntityType.AddAnnotation("Relational:TableName", "PrincipalBase"); - runtimeEntityType.AddAnnotation("Relational:ViewName", null); - runtimeEntityType.AddAnnotation("Relational:ViewSchema", null); + return skipNavigation; +} - Customize(runtimeEntityType); - } +public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) +{ + runtimeEntityType.AddAnnotation("Relational:FunctionName", null); + runtimeEntityType.AddAnnotation("Relational:MappingStrategy", "TPH"); + runtimeEntityType.AddAnnotation("Relational:Schema", null); + runtimeEntityType.AddAnnotation("Relational:SqlQuery", null); + runtimeEntityType.AddAnnotation("Relational:TableName", "PrincipalBase"); + runtimeEntityType.AddAnnotation("Relational:ViewName", null); + runtimeEntityType.AddAnnotation("Relational:ViewSchema", null); + + Customize(runtimeEntityType); +} - static partial void Customize(RuntimeEntityType runtimeEntityType); - } +static partial void Customize(RuntimeEntityType runtimeEntityType); +} } """, c), c => AssertFileContents( @@ -5460,6 +7430,8 @@ public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) """ // using System; +using System.Collections.Generic; +using System.Net; using System.Reflection; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.ChangeTracking; @@ -5467,6 +7439,8 @@ public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) using Microsoft.EntityFrameworkCore.Scaffolding.Internal; using Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal; using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.EntityFrameworkCore.Storage.Json; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; #pragma warning disable 219, 612, 618 #nullable disable @@ -5576,56 +7550,448 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType bas (int v) => v)); number.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); - var context = runtimeEntityType.AddServiceProperty( - "Context", - propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("Context", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), - serviceType: typeof(DbContext)); - - var key = runtimeEntityType.AddKey( - new[] { principalBaseId, principalBaseAlternateId }); - runtimeEntityType.SetPrimaryKey(key); - - return runtimeEntityType; - } - - public static RuntimeForeignKey CreateForeignKey1(RuntimeEntityType declaringEntityType, RuntimeEntityType principalEntityType) - { - var runtimeForeignKey = declaringEntityType.AddForeignKey(new[] { declaringEntityType.FindProperty("PrincipalBaseId"), declaringEntityType.FindProperty("PrincipalBaseAlternateId") }, - principalEntityType.FindKey(new[] { principalEntityType.FindProperty("Id"), principalEntityType.FindProperty("AlternateId") }), - principalEntityType, - deleteBehavior: DeleteBehavior.Cascade, - unique: true, - required: true, - requiredDependent: true, - ownership: true); - - var owned = principalEntityType.AddNavigation("Owned", - runtimeForeignKey, - onDependent: false, - typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType), - propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("Owned", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), - fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("_ownedField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + var refTypeArray = runtimeEntityType.AddProperty( + "RefTypeArray", + typeof(IPAddress[]), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("RefTypeArray", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("_refTypeArray", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), propertyAccessMode: PropertyAccessMode.Field, - eagerLoaded: true); - - return runtimeForeignKey; - } + nullable: true); + refTypeArray.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v)), + keyComparer: new ListComparer(new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v)), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), + converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter( + new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v))))), + storeTypePostfix: StoreTypePostfix.None, + jsonValueReaderWriter: new JsonCollectionReaderWriter( + new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v)))), + elementMapping: SqlServerStringTypeMapping.Default.Clone( + comparer: new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v), + keyComparer: new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(45)", + size: 45, + dbType: System.Data.DbType.String), + converter: new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v)), + jsonValueReaderWriter: new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v))))); +refTypeArray.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var refTypeEnumerable = runtimeEntityType.AddProperty( + "RefTypeEnumerable", + typeof(IEnumerable), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("RefTypeEnumerable", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("_refTypeEnumerable", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + propertyAccessMode: PropertyAccessMode.Field, + nullable: true); +refTypeEnumerable.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, string>( + JsonStringReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, string>( + JsonStringReaderWriter.Instance), +elementMapping: SqlServerStringTypeMapping.Default.Clone( + comparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + keyComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), + storeTypePostfix: StoreTypePostfix.None)); +refTypeEnumerable.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var refTypeIList = runtimeEntityType.AddProperty( + "RefTypeIList", + typeof(IList), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("RefTypeIList", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("_refTypeIList", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + propertyAccessMode: PropertyAccessMode.Field, + nullable: true); +refTypeIList.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, string>( + JsonStringReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, string>( + JsonStringReaderWriter.Instance), +elementMapping: SqlServerStringTypeMapping.Default.Clone( + comparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + keyComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), + storeTypePostfix: StoreTypePostfix.None)); +refTypeIList.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var refTypeList = runtimeEntityType.AddProperty( + "RefTypeList", + typeof(List), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("RefTypeList", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("_refTypeList", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + propertyAccessMode: PropertyAccessMode.Field, + nullable: true); +refTypeList.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, IPAddress>( + new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v))))), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, IPAddress>( + new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v)))), +elementMapping: SqlServerStringTypeMapping.Default.Clone( + comparer: new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v), + keyComparer: new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(45)", + size: 45, + dbType: System.Data.DbType.String), + converter: new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v)), + jsonValueReaderWriter: new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v))))); +refTypeList.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var valueTypeArray = runtimeEntityType.AddProperty( + "ValueTypeArray", + typeof(DateTime[]), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("ValueTypeArray", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("_valueTypeArray", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + propertyAccessMode: PropertyAccessMode.Field, + nullable: true); +valueTypeArray.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter( + JsonDateTimeReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter( + JsonDateTimeReaderWriter.Instance), +elementMapping: SqlServerDateTimeTypeMapping.Default.Clone( + comparer: new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v), + keyComparer: new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v), + providerValueComparer: new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v))); +valueTypeArray.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var valueTypeEnumerable = runtimeEntityType.AddProperty( + "ValueTypeEnumerable", + typeof(IEnumerable), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("ValueTypeEnumerable", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("_valueTypeEnumerable", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + propertyAccessMode: PropertyAccessMode.Field, + nullable: true); +valueTypeEnumerable.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, byte>( + JsonByteReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, byte>( + JsonByteReaderWriter.Instance), +elementMapping: SqlServerByteTypeMapping.Default.Clone( + comparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v), + keyComparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v), + providerValueComparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v))); +valueTypeEnumerable.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var valueTypeIList = runtimeEntityType.AddProperty( + "ValueTypeIList", + typeof(IList), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("ValueTypeIList", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + propertyAccessMode: PropertyAccessMode.Field, + nullable: true); +valueTypeIList.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, byte>( + JsonByteReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, byte>( + JsonByteReaderWriter.Instance), +elementMapping: SqlServerByteTypeMapping.Default.Clone( + comparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v), + keyComparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v), + providerValueComparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v))); +valueTypeIList.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var valueTypeList = runtimeEntityType.AddProperty( + "ValueTypeList", + typeof(List), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("ValueTypeList", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("_valueTypeList", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + propertyAccessMode: PropertyAccessMode.Field, + nullable: true); +valueTypeList.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, short>( + JsonInt16ReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, short>( + JsonInt16ReaderWriter.Instance), +elementMapping: SqlServerShortTypeMapping.Default.Clone( + comparer: new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v), + keyComparer: new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v), + providerValueComparer: new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v))); +valueTypeList.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var context = runtimeEntityType.AddServiceProperty( + "Context", + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("Context", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + serviceType: typeof(DbContext)); + +var key = runtimeEntityType.AddKey( + new[] { principalBaseId, principalBaseAlternateId }); +runtimeEntityType.SetPrimaryKey(key); + +return runtimeEntityType; +} - public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) - { - runtimeEntityType.AddAnnotation("Relational:ContainerColumnName", "Owned"); - runtimeEntityType.AddAnnotation("Relational:FunctionName", null); - runtimeEntityType.AddAnnotation("Relational:Schema", null); - runtimeEntityType.AddAnnotation("Relational:SqlQuery", null); - runtimeEntityType.AddAnnotation("Relational:TableName", "PrincipalBase"); - runtimeEntityType.AddAnnotation("Relational:ViewName", null); - runtimeEntityType.AddAnnotation("Relational:ViewSchema", null); +public static RuntimeForeignKey CreateForeignKey1(RuntimeEntityType declaringEntityType, RuntimeEntityType principalEntityType) +{ + var runtimeForeignKey = declaringEntityType.AddForeignKey(new[] { declaringEntityType.FindProperty("PrincipalBaseId"), declaringEntityType.FindProperty("PrincipalBaseAlternateId") }, + principalEntityType.FindKey(new[] { principalEntityType.FindProperty("Id"), principalEntityType.FindProperty("AlternateId") }), + principalEntityType, + deleteBehavior: DeleteBehavior.Cascade, + unique: true, + required: true, + requiredDependent: true, + ownership: true); + + var owned = principalEntityType.AddNavigation("Owned", + runtimeForeignKey, + onDependent: false, + typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("Owned", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("_ownedField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + propertyAccessMode: PropertyAccessMode.Field, + eagerLoaded: true); + + return runtimeForeignKey; +} - Customize(runtimeEntityType); - } +public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) +{ + runtimeEntityType.AddAnnotation("Relational:ContainerColumnName", "Owned"); + runtimeEntityType.AddAnnotation("Relational:FunctionName", null); + runtimeEntityType.AddAnnotation("Relational:Schema", null); + runtimeEntityType.AddAnnotation("Relational:SqlQuery", null); + runtimeEntityType.AddAnnotation("Relational:TableName", "PrincipalBase"); + runtimeEntityType.AddAnnotation("Relational:ViewName", null); + runtimeEntityType.AddAnnotation("Relational:ViewSchema", null); + + Customize(runtimeEntityType); +} - static partial void Customize(RuntimeEntityType runtimeEntityType); - } +static partial void Customize(RuntimeEntityType runtimeEntityType); +} } """, c), c => AssertFileContents( @@ -5634,6 +8000,7 @@ public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) // using System; using System.Collections.Generic; +using System.Net; using System.Reflection; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.ChangeTracking; @@ -5641,6 +8008,8 @@ public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) using Microsoft.EntityFrameworkCore.Scaffolding.Internal; using Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal; using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.EntityFrameworkCore.Storage.Json; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; #pragma warning disable 219, 612, 618 #nullable disable @@ -5766,52 +8135,436 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType bas (int v) => v)); number.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); - var context = runtimeEntityType.AddServiceProperty( - "Context", - propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("Context", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), - serviceType: typeof(DbContext)); - - var key = runtimeEntityType.AddKey( - new[] { principalDerivedId, principalDerivedAlternateId, id }); - runtimeEntityType.SetPrimaryKey(key); - - return runtimeEntityType; - } - - public static RuntimeForeignKey CreateForeignKey1(RuntimeEntityType declaringEntityType, RuntimeEntityType principalEntityType) - { - var runtimeForeignKey = declaringEntityType.AddForeignKey(new[] { declaringEntityType.FindProperty("PrincipalDerivedId"), declaringEntityType.FindProperty("PrincipalDerivedAlternateId") }, - principalEntityType.FindKey(new[] { principalEntityType.FindProperty("Id"), principalEntityType.FindProperty("AlternateId") }), - principalEntityType, - deleteBehavior: DeleteBehavior.Cascade, - required: true, - ownership: true); - - var manyOwned = principalEntityType.AddNavigation("ManyOwned", - runtimeForeignKey, - onDependent: false, - typeof(ICollection), - fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalDerived>).GetField("ManyOwned", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), - eagerLoaded: true); - - return runtimeForeignKey; - } + var refTypeArray = runtimeEntityType.AddProperty( + "RefTypeArray", + typeof(IPAddress[]), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("RefTypeArray", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("_refTypeArray", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); + refTypeArray.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v)), + keyComparer: new ListComparer(new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v)), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), + converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter( + new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v))))), + storeTypePostfix: StoreTypePostfix.None, + jsonValueReaderWriter: new JsonCollectionReaderWriter( + new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v)))), + elementMapping: SqlServerStringTypeMapping.Default.Clone( + comparer: new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v), + keyComparer: new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(45)", + size: 45, + dbType: System.Data.DbType.String), + converter: new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v)), + jsonValueReaderWriter: new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v))))); +refTypeArray.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var refTypeEnumerable = runtimeEntityType.AddProperty( + "RefTypeEnumerable", + typeof(IEnumerable), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("RefTypeEnumerable", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("_refTypeEnumerable", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +refTypeEnumerable.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, string>( + JsonStringReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, string>( + JsonStringReaderWriter.Instance), +elementMapping: SqlServerStringTypeMapping.Default.Clone( + comparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + keyComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), + storeTypePostfix: StoreTypePostfix.None)); +refTypeEnumerable.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var refTypeIList = runtimeEntityType.AddProperty( + "RefTypeIList", + typeof(IList), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("RefTypeIList", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("_refTypeIList", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +refTypeIList.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, string>( + JsonStringReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, string>( + JsonStringReaderWriter.Instance), +elementMapping: SqlServerStringTypeMapping.Default.Clone( + comparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + keyComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), + storeTypePostfix: StoreTypePostfix.None)); +refTypeIList.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var refTypeList = runtimeEntityType.AddProperty( + "RefTypeList", + typeof(List), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("RefTypeList", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("_refTypeList", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +refTypeList.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, IPAddress>( + new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v))))), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, IPAddress>( + new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v)))), +elementMapping: SqlServerStringTypeMapping.Default.Clone( + comparer: new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v), + keyComparer: new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(45)", + size: 45, + dbType: System.Data.DbType.String), + converter: new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v)), + jsonValueReaderWriter: new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v))))); +refTypeList.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var valueTypeArray = runtimeEntityType.AddProperty( + "ValueTypeArray", + typeof(DateTime[]), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("ValueTypeArray", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("_valueTypeArray", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +valueTypeArray.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter( + JsonDateTimeReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter( + JsonDateTimeReaderWriter.Instance), +elementMapping: SqlServerDateTimeTypeMapping.Default.Clone( + comparer: new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v), + keyComparer: new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v), + providerValueComparer: new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v))); +valueTypeArray.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var valueTypeEnumerable = runtimeEntityType.AddProperty( + "ValueTypeEnumerable", + typeof(IEnumerable), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("ValueTypeEnumerable", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("_valueTypeEnumerable", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +valueTypeEnumerable.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, byte>( + JsonByteReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, byte>( + JsonByteReaderWriter.Instance), +elementMapping: SqlServerByteTypeMapping.Default.Clone( + comparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v), + keyComparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v), + providerValueComparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v))); +valueTypeEnumerable.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var valueTypeIList = runtimeEntityType.AddProperty( + "ValueTypeIList", + typeof(IList), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("ValueTypeIList", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +valueTypeIList.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, byte>( + JsonByteReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, byte>( + JsonByteReaderWriter.Instance), +elementMapping: SqlServerByteTypeMapping.Default.Clone( + comparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v), + keyComparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v), + providerValueComparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v))); +valueTypeIList.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var valueTypeList = runtimeEntityType.AddProperty( + "ValueTypeList", + typeof(List), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("ValueTypeList", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("_valueTypeList", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +valueTypeList.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, short>( + JsonInt16ReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, short>( + JsonInt16ReaderWriter.Instance), +elementMapping: SqlServerShortTypeMapping.Default.Clone( + comparer: new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v), + keyComparer: new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v), + providerValueComparer: new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v))); +valueTypeList.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var context = runtimeEntityType.AddServiceProperty( + "Context", + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("Context", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + serviceType: typeof(DbContext)); + +var key = runtimeEntityType.AddKey( + new[] { principalDerivedId, principalDerivedAlternateId, id }); +runtimeEntityType.SetPrimaryKey(key); + +return runtimeEntityType; +} - public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) - { - runtimeEntityType.AddAnnotation("Relational:ContainerColumnName", "ManyOwned"); - runtimeEntityType.AddAnnotation("Relational:FunctionName", null); - runtimeEntityType.AddAnnotation("Relational:Schema", null); - runtimeEntityType.AddAnnotation("Relational:SqlQuery", null); - runtimeEntityType.AddAnnotation("Relational:TableName", "PrincipalBase"); - runtimeEntityType.AddAnnotation("Relational:ViewName", null); - runtimeEntityType.AddAnnotation("Relational:ViewSchema", null); +public static RuntimeForeignKey CreateForeignKey1(RuntimeEntityType declaringEntityType, RuntimeEntityType principalEntityType) +{ + var runtimeForeignKey = declaringEntityType.AddForeignKey(new[] { declaringEntityType.FindProperty("PrincipalDerivedId"), declaringEntityType.FindProperty("PrincipalDerivedAlternateId") }, + principalEntityType.FindKey(new[] { principalEntityType.FindProperty("Id"), principalEntityType.FindProperty("AlternateId") }), + principalEntityType, + deleteBehavior: DeleteBehavior.Cascade, + required: true, + ownership: true); + + var manyOwned = principalEntityType.AddNavigation("ManyOwned", + runtimeForeignKey, + onDependent: false, + typeof(ICollection), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalDerived>).GetField("ManyOwned", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + eagerLoaded: true); + + return runtimeForeignKey; +} - Customize(runtimeEntityType); - } +public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) +{ + runtimeEntityType.AddAnnotation("Relational:ContainerColumnName", "ManyOwned"); + runtimeEntityType.AddAnnotation("Relational:FunctionName", null); + runtimeEntityType.AddAnnotation("Relational:Schema", null); + runtimeEntityType.AddAnnotation("Relational:SqlQuery", null); + runtimeEntityType.AddAnnotation("Relational:TableName", "PrincipalBase"); + runtimeEntityType.AddAnnotation("Relational:ViewName", null); + runtimeEntityType.AddAnnotation("Relational:ViewSchema", null); + + Customize(runtimeEntityType); +} - static partial void Customize(RuntimeEntityType runtimeEntityType); - } +static partial void Customize(RuntimeEntityType runtimeEntityType); +} } """, c), c => AssertFileContents( @@ -6735,6 +9488,46 @@ private IRelationalModel CreateRelationalModel() IsNullable = true }; microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("PrincipalBaseId", principalBaseIdColumnBase); + var refTypeArrayColumnBase = new ColumnBase("RefTypeArray", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("RefTypeArray", refTypeArrayColumnBase); + var refTypeEnumerableColumnBase = new ColumnBase("RefTypeEnumerable", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("RefTypeEnumerable", refTypeEnumerableColumnBase); + var refTypeIListColumnBase = new ColumnBase("RefTypeIList", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("RefTypeIList", refTypeIListColumnBase); + var refTypeListColumnBase = new ColumnBase("RefTypeList", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("RefTypeList", refTypeListColumnBase); + var valueTypeArrayColumnBase = new ColumnBase("ValueTypeArray", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("ValueTypeArray", valueTypeArrayColumnBase); + var valueTypeEnumerableColumnBase = new ColumnBase("ValueTypeEnumerable", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("ValueTypeEnumerable", valueTypeEnumerableColumnBase); + var valueTypeIListColumnBase = new ColumnBase("ValueTypeIList", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("ValueTypeIList", valueTypeIListColumnBase); + var valueTypeListColumnBase = new ColumnBase("ValueTypeList", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("ValueTypeList", valueTypeListColumnBase); relationalModel.DefaultTables.Add("Microsoft.EntityFrameworkCore.Scaffolding.Internal.CSharpRuntimeModelCodeGeneratorTest+PrincipalBase", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase); var microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase = new TableMappingBase(principalBase, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase, true); microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.AddTypeMapping(microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase, false); @@ -6746,6 +9539,14 @@ private IRelationalModel CreateRelationalModel() RelationalModel.CreateColumnMapping((ColumnBase)flagsEnum1ColumnBase, principalBase.FindProperty("FlagsEnum1")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); RelationalModel.CreateColumnMapping((ColumnBase)flagsEnum2ColumnBase, principalBase.FindProperty("FlagsEnum2")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); RelationalModel.CreateColumnMapping((ColumnBase)principalBaseIdColumnBase, principalBase.FindProperty("PrincipalBaseId")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)refTypeArrayColumnBase, principalBase.FindProperty("RefTypeArray")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)refTypeEnumerableColumnBase, principalBase.FindProperty("RefTypeEnumerable")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)refTypeIListColumnBase, principalBase.FindProperty("RefTypeIList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)refTypeListColumnBase, principalBase.FindProperty("RefTypeList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)valueTypeArrayColumnBase, principalBase.FindProperty("ValueTypeArray")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)valueTypeEnumerableColumnBase, principalBase.FindProperty("ValueTypeEnumerable")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)valueTypeIListColumnBase, principalBase.FindProperty("ValueTypeIList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)valueTypeListColumnBase, principalBase.FindProperty("ValueTypeList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); var tableMappings = new List(); principalBase.SetRuntimeAnnotation("Relational:TableMappings", tableMappings); @@ -6790,34 +9591,154 @@ private IRelationalModel CreateRelationalModel() IsNullable = true }; principalBaseTable.Columns.Add("Owned_Principal_Id", owned_Principal_IdColumn); - var principalBaseIdColumn = new Column("PrincipalBaseId", "bigint", principalBaseTable) + var owned_Principal_RefTypeArrayColumn = new Column("Owned_Principal_RefTypeArray", "nvarchar(max)", principalBaseTable) { IsNullable = true }; - principalBaseTable.Columns.Add("PrincipalBaseId", principalBaseIdColumn); - var pK_PrincipalBase = new UniqueConstraint("PK_PrincipalBase", principalBaseTable, new[] { idColumn }); - principalBaseTable.PrimaryKey = pK_PrincipalBase; - var pK_PrincipalBaseUc = RelationalModel.GetKey(this, - "Microsoft.EntityFrameworkCore.Scaffolding.Internal.CSharpRuntimeModelCodeGeneratorTest+PrincipalBase", - new[] { "Id" }); - pK_PrincipalBase.MappedKeys.Add(pK_PrincipalBaseUc); - RelationalModel.GetOrCreateUniqueConstraints(pK_PrincipalBaseUc).Add(pK_PrincipalBase); - principalBaseTable.UniqueConstraints.Add("PK_PrincipalBase", pK_PrincipalBase); - var iX_PrincipalBase_PrincipalBaseId = new TableIndex( - "IX_PrincipalBase_PrincipalBaseId", principalBaseTable, new[] { principalBaseIdColumn }, false); - var iX_PrincipalBase_PrincipalBaseIdIx = RelationalModel.GetIndex(this, - "Microsoft.EntityFrameworkCore.Scaffolding.Internal.CSharpRuntimeModelCodeGeneratorTest+PrincipalBase", - new[] { "PrincipalBaseId" }); - iX_PrincipalBase_PrincipalBaseId.MappedIndexes.Add(iX_PrincipalBase_PrincipalBaseIdIx); - RelationalModel.GetOrCreateTableIndexes(iX_PrincipalBase_PrincipalBaseIdIx).Add(iX_PrincipalBase_PrincipalBaseId); - principalBaseTable.Indexes.Add("IX_PrincipalBase_PrincipalBaseId", iX_PrincipalBase_PrincipalBaseId); - relationalModel.Tables.Add(("PrincipalBase", null), principalBaseTable); - var principalBaseTableMapping = new TableMapping(principalBase, principalBaseTable, true) + principalBaseTable.Columns.Add("Owned_Principal_RefTypeArray", owned_Principal_RefTypeArrayColumn); + var owned_Principal_RefTypeEnumerableColumn = new Column("Owned_Principal_RefTypeEnumerable", "nvarchar(max)", principalBaseTable) { - IsSharedTablePrincipal = true, + IsNullable = true }; - principalBaseTable.AddTypeMapping(principalBaseTableMapping, false); - tableMappings.Add(principalBaseTableMapping); + principalBaseTable.Columns.Add("Owned_Principal_RefTypeEnumerable", owned_Principal_RefTypeEnumerableColumn); + var owned_Principal_RefTypeIListColumn = new Column("Owned_Principal_RefTypeIList", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("Owned_Principal_RefTypeIList", owned_Principal_RefTypeIListColumn); + var owned_Principal_RefTypeListColumn = new Column("Owned_Principal_RefTypeList", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("Owned_Principal_RefTypeList", owned_Principal_RefTypeListColumn); + var owned_Principal_ValueTypeArrayColumn = new Column("Owned_Principal_ValueTypeArray", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("Owned_Principal_ValueTypeArray", owned_Principal_ValueTypeArrayColumn); + var owned_Principal_ValueTypeEnumerableColumn = new Column("Owned_Principal_ValueTypeEnumerable", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("Owned_Principal_ValueTypeEnumerable", owned_Principal_ValueTypeEnumerableColumn); + var owned_Principal_ValueTypeIListColumn = new Column("Owned_Principal_ValueTypeIList", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("Owned_Principal_ValueTypeIList", owned_Principal_ValueTypeIListColumn); + var owned_Principal_ValueTypeListColumn = new Column("Owned_Principal_ValueTypeList", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("Owned_Principal_ValueTypeList", owned_Principal_ValueTypeListColumn); + var owned_RefTypeArrayColumn = new Column("Owned_RefTypeArray", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("Owned_RefTypeArray", owned_RefTypeArrayColumn); + var owned_RefTypeEnumerableColumn = new Column("Owned_RefTypeEnumerable", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("Owned_RefTypeEnumerable", owned_RefTypeEnumerableColumn); + var owned_RefTypeIListColumn = new Column("Owned_RefTypeIList", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("Owned_RefTypeIList", owned_RefTypeIListColumn); + var owned_RefTypeListColumn = new Column("Owned_RefTypeList", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("Owned_RefTypeList", owned_RefTypeListColumn); + var owned_ValueTypeArrayColumn = new Column("Owned_ValueTypeArray", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("Owned_ValueTypeArray", owned_ValueTypeArrayColumn); + var owned_ValueTypeEnumerableColumn = new Column("Owned_ValueTypeEnumerable", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("Owned_ValueTypeEnumerable", owned_ValueTypeEnumerableColumn); + var owned_ValueTypeIListColumn = new Column("Owned_ValueTypeIList", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("Owned_ValueTypeIList", owned_ValueTypeIListColumn); + var owned_ValueTypeListColumn = new Column("Owned_ValueTypeList", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("Owned_ValueTypeList", owned_ValueTypeListColumn); + var principalBaseIdColumn = new Column("PrincipalBaseId", "bigint", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("PrincipalBaseId", principalBaseIdColumn); + var refTypeArrayColumn = new Column("RefTypeArray", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("RefTypeArray", refTypeArrayColumn); + var refTypeEnumerableColumn = new Column("RefTypeEnumerable", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("RefTypeEnumerable", refTypeEnumerableColumn); + var refTypeIListColumn = new Column("RefTypeIList", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("RefTypeIList", refTypeIListColumn); + var refTypeListColumn = new Column("RefTypeList", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("RefTypeList", refTypeListColumn); + var valueTypeArrayColumn = new Column("ValueTypeArray", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("ValueTypeArray", valueTypeArrayColumn); + var valueTypeEnumerableColumn = new Column("ValueTypeEnumerable", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("ValueTypeEnumerable", valueTypeEnumerableColumn); + var valueTypeIListColumn = new Column("ValueTypeIList", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("ValueTypeIList", valueTypeIListColumn); + var valueTypeListColumn = new Column("ValueTypeList", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("ValueTypeList", valueTypeListColumn); + var pK_PrincipalBase = new UniqueConstraint("PK_PrincipalBase", principalBaseTable, new[] { idColumn }); + principalBaseTable.PrimaryKey = pK_PrincipalBase; + var pK_PrincipalBaseUc = RelationalModel.GetKey(this, + "Microsoft.EntityFrameworkCore.Scaffolding.Internal.CSharpRuntimeModelCodeGeneratorTest+PrincipalBase", + new[] { "Id" }); + pK_PrincipalBase.MappedKeys.Add(pK_PrincipalBaseUc); + RelationalModel.GetOrCreateUniqueConstraints(pK_PrincipalBaseUc).Add(pK_PrincipalBase); + principalBaseTable.UniqueConstraints.Add("PK_PrincipalBase", pK_PrincipalBase); + var iX_PrincipalBase_PrincipalBaseId = new TableIndex( + "IX_PrincipalBase_PrincipalBaseId", principalBaseTable, new[] { principalBaseIdColumn }, false); + var iX_PrincipalBase_PrincipalBaseIdIx = RelationalModel.GetIndex(this, + "Microsoft.EntityFrameworkCore.Scaffolding.Internal.CSharpRuntimeModelCodeGeneratorTest+PrincipalBase", + new[] { "PrincipalBaseId" }); + iX_PrincipalBase_PrincipalBaseId.MappedIndexes.Add(iX_PrincipalBase_PrincipalBaseIdIx); + RelationalModel.GetOrCreateTableIndexes(iX_PrincipalBase_PrincipalBaseIdIx).Add(iX_PrincipalBase_PrincipalBaseId); + principalBaseTable.Indexes.Add("IX_PrincipalBase_PrincipalBaseId", iX_PrincipalBase_PrincipalBaseId); + relationalModel.Tables.Add(("PrincipalBase", null), principalBaseTable); + var principalBaseTableMapping = new TableMapping(principalBase, principalBaseTable, true) + { + IsSharedTablePrincipal = true, + }; + principalBaseTable.AddTypeMapping(principalBaseTableMapping, false); + tableMappings.Add(principalBaseTableMapping); RelationalModel.CreateColumnMapping(idColumn, principalBase.FindProperty("Id")!, principalBaseTableMapping); RelationalModel.CreateColumnMapping(discriminatorColumn, principalBase.FindProperty("Discriminator")!, principalBaseTableMapping); RelationalModel.CreateColumnMapping(enum1Column, principalBase.FindProperty("Enum1")!, principalBaseTableMapping); @@ -6825,6 +9746,14 @@ private IRelationalModel CreateRelationalModel() RelationalModel.CreateColumnMapping(flagsEnum1Column, principalBase.FindProperty("FlagsEnum1")!, principalBaseTableMapping); RelationalModel.CreateColumnMapping(flagsEnum2Column, principalBase.FindProperty("FlagsEnum2")!, principalBaseTableMapping); RelationalModel.CreateColumnMapping(principalBaseIdColumn, principalBase.FindProperty("PrincipalBaseId")!, principalBaseTableMapping); + RelationalModel.CreateColumnMapping(refTypeArrayColumn, principalBase.FindProperty("RefTypeArray")!, principalBaseTableMapping); + RelationalModel.CreateColumnMapping(refTypeEnumerableColumn, principalBase.FindProperty("RefTypeEnumerable")!, principalBaseTableMapping); + RelationalModel.CreateColumnMapping(refTypeIListColumn, principalBase.FindProperty("RefTypeIList")!, principalBaseTableMapping); + RelationalModel.CreateColumnMapping(refTypeListColumn, principalBase.FindProperty("RefTypeList")!, principalBaseTableMapping); + RelationalModel.CreateColumnMapping(valueTypeArrayColumn, principalBase.FindProperty("ValueTypeArray")!, principalBaseTableMapping); + RelationalModel.CreateColumnMapping(valueTypeEnumerableColumn, principalBase.FindProperty("ValueTypeEnumerable")!, principalBaseTableMapping); + RelationalModel.CreateColumnMapping(valueTypeIListColumn, principalBase.FindProperty("ValueTypeIList")!, principalBaseTableMapping); + RelationalModel.CreateColumnMapping(valueTypeListColumn, principalBase.FindProperty("ValueTypeList")!, principalBaseTableMapping); var viewMappings = new List(); principalBase.SetRuntimeAnnotation("Relational:ViewMappings", viewMappings); @@ -6849,6 +9778,46 @@ private IRelationalModel CreateRelationalModel() IsNullable = true }; principalBaseViewView.Columns.Add("PrincipalBaseId", principalBaseIdViewColumn); + var refTypeArrayViewColumn = new ViewColumn("RefTypeArray", "nvarchar(max)", principalBaseViewView) + { + IsNullable = true + }; + principalBaseViewView.Columns.Add("RefTypeArray", refTypeArrayViewColumn); + var refTypeEnumerableViewColumn = new ViewColumn("RefTypeEnumerable", "nvarchar(max)", principalBaseViewView) + { + IsNullable = true + }; + principalBaseViewView.Columns.Add("RefTypeEnumerable", refTypeEnumerableViewColumn); + var refTypeIListViewColumn = new ViewColumn("RefTypeIList", "nvarchar(max)", principalBaseViewView) + { + IsNullable = true + }; + principalBaseViewView.Columns.Add("RefTypeIList", refTypeIListViewColumn); + var refTypeListViewColumn = new ViewColumn("RefTypeList", "nvarchar(max)", principalBaseViewView) + { + IsNullable = true + }; + principalBaseViewView.Columns.Add("RefTypeList", refTypeListViewColumn); + var valueTypeArrayViewColumn = new ViewColumn("ValueTypeArray", "nvarchar(max)", principalBaseViewView) + { + IsNullable = true + }; + principalBaseViewView.Columns.Add("ValueTypeArray", valueTypeArrayViewColumn); + var valueTypeEnumerableViewColumn = new ViewColumn("ValueTypeEnumerable", "nvarchar(max)", principalBaseViewView) + { + IsNullable = true + }; + principalBaseViewView.Columns.Add("ValueTypeEnumerable", valueTypeEnumerableViewColumn); + var valueTypeIListViewColumn = new ViewColumn("ValueTypeIList", "nvarchar(max)", principalBaseViewView) + { + IsNullable = true + }; + principalBaseViewView.Columns.Add("ValueTypeIList", valueTypeIListViewColumn); + var valueTypeListViewColumn = new ViewColumn("ValueTypeList", "nvarchar(max)", principalBaseViewView) + { + IsNullable = true + }; + principalBaseViewView.Columns.Add("ValueTypeList", valueTypeListViewColumn); relationalModel.Views.Add(("PrincipalBaseView", null), principalBaseViewView); var principalBaseViewViewMapping = new ViewMapping(principalBase, principalBaseViewView, true); principalBaseViewView.AddTypeMapping(principalBaseViewViewMapping, false); @@ -6860,6 +9829,14 @@ private IRelationalModel CreateRelationalModel() RelationalModel.CreateViewColumnMapping(flagsEnum1ViewColumn, principalBase.FindProperty("FlagsEnum1")!, principalBaseViewViewMapping); RelationalModel.CreateViewColumnMapping(flagsEnum2ViewColumn, principalBase.FindProperty("FlagsEnum2")!, principalBaseViewViewMapping); RelationalModel.CreateViewColumnMapping(principalBaseIdViewColumn, principalBase.FindProperty("PrincipalBaseId")!, principalBaseViewViewMapping); + RelationalModel.CreateViewColumnMapping(refTypeArrayViewColumn, principalBase.FindProperty("RefTypeArray")!, principalBaseViewViewMapping); + RelationalModel.CreateViewColumnMapping(refTypeEnumerableViewColumn, principalBase.FindProperty("RefTypeEnumerable")!, principalBaseViewViewMapping); + RelationalModel.CreateViewColumnMapping(refTypeIListViewColumn, principalBase.FindProperty("RefTypeIList")!, principalBaseViewViewMapping); + RelationalModel.CreateViewColumnMapping(refTypeListViewColumn, principalBase.FindProperty("RefTypeList")!, principalBaseViewViewMapping); + RelationalModel.CreateViewColumnMapping(valueTypeArrayViewColumn, principalBase.FindProperty("ValueTypeArray")!, principalBaseViewViewMapping); + RelationalModel.CreateViewColumnMapping(valueTypeEnumerableViewColumn, principalBase.FindProperty("ValueTypeEnumerable")!, principalBaseViewViewMapping); + RelationalModel.CreateViewColumnMapping(valueTypeIListViewColumn, principalBase.FindProperty("ValueTypeIList")!, principalBaseViewViewMapping); + RelationalModel.CreateViewColumnMapping(valueTypeListViewColumn, principalBase.FindProperty("ValueTypeList")!, principalBaseViewViewMapping); var sqlQueryMappings = new List(); principalBase.SetRuntimeAnnotation("Relational:SqlQueryMappings", sqlQueryMappings); @@ -6884,6 +9861,46 @@ private IRelationalModel CreateRelationalModel() IsNullable = true }; microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQuery.Columns.Add("PrincipalBaseId", principalBaseIdSqlQueryColumn); + var refTypeArraySqlQueryColumn = new SqlQueryColumn("RefTypeArray", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQuery) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQuery.Columns.Add("RefTypeArray", refTypeArraySqlQueryColumn); + var refTypeEnumerableSqlQueryColumn = new SqlQueryColumn("RefTypeEnumerable", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQuery) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQuery.Columns.Add("RefTypeEnumerable", refTypeEnumerableSqlQueryColumn); + var refTypeIListSqlQueryColumn = new SqlQueryColumn("RefTypeIList", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQuery) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQuery.Columns.Add("RefTypeIList", refTypeIListSqlQueryColumn); + var refTypeListSqlQueryColumn = new SqlQueryColumn("RefTypeList", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQuery) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQuery.Columns.Add("RefTypeList", refTypeListSqlQueryColumn); + var valueTypeArraySqlQueryColumn = new SqlQueryColumn("ValueTypeArray", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQuery) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQuery.Columns.Add("ValueTypeArray", valueTypeArraySqlQueryColumn); + var valueTypeEnumerableSqlQueryColumn = new SqlQueryColumn("ValueTypeEnumerable", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQuery) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQuery.Columns.Add("ValueTypeEnumerable", valueTypeEnumerableSqlQueryColumn); + var valueTypeIListSqlQueryColumn = new SqlQueryColumn("ValueTypeIList", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQuery) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQuery.Columns.Add("ValueTypeIList", valueTypeIListSqlQueryColumn); + var valueTypeListSqlQueryColumn = new SqlQueryColumn("ValueTypeList", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQuery) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQuery.Columns.Add("ValueTypeList", valueTypeListSqlQueryColumn); relationalModel.Queries.Add("Microsoft.EntityFrameworkCore.Scaffolding.Internal.CSharpRuntimeModelCodeGeneratorTest+PrincipalBase.MappedSqlQuery", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQuery); var microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQueryMapping = new SqlQueryMapping(principalBase, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQuery, true); microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQuery.AddTypeMapping(microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQueryMapping, false); @@ -6896,6 +9913,14 @@ private IRelationalModel CreateRelationalModel() RelationalModel.CreateSqlQueryColumnMapping(flagsEnum1SqlQueryColumn, principalBase.FindProperty("FlagsEnum1")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQueryMapping); RelationalModel.CreateSqlQueryColumnMapping(flagsEnum2SqlQueryColumn, principalBase.FindProperty("FlagsEnum2")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQueryMapping); RelationalModel.CreateSqlQueryColumnMapping(principalBaseIdSqlQueryColumn, principalBase.FindProperty("PrincipalBaseId")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQueryMapping); + RelationalModel.CreateSqlQueryColumnMapping(refTypeArraySqlQueryColumn, principalBase.FindProperty("RefTypeArray")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQueryMapping); + RelationalModel.CreateSqlQueryColumnMapping(refTypeEnumerableSqlQueryColumn, principalBase.FindProperty("RefTypeEnumerable")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQueryMapping); + RelationalModel.CreateSqlQueryColumnMapping(refTypeIListSqlQueryColumn, principalBase.FindProperty("RefTypeIList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQueryMapping); + RelationalModel.CreateSqlQueryColumnMapping(refTypeListSqlQueryColumn, principalBase.FindProperty("RefTypeList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQueryMapping); + RelationalModel.CreateSqlQueryColumnMapping(valueTypeArraySqlQueryColumn, principalBase.FindProperty("ValueTypeArray")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQueryMapping); + RelationalModel.CreateSqlQueryColumnMapping(valueTypeEnumerableSqlQueryColumn, principalBase.FindProperty("ValueTypeEnumerable")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQueryMapping); + RelationalModel.CreateSqlQueryColumnMapping(valueTypeIListSqlQueryColumn, principalBase.FindProperty("ValueTypeIList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQueryMapping); + RelationalModel.CreateSqlQueryColumnMapping(valueTypeListSqlQueryColumn, principalBase.FindProperty("ValueTypeList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQueryMapping); var functionMappings = new List(); principalBase.SetRuntimeAnnotation("Relational:FunctionMappings", functionMappings); @@ -6921,6 +9946,46 @@ private IRelationalModel CreateRelationalModel() IsNullable = true }; principalBaseTvfFunction.Columns.Add("PrincipalBaseId", principalBaseIdFunctionColumn); + var refTypeArrayFunctionColumn = new FunctionColumn("RefTypeArray", "nvarchar(max)", principalBaseTvfFunction) + { + IsNullable = true + }; + principalBaseTvfFunction.Columns.Add("RefTypeArray", refTypeArrayFunctionColumn); + var refTypeEnumerableFunctionColumn = new FunctionColumn("RefTypeEnumerable", "nvarchar(max)", principalBaseTvfFunction) + { + IsNullable = true + }; + principalBaseTvfFunction.Columns.Add("RefTypeEnumerable", refTypeEnumerableFunctionColumn); + var refTypeIListFunctionColumn = new FunctionColumn("RefTypeIList", "nvarchar(max)", principalBaseTvfFunction) + { + IsNullable = true + }; + principalBaseTvfFunction.Columns.Add("RefTypeIList", refTypeIListFunctionColumn); + var refTypeListFunctionColumn = new FunctionColumn("RefTypeList", "nvarchar(max)", principalBaseTvfFunction) + { + IsNullable = true + }; + principalBaseTvfFunction.Columns.Add("RefTypeList", refTypeListFunctionColumn); + var valueTypeArrayFunctionColumn = new FunctionColumn("ValueTypeArray", "nvarchar(max)", principalBaseTvfFunction) + { + IsNullable = true + }; + principalBaseTvfFunction.Columns.Add("ValueTypeArray", valueTypeArrayFunctionColumn); + var valueTypeEnumerableFunctionColumn = new FunctionColumn("ValueTypeEnumerable", "nvarchar(max)", principalBaseTvfFunction) + { + IsNullable = true + }; + principalBaseTvfFunction.Columns.Add("ValueTypeEnumerable", valueTypeEnumerableFunctionColumn); + var valueTypeIListFunctionColumn = new FunctionColumn("ValueTypeIList", "nvarchar(max)", principalBaseTvfFunction) + { + IsNullable = true + }; + principalBaseTvfFunction.Columns.Add("ValueTypeIList", valueTypeIListFunctionColumn); + var valueTypeListFunctionColumn = new FunctionColumn("ValueTypeList", "nvarchar(max)", principalBaseTvfFunction) + { + IsNullable = true + }; + principalBaseTvfFunction.Columns.Add("ValueTypeList", valueTypeListFunctionColumn); relationalModel.Functions.Add( ("PrincipalBaseTvf", "dbo", new string[0]), principalBaseTvfFunction); @@ -6935,6 +10000,14 @@ private IRelationalModel CreateRelationalModel() RelationalModel.CreateFunctionColumnMapping(flagsEnum1FunctionColumn, principalBase.FindProperty("FlagsEnum1")!, principalBaseTvfFunctionMapping); RelationalModel.CreateFunctionColumnMapping(flagsEnum2FunctionColumn, principalBase.FindProperty("FlagsEnum2")!, principalBaseTvfFunctionMapping); RelationalModel.CreateFunctionColumnMapping(principalBaseIdFunctionColumn, principalBase.FindProperty("PrincipalBaseId")!, principalBaseTvfFunctionMapping); + RelationalModel.CreateFunctionColumnMapping(refTypeArrayFunctionColumn, principalBase.FindProperty("RefTypeArray")!, principalBaseTvfFunctionMapping); + RelationalModel.CreateFunctionColumnMapping(refTypeEnumerableFunctionColumn, principalBase.FindProperty("RefTypeEnumerable")!, principalBaseTvfFunctionMapping); + RelationalModel.CreateFunctionColumnMapping(refTypeIListFunctionColumn, principalBase.FindProperty("RefTypeIList")!, principalBaseTvfFunctionMapping); + RelationalModel.CreateFunctionColumnMapping(refTypeListFunctionColumn, principalBase.FindProperty("RefTypeList")!, principalBaseTvfFunctionMapping); + RelationalModel.CreateFunctionColumnMapping(valueTypeArrayFunctionColumn, principalBase.FindProperty("ValueTypeArray")!, principalBaseTvfFunctionMapping); + RelationalModel.CreateFunctionColumnMapping(valueTypeEnumerableFunctionColumn, principalBase.FindProperty("ValueTypeEnumerable")!, principalBaseTvfFunctionMapping); + RelationalModel.CreateFunctionColumnMapping(valueTypeIListFunctionColumn, principalBase.FindProperty("ValueTypeIList")!, principalBaseTvfFunctionMapping); + RelationalModel.CreateFunctionColumnMapping(valueTypeListFunctionColumn, principalBase.FindProperty("ValueTypeList")!, principalBaseTvfFunctionMapping); var deleteSprocMappings = new List(); principalBase.SetRuntimeAnnotation("Relational:DeleteStoredProcedureMappings", deleteSprocMappings); @@ -6970,9 +10043,49 @@ private IRelationalModel CreateRelationalModel() principalBase_InsertStoreSproc.AddParameter(flagsEnum1Parameter); var flagsEnum2Parameter = new StoreStoredProcedureParameter("FlagsEnum2", "int", 4, principalBase_InsertStoreSproc, System.Data.ParameterDirection.Input); principalBase_InsertStoreSproc.AddParameter(flagsEnum2Parameter); - var discriminatorParameter = new StoreStoredProcedureParameter("Discriminator", "nvarchar(55)", 5, principalBase_InsertStoreSproc, System.Data.ParameterDirection.Input); + var valueTypeListParameter = new StoreStoredProcedureParameter("ValueTypeList", "nvarchar(max)", 5, principalBase_InsertStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + principalBase_InsertStoreSproc.AddParameter(valueTypeListParameter); + var valueTypeIListParameter = new StoreStoredProcedureParameter("ValueTypeIList", "nvarchar(max)", 6, principalBase_InsertStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + principalBase_InsertStoreSproc.AddParameter(valueTypeIListParameter); + var valueTypeArrayParameter = new StoreStoredProcedureParameter("ValueTypeArray", "nvarchar(max)", 7, principalBase_InsertStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + principalBase_InsertStoreSproc.AddParameter(valueTypeArrayParameter); + var valueTypeEnumerableParameter = new StoreStoredProcedureParameter("ValueTypeEnumerable", "nvarchar(max)", 8, principalBase_InsertStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + principalBase_InsertStoreSproc.AddParameter(valueTypeEnumerableParameter); + var refTypeListParameter = new StoreStoredProcedureParameter("RefTypeList", "nvarchar(max)", 9, principalBase_InsertStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + principalBase_InsertStoreSproc.AddParameter(refTypeListParameter); + var refTypeIListParameter = new StoreStoredProcedureParameter("RefTypeIList", "nvarchar(max)", 10, principalBase_InsertStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + principalBase_InsertStoreSproc.AddParameter(refTypeIListParameter); + var refTypeArrayParameter = new StoreStoredProcedureParameter("RefTypeArray", "nvarchar(max)", 11, principalBase_InsertStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + principalBase_InsertStoreSproc.AddParameter(refTypeArrayParameter); + var refTypeEnumerableParameter = new StoreStoredProcedureParameter("RefTypeEnumerable", "nvarchar(max)", 12, principalBase_InsertStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + principalBase_InsertStoreSproc.AddParameter(refTypeEnumerableParameter); + var discriminatorParameter = new StoreStoredProcedureParameter("Discriminator", "nvarchar(55)", 13, principalBase_InsertStoreSproc, System.Data.ParameterDirection.Input); principalBase_InsertStoreSproc.AddParameter(discriminatorParameter); - var idParameter = new StoreStoredProcedureParameter("Id", "bigint", 6, principalBase_InsertStoreSproc, System.Data.ParameterDirection.Output); + var idParameter = new StoreStoredProcedureParameter("Id", "bigint", 14, principalBase_InsertStoreSproc, System.Data.ParameterDirection.Output); principalBase_InsertStoreSproc.AddParameter(idParameter); principalBase_InsertStoreSproc.AddStoredProcedure((IRuntimeStoredProcedure)principalBase.GetInsertStoredProcedure()!); relationalModel.StoredProcedures.Add(("PrincipalBase_Insert", null), principalBase_InsertStoreSproc); @@ -6988,6 +10101,14 @@ private IRelationalModel CreateRelationalModel() RelationalModel.CreateStoredProcedureParameterMapping(flagsEnum1Parameter, principalBase_InsertISproc.FindParameter("FlagsEnum1")!, principalBase.FindProperty("FlagsEnum1")!, principalBase_InsertSprocMapping); RelationalModel.CreateStoredProcedureParameterMapping(flagsEnum2Parameter, principalBase_InsertISproc.FindParameter("FlagsEnum2")!, principalBase.FindProperty("FlagsEnum2")!, principalBase_InsertSprocMapping); RelationalModel.CreateStoredProcedureParameterMapping(principalBaseIdParameter, principalBase_InsertISproc.FindParameter("PrincipalBaseId")!, principalBase.FindProperty("PrincipalBaseId")!, principalBase_InsertSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(refTypeArrayParameter, principalBase_InsertISproc.FindParameter("RefTypeArray")!, principalBase.FindProperty("RefTypeArray")!, principalBase_InsertSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(refTypeEnumerableParameter, principalBase_InsertISproc.FindParameter("RefTypeEnumerable")!, principalBase.FindProperty("RefTypeEnumerable")!, principalBase_InsertSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(refTypeIListParameter, principalBase_InsertISproc.FindParameter("RefTypeIList")!, principalBase.FindProperty("RefTypeIList")!, principalBase_InsertSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(refTypeListParameter, principalBase_InsertISproc.FindParameter("RefTypeList")!, principalBase.FindProperty("RefTypeList")!, principalBase_InsertSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(valueTypeArrayParameter, principalBase_InsertISproc.FindParameter("ValueTypeArray")!, principalBase.FindProperty("ValueTypeArray")!, principalBase_InsertSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(valueTypeEnumerableParameter, principalBase_InsertISproc.FindParameter("ValueTypeEnumerable")!, principalBase.FindProperty("ValueTypeEnumerable")!, principalBase_InsertSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(valueTypeIListParameter, principalBase_InsertISproc.FindParameter("ValueTypeIList")!, principalBase.FindProperty("ValueTypeIList")!, principalBase_InsertSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(valueTypeListParameter, principalBase_InsertISproc.FindParameter("ValueTypeList")!, principalBase.FindProperty("ValueTypeList")!, principalBase_InsertSprocMapping); var updateSprocMappings = new List(); principalBase.SetRuntimeAnnotation("Relational:UpdateStoredProcedureMappings", updateSprocMappings); @@ -7008,7 +10129,47 @@ private IRelationalModel CreateRelationalModel() principalBase_UpdateStoreSproc.AddParameter(flagsEnum1Parameter0); var flagsEnum2Parameter0 = new StoreStoredProcedureParameter("FlagsEnum2", "int", 4, principalBase_UpdateStoreSproc, System.Data.ParameterDirection.Input); principalBase_UpdateStoreSproc.AddParameter(flagsEnum2Parameter0); - var id_OriginalParameter0 = new StoreStoredProcedureParameter("Id_Original", "bigint", 5, principalBase_UpdateStoreSproc, System.Data.ParameterDirection.Input); + var valueTypeListParameter0 = new StoreStoredProcedureParameter("ValueTypeList", "nvarchar(max)", 5, principalBase_UpdateStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + principalBase_UpdateStoreSproc.AddParameter(valueTypeListParameter0); + var valueTypeIListParameter0 = new StoreStoredProcedureParameter("ValueTypeIList", "nvarchar(max)", 6, principalBase_UpdateStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + principalBase_UpdateStoreSproc.AddParameter(valueTypeIListParameter0); + var valueTypeArrayParameter0 = new StoreStoredProcedureParameter("ValueTypeArray", "nvarchar(max)", 7, principalBase_UpdateStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + principalBase_UpdateStoreSproc.AddParameter(valueTypeArrayParameter0); + var valueTypeEnumerableParameter0 = new StoreStoredProcedureParameter("ValueTypeEnumerable", "nvarchar(max)", 8, principalBase_UpdateStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + principalBase_UpdateStoreSproc.AddParameter(valueTypeEnumerableParameter0); + var refTypeListParameter0 = new StoreStoredProcedureParameter("RefTypeList", "nvarchar(max)", 9, principalBase_UpdateStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + principalBase_UpdateStoreSproc.AddParameter(refTypeListParameter0); + var refTypeIListParameter0 = new StoreStoredProcedureParameter("RefTypeIList", "nvarchar(max)", 10, principalBase_UpdateStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + principalBase_UpdateStoreSproc.AddParameter(refTypeIListParameter0); + var refTypeArrayParameter0 = new StoreStoredProcedureParameter("RefTypeArray", "nvarchar(max)", 11, principalBase_UpdateStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + principalBase_UpdateStoreSproc.AddParameter(refTypeArrayParameter0); + var refTypeEnumerableParameter0 = new StoreStoredProcedureParameter("RefTypeEnumerable", "nvarchar(max)", 12, principalBase_UpdateStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + principalBase_UpdateStoreSproc.AddParameter(refTypeEnumerableParameter0); + var id_OriginalParameter0 = new StoreStoredProcedureParameter("Id_Original", "bigint", 13, principalBase_UpdateStoreSproc, System.Data.ParameterDirection.Input); principalBase_UpdateStoreSproc.AddParameter(id_OriginalParameter0); principalBase_UpdateStoreSproc.AddStoredProcedure((IRuntimeStoredProcedure)principalBase.GetUpdateStoredProcedure()!); relationalModel.StoredProcedures.Add(("PrincipalBase_Update", null), principalBase_UpdateStoreSproc); @@ -7023,6 +10184,14 @@ private IRelationalModel CreateRelationalModel() RelationalModel.CreateStoredProcedureParameterMapping(flagsEnum1Parameter0, principalBase_UpdateUSproc.FindParameter("FlagsEnum1")!, principalBase.FindProperty("FlagsEnum1")!, principalBase_UpdateSprocMapping); RelationalModel.CreateStoredProcedureParameterMapping(flagsEnum2Parameter0, principalBase_UpdateUSproc.FindParameter("FlagsEnum2")!, principalBase.FindProperty("FlagsEnum2")!, principalBase_UpdateSprocMapping); RelationalModel.CreateStoredProcedureParameterMapping(principalBaseIdParameter0, principalBase_UpdateUSproc.FindParameter("PrincipalBaseId")!, principalBase.FindProperty("PrincipalBaseId")!, principalBase_UpdateSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(refTypeArrayParameter0, principalBase_UpdateUSproc.FindParameter("RefTypeArray")!, principalBase.FindProperty("RefTypeArray")!, principalBase_UpdateSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(refTypeEnumerableParameter0, principalBase_UpdateUSproc.FindParameter("RefTypeEnumerable")!, principalBase.FindProperty("RefTypeEnumerable")!, principalBase_UpdateSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(refTypeIListParameter0, principalBase_UpdateUSproc.FindParameter("RefTypeIList")!, principalBase.FindProperty("RefTypeIList")!, principalBase_UpdateSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(refTypeListParameter0, principalBase_UpdateUSproc.FindParameter("RefTypeList")!, principalBase.FindProperty("RefTypeList")!, principalBase_UpdateSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(valueTypeArrayParameter0, principalBase_UpdateUSproc.FindParameter("ValueTypeArray")!, principalBase.FindProperty("ValueTypeArray")!, principalBase_UpdateSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(valueTypeEnumerableParameter0, principalBase_UpdateUSproc.FindParameter("ValueTypeEnumerable")!, principalBase.FindProperty("ValueTypeEnumerable")!, principalBase_UpdateSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(valueTypeIListParameter0, principalBase_UpdateUSproc.FindParameter("ValueTypeIList")!, principalBase.FindProperty("ValueTypeIList")!, principalBase_UpdateSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(valueTypeListParameter0, principalBase_UpdateUSproc.FindParameter("ValueTypeList")!, principalBase.FindProperty("ValueTypeList")!, principalBase_UpdateSprocMapping); var ownedType = principalBase.FindComplexProperty("Owned")!.ComplexType; @@ -7033,6 +10202,14 @@ private IRelationalModel CreateRelationalModel() tableMappings0.Add(principalBaseTableMapping0); RelationalModel.CreateColumnMapping(deetsColumn, ownedType.FindProperty("Details")!, principalBaseTableMapping0); RelationalModel.CreateColumnMapping(owned_NumberColumn, ownedType.FindProperty("Number")!, principalBaseTableMapping0); + RelationalModel.CreateColumnMapping(owned_RefTypeArrayColumn, ownedType.FindProperty("RefTypeArray")!, principalBaseTableMapping0); + RelationalModel.CreateColumnMapping(owned_RefTypeEnumerableColumn, ownedType.FindProperty("RefTypeEnumerable")!, principalBaseTableMapping0); + RelationalModel.CreateColumnMapping(owned_RefTypeIListColumn, ownedType.FindProperty("RefTypeIList")!, principalBaseTableMapping0); + RelationalModel.CreateColumnMapping(owned_RefTypeListColumn, ownedType.FindProperty("RefTypeList")!, principalBaseTableMapping0); + RelationalModel.CreateColumnMapping(owned_ValueTypeArrayColumn, ownedType.FindProperty("ValueTypeArray")!, principalBaseTableMapping0); + RelationalModel.CreateColumnMapping(owned_ValueTypeEnumerableColumn, ownedType.FindProperty("ValueTypeEnumerable")!, principalBaseTableMapping0); + RelationalModel.CreateColumnMapping(owned_ValueTypeIListColumn, ownedType.FindProperty("ValueTypeIList")!, principalBaseTableMapping0); + RelationalModel.CreateColumnMapping(owned_ValueTypeListColumn, ownedType.FindProperty("ValueTypeList")!, principalBaseTableMapping0); var principalBase0 = ownedType.FindComplexProperty("Principal")!.ComplexType; @@ -7047,6 +10224,14 @@ private IRelationalModel CreateRelationalModel() RelationalModel.CreateColumnMapping(owned_Principal_FlagsEnum1Column, principalBase0.FindProperty("FlagsEnum1")!, principalBaseTableMapping1); RelationalModel.CreateColumnMapping(owned_Principal_FlagsEnum2Column, principalBase0.FindProperty("FlagsEnum2")!, principalBaseTableMapping1); RelationalModel.CreateColumnMapping(owned_Principal_IdColumn, principalBase0.FindProperty("Id")!, principalBaseTableMapping1); + RelationalModel.CreateColumnMapping(owned_Principal_RefTypeArrayColumn, principalBase0.FindProperty("RefTypeArray")!, principalBaseTableMapping1); + RelationalModel.CreateColumnMapping(owned_Principal_RefTypeEnumerableColumn, principalBase0.FindProperty("RefTypeEnumerable")!, principalBaseTableMapping1); + RelationalModel.CreateColumnMapping(owned_Principal_RefTypeIListColumn, principalBase0.FindProperty("RefTypeIList")!, principalBaseTableMapping1); + RelationalModel.CreateColumnMapping(owned_Principal_RefTypeListColumn, principalBase0.FindProperty("RefTypeList")!, principalBaseTableMapping1); + RelationalModel.CreateColumnMapping(owned_Principal_ValueTypeArrayColumn, principalBase0.FindProperty("ValueTypeArray")!, principalBaseTableMapping1); + RelationalModel.CreateColumnMapping(owned_Principal_ValueTypeEnumerableColumn, principalBase0.FindProperty("ValueTypeEnumerable")!, principalBaseTableMapping1); + RelationalModel.CreateColumnMapping(owned_Principal_ValueTypeIListColumn, principalBase0.FindProperty("ValueTypeIList")!, principalBaseTableMapping1); + RelationalModel.CreateColumnMapping(owned_Principal_ValueTypeListColumn, principalBase0.FindProperty("ValueTypeList")!, principalBaseTableMapping1); var principalDerived = FindEntityType("Microsoft.EntityFrameworkCore.Scaffolding.Internal.CSharpRuntimeModelCodeGeneratorTest+PrincipalDerived>")!; @@ -7062,6 +10247,14 @@ private IRelationalModel CreateRelationalModel() RelationalModel.CreateColumnMapping((ColumnBase)flagsEnum1ColumnBase, principalDerived.FindProperty("FlagsEnum1")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0); RelationalModel.CreateColumnMapping((ColumnBase)flagsEnum2ColumnBase, principalDerived.FindProperty("FlagsEnum2")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0); RelationalModel.CreateColumnMapping((ColumnBase)principalBaseIdColumnBase, principalDerived.FindProperty("PrincipalBaseId")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0); + RelationalModel.CreateColumnMapping((ColumnBase)refTypeArrayColumnBase, principalDerived.FindProperty("RefTypeArray")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0); + RelationalModel.CreateColumnMapping((ColumnBase)refTypeEnumerableColumnBase, principalDerived.FindProperty("RefTypeEnumerable")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0); + RelationalModel.CreateColumnMapping((ColumnBase)refTypeIListColumnBase, principalDerived.FindProperty("RefTypeIList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0); + RelationalModel.CreateColumnMapping((ColumnBase)refTypeListColumnBase, principalDerived.FindProperty("RefTypeList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0); + RelationalModel.CreateColumnMapping((ColumnBase)valueTypeArrayColumnBase, principalDerived.FindProperty("ValueTypeArray")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0); + RelationalModel.CreateColumnMapping((ColumnBase)valueTypeEnumerableColumnBase, principalDerived.FindProperty("ValueTypeEnumerable")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0); + RelationalModel.CreateColumnMapping((ColumnBase)valueTypeIListColumnBase, principalDerived.FindProperty("ValueTypeIList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0); + RelationalModel.CreateColumnMapping((ColumnBase)valueTypeListColumnBase, principalDerived.FindProperty("ValueTypeList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0); var tableMappings2 = new List(); principalDerived.SetRuntimeAnnotation("Relational:TableMappings", tableMappings2); @@ -7078,6 +10271,14 @@ private IRelationalModel CreateRelationalModel() RelationalModel.CreateColumnMapping(flagsEnum1Column, principalDerived.FindProperty("FlagsEnum1")!, principalBaseTableMapping2); RelationalModel.CreateColumnMapping(flagsEnum2Column, principalDerived.FindProperty("FlagsEnum2")!, principalBaseTableMapping2); RelationalModel.CreateColumnMapping(principalBaseIdColumn, principalDerived.FindProperty("PrincipalBaseId")!, principalBaseTableMapping2); + RelationalModel.CreateColumnMapping(refTypeArrayColumn, principalDerived.FindProperty("RefTypeArray")!, principalBaseTableMapping2); + RelationalModel.CreateColumnMapping(refTypeEnumerableColumn, principalDerived.FindProperty("RefTypeEnumerable")!, principalBaseTableMapping2); + RelationalModel.CreateColumnMapping(refTypeIListColumn, principalDerived.FindProperty("RefTypeIList")!, principalBaseTableMapping2); + RelationalModel.CreateColumnMapping(refTypeListColumn, principalDerived.FindProperty("RefTypeList")!, principalBaseTableMapping2); + RelationalModel.CreateColumnMapping(valueTypeArrayColumn, principalDerived.FindProperty("ValueTypeArray")!, principalBaseTableMapping2); + RelationalModel.CreateColumnMapping(valueTypeEnumerableColumn, principalDerived.FindProperty("ValueTypeEnumerable")!, principalBaseTableMapping2); + RelationalModel.CreateColumnMapping(valueTypeIListColumn, principalDerived.FindProperty("ValueTypeIList")!, principalBaseTableMapping2); + RelationalModel.CreateColumnMapping(valueTypeListColumn, principalDerived.FindProperty("ValueTypeList")!, principalBaseTableMapping2); var sqlQueryMappings0 = new List(); principalDerived.SetRuntimeAnnotation("Relational:SqlQueryMappings", sqlQueryMappings0); @@ -7092,6 +10293,14 @@ private IRelationalModel CreateRelationalModel() RelationalModel.CreateSqlQueryColumnMapping(flagsEnum1SqlQueryColumn, principalDerived.FindProperty("FlagsEnum1")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQueryMapping0); RelationalModel.CreateSqlQueryColumnMapping(flagsEnum2SqlQueryColumn, principalDerived.FindProperty("FlagsEnum2")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQueryMapping0); RelationalModel.CreateSqlQueryColumnMapping(principalBaseIdSqlQueryColumn, principalDerived.FindProperty("PrincipalBaseId")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQueryMapping0); + RelationalModel.CreateSqlQueryColumnMapping(refTypeArraySqlQueryColumn, principalDerived.FindProperty("RefTypeArray")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQueryMapping0); + RelationalModel.CreateSqlQueryColumnMapping(refTypeEnumerableSqlQueryColumn, principalDerived.FindProperty("RefTypeEnumerable")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQueryMapping0); + RelationalModel.CreateSqlQueryColumnMapping(refTypeIListSqlQueryColumn, principalDerived.FindProperty("RefTypeIList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQueryMapping0); + RelationalModel.CreateSqlQueryColumnMapping(refTypeListSqlQueryColumn, principalDerived.FindProperty("RefTypeList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQueryMapping0); + RelationalModel.CreateSqlQueryColumnMapping(valueTypeArraySqlQueryColumn, principalDerived.FindProperty("ValueTypeArray")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQueryMapping0); + RelationalModel.CreateSqlQueryColumnMapping(valueTypeEnumerableSqlQueryColumn, principalDerived.FindProperty("ValueTypeEnumerable")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQueryMapping0); + RelationalModel.CreateSqlQueryColumnMapping(valueTypeIListSqlQueryColumn, principalDerived.FindProperty("ValueTypeIList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQueryMapping0); + RelationalModel.CreateSqlQueryColumnMapping(valueTypeListSqlQueryColumn, principalDerived.FindProperty("ValueTypeList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappedSqlQuerySqlQueryMapping0); var deleteSprocMappings0 = new List(); principalDerived.SetRuntimeAnnotation("Relational:DeleteStoredProcedureMappings", deleteSprocMappings0); @@ -7116,6 +10325,14 @@ private IRelationalModel CreateRelationalModel() RelationalModel.CreateStoredProcedureParameterMapping(flagsEnum1Parameter, principalBase_InsertISproc0.FindParameter("FlagsEnum1")!, principalDerived.FindProperty("FlagsEnum1")!, principalBase_InsertSprocMapping0); RelationalModel.CreateStoredProcedureParameterMapping(flagsEnum2Parameter, principalBase_InsertISproc0.FindParameter("FlagsEnum2")!, principalDerived.FindProperty("FlagsEnum2")!, principalBase_InsertSprocMapping0); RelationalModel.CreateStoredProcedureParameterMapping(principalBaseIdParameter, principalBase_InsertISproc0.FindParameter("PrincipalBaseId")!, principalDerived.FindProperty("PrincipalBaseId")!, principalBase_InsertSprocMapping0); + RelationalModel.CreateStoredProcedureParameterMapping(refTypeArrayParameter, principalBase_InsertISproc0.FindParameter("RefTypeArray")!, principalDerived.FindProperty("RefTypeArray")!, principalBase_InsertSprocMapping0); + RelationalModel.CreateStoredProcedureParameterMapping(refTypeEnumerableParameter, principalBase_InsertISproc0.FindParameter("RefTypeEnumerable")!, principalDerived.FindProperty("RefTypeEnumerable")!, principalBase_InsertSprocMapping0); + RelationalModel.CreateStoredProcedureParameterMapping(refTypeIListParameter, principalBase_InsertISproc0.FindParameter("RefTypeIList")!, principalDerived.FindProperty("RefTypeIList")!, principalBase_InsertSprocMapping0); + RelationalModel.CreateStoredProcedureParameterMapping(refTypeListParameter, principalBase_InsertISproc0.FindParameter("RefTypeList")!, principalDerived.FindProperty("RefTypeList")!, principalBase_InsertSprocMapping0); + RelationalModel.CreateStoredProcedureParameterMapping(valueTypeArrayParameter, principalBase_InsertISproc0.FindParameter("ValueTypeArray")!, principalDerived.FindProperty("ValueTypeArray")!, principalBase_InsertSprocMapping0); + RelationalModel.CreateStoredProcedureParameterMapping(valueTypeEnumerableParameter, principalBase_InsertISproc0.FindParameter("ValueTypeEnumerable")!, principalDerived.FindProperty("ValueTypeEnumerable")!, principalBase_InsertSprocMapping0); + RelationalModel.CreateStoredProcedureParameterMapping(valueTypeIListParameter, principalBase_InsertISproc0.FindParameter("ValueTypeIList")!, principalDerived.FindProperty("ValueTypeIList")!, principalBase_InsertSprocMapping0); + RelationalModel.CreateStoredProcedureParameterMapping(valueTypeListParameter, principalBase_InsertISproc0.FindParameter("ValueTypeList")!, principalDerived.FindProperty("ValueTypeList")!, principalBase_InsertSprocMapping0); var updateSprocMappings0 = new List(); principalDerived.SetRuntimeAnnotation("Relational:UpdateStoredProcedureMappings", updateSprocMappings0); @@ -7130,6 +10347,14 @@ private IRelationalModel CreateRelationalModel() RelationalModel.CreateStoredProcedureParameterMapping(flagsEnum1Parameter0, principalBase_UpdateUSproc0.FindParameter("FlagsEnum1")!, principalDerived.FindProperty("FlagsEnum1")!, principalBase_UpdateSprocMapping0); RelationalModel.CreateStoredProcedureParameterMapping(flagsEnum2Parameter0, principalBase_UpdateUSproc0.FindParameter("FlagsEnum2")!, principalDerived.FindProperty("FlagsEnum2")!, principalBase_UpdateSprocMapping0); RelationalModel.CreateStoredProcedureParameterMapping(principalBaseIdParameter0, principalBase_UpdateUSproc0.FindParameter("PrincipalBaseId")!, principalDerived.FindProperty("PrincipalBaseId")!, principalBase_UpdateSprocMapping0); + RelationalModel.CreateStoredProcedureParameterMapping(refTypeArrayParameter0, principalBase_UpdateUSproc0.FindParameter("RefTypeArray")!, principalDerived.FindProperty("RefTypeArray")!, principalBase_UpdateSprocMapping0); + RelationalModel.CreateStoredProcedureParameterMapping(refTypeEnumerableParameter0, principalBase_UpdateUSproc0.FindParameter("RefTypeEnumerable")!, principalDerived.FindProperty("RefTypeEnumerable")!, principalBase_UpdateSprocMapping0); + RelationalModel.CreateStoredProcedureParameterMapping(refTypeIListParameter0, principalBase_UpdateUSproc0.FindParameter("RefTypeIList")!, principalDerived.FindProperty("RefTypeIList")!, principalBase_UpdateSprocMapping0); + RelationalModel.CreateStoredProcedureParameterMapping(refTypeListParameter0, principalBase_UpdateUSproc0.FindParameter("RefTypeList")!, principalDerived.FindProperty("RefTypeList")!, principalBase_UpdateSprocMapping0); + RelationalModel.CreateStoredProcedureParameterMapping(valueTypeArrayParameter0, principalBase_UpdateUSproc0.FindParameter("ValueTypeArray")!, principalDerived.FindProperty("ValueTypeArray")!, principalBase_UpdateSprocMapping0); + RelationalModel.CreateStoredProcedureParameterMapping(valueTypeEnumerableParameter0, principalBase_UpdateUSproc0.FindParameter("ValueTypeEnumerable")!, principalDerived.FindProperty("ValueTypeEnumerable")!, principalBase_UpdateSprocMapping0); + RelationalModel.CreateStoredProcedureParameterMapping(valueTypeIListParameter0, principalBase_UpdateUSproc0.FindParameter("ValueTypeIList")!, principalDerived.FindProperty("ValueTypeIList")!, principalBase_UpdateSprocMapping0); + RelationalModel.CreateStoredProcedureParameterMapping(valueTypeListParameter0, principalBase_UpdateUSproc0.FindParameter("ValueTypeList")!, principalDerived.FindProperty("ValueTypeList")!, principalBase_UpdateSprocMapping0); var fK_PrincipalBase_PrincipalBase_PrincipalBaseId = new ForeignKeyConstraint( "FK_PrincipalBase_PrincipalBase_PrincipalBaseId", principalBaseTable, principalBaseTable, new[] { principalBaseIdColumn }, @@ -7154,6 +10379,7 @@ private IRelationalModel CreateRelationalModel() // using System; using System.Collections.Generic; +using System.Net; using System.Reflection; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.ChangeTracking; @@ -7315,7 +10541,1041 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType bas (int value) => (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum)value))); flagsEnum1.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); - var flagsEnum2 = runtimeEntityType.AddProperty( + var flagsEnum2 = runtimeEntityType.AddProperty( + "FlagsEnum2", + typeof(CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("FlagsEnum2", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + sentinel: (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum)0); + flagsEnum2.TypeMapping = IntTypeMapping.Default.Clone( + comparer: new ValueComparer( + (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum v1, CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum v2) => object.Equals((object)v1, (object)v2), + (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum v) => v.GetHashCode(), + (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum v) => v), + keyComparer: new ValueComparer( + (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum v1, CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum v2) => object.Equals((object)v1, (object)v2), + (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum v) => v.GetHashCode(), + (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum v) => v), + providerValueComparer: new ValueComparer( + (int v1, int v2) => v1 == v2, + (int v) => v, + (int v) => v), + converter: new ValueConverter( + (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum value) => (int)value, + (int value) => (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum)value), + jsonValueReaderWriter: new JsonConvertedValueReaderWriter( + JsonInt32ReaderWriter.Instance, + new ValueConverter( + (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum value) => (int)value, + (int value) => (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum)value))); + flagsEnum2.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + + var principalBaseId = runtimeEntityType.AddProperty( + "PrincipalBaseId", + typeof(long?), + nullable: true); + principalBaseId.TypeMapping = SqlServerLongTypeMapping.Default.Clone( + comparer: new ValueComparer( + (Nullable v1, Nullable v2) => v1.HasValue && v2.HasValue && (long)v1 == (long)v2 || !v1.HasValue && !v2.HasValue, + (Nullable v) => v.HasValue ? ((long)v).GetHashCode() : 0, + (Nullable v) => v.HasValue ? (Nullable)(long)v : default(Nullable)), + keyComparer: new ValueComparer( + (Nullable v1, Nullable v2) => v1.HasValue && v2.HasValue && (long)v1 == (long)v2 || !v1.HasValue && !v2.HasValue, + (Nullable v) => v.HasValue ? ((long)v).GetHashCode() : 0, + (Nullable v) => v.HasValue ? (Nullable)(long)v : default(Nullable)), + providerValueComparer: new ValueComparer( + (Nullable v1, Nullable v2) => v1.HasValue && v2.HasValue && (long)v1 == (long)v2 || !v1.HasValue && !v2.HasValue, + (Nullable v) => v.HasValue ? ((long)v).GetHashCode() : 0, + (Nullable v) => v.HasValue ? (Nullable)(long)v : default(Nullable))); + principalBaseId.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + + var refTypeArray = runtimeEntityType.AddProperty( + "RefTypeArray", + typeof(IPAddress[]), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("RefTypeArray", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); + refTypeArray.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v)), + keyComparer: new ListComparer(new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v)), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), + converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter( + new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v))))), + storeTypePostfix: StoreTypePostfix.None, + jsonValueReaderWriter: new JsonCollectionReaderWriter( + new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v)))), + elementMapping: SqlServerStringTypeMapping.Default.Clone( + comparer: new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v), + keyComparer: new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(45)", + size: 45, + dbType: System.Data.DbType.String), + converter: new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v)), + jsonValueReaderWriter: new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v))))); +refTypeArray.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var refTypeEnumerable = runtimeEntityType.AddProperty( + "RefTypeEnumerable", + typeof(IEnumerable), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("RefTypeEnumerable", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +refTypeEnumerable.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, string>( + JsonStringReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, string>( + JsonStringReaderWriter.Instance), +elementMapping: SqlServerStringTypeMapping.Default.Clone( + comparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + keyComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), + storeTypePostfix: StoreTypePostfix.None)); +refTypeEnumerable.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var refTypeIList = runtimeEntityType.AddProperty( + "RefTypeIList", + typeof(IList), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("RefTypeIList", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +refTypeIList.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, string>( + JsonStringReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, string>( + JsonStringReaderWriter.Instance), +elementMapping: SqlServerStringTypeMapping.Default.Clone( + comparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + keyComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), + storeTypePostfix: StoreTypePostfix.None)); +refTypeIList.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var refTypeList = runtimeEntityType.AddProperty( + "RefTypeList", + typeof(List), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("RefTypeList", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +refTypeList.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, IPAddress>( + new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v))))), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, IPAddress>( + new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v)))), +elementMapping: SqlServerStringTypeMapping.Default.Clone( + comparer: new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v), + keyComparer: new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(45)", + size: 45, + dbType: System.Data.DbType.String), + converter: new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v)), + jsonValueReaderWriter: new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v))))); +refTypeList.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var valueTypeArray = runtimeEntityType.AddProperty( + "ValueTypeArray", + typeof(DateTime[]), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("ValueTypeArray", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +valueTypeArray.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter( + JsonDateTimeReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter( + JsonDateTimeReaderWriter.Instance), +elementMapping: SqlServerDateTimeTypeMapping.Default.Clone( + comparer: new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v), + keyComparer: new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v), + providerValueComparer: new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v))); +valueTypeArray.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var valueTypeEnumerable = runtimeEntityType.AddProperty( + "ValueTypeEnumerable", + typeof(IEnumerable), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("ValueTypeEnumerable", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +valueTypeEnumerable.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, byte>( + JsonByteReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, byte>( + JsonByteReaderWriter.Instance), +elementMapping: SqlServerByteTypeMapping.Default.Clone( + comparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v), + keyComparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v), + providerValueComparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v))); +valueTypeEnumerable.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var valueTypeIList = runtimeEntityType.AddProperty( + "ValueTypeIList", + typeof(IList), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("ValueTypeIList", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +valueTypeIList.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, byte>( + JsonByteReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, byte>( + JsonByteReaderWriter.Instance), +elementMapping: SqlServerByteTypeMapping.Default.Clone( + comparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v), + keyComparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v), + providerValueComparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v))); +valueTypeIList.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var valueTypeList = runtimeEntityType.AddProperty( + "ValueTypeList", + typeof(List), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("ValueTypeList", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +valueTypeList.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, short>( + JsonInt16ReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, short>( + JsonInt16ReaderWriter.Instance), +elementMapping: SqlServerShortTypeMapping.Default.Clone( + comparer: new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v), + keyComparer: new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v), + providerValueComparer: new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v))); +valueTypeList.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +OwnedComplexProperty.Create(runtimeEntityType); +var key = runtimeEntityType.AddKey( + new[] { id }); +runtimeEntityType.SetPrimaryKey(key); + +var index = runtimeEntityType.AddIndex( + new[] { principalBaseId }); + +return runtimeEntityType; +} + +private static class OwnedComplexProperty +{ + public static RuntimeComplexProperty Create(RuntimeEntityType declaringType) + { + var complexProperty = declaringType.AddComplexProperty("Owned", + typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType), + "Microsoft.EntityFrameworkCore.Scaffolding.Internal.CSharpRuntimeModelCodeGeneratorTest+PrincipalBase.Owned#OwnedType", + typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("Owned", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("_ownedField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + propertyAccessMode: PropertyAccessMode.Field, + changeTrackingStrategy: ChangeTrackingStrategy.ChangingAndChangedNotificationsWithOriginalValues); + + var complexType = complexProperty.ComplexType; + var details = complexType.AddProperty( + "Details", + typeof(string), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("Details", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("_details", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + propertyAccessMode: PropertyAccessMode.FieldDuringConstruction, + nullable: true, + concurrencyToken: true, + valueGenerated: ValueGenerated.OnAddOrUpdate, + beforeSaveBehavior: PropertySaveBehavior.Ignore, + afterSaveBehavior: PropertySaveBehavior.Ignore, + maxLength: 64, + unicode: false, + precision: 3, + scale: 2, + sentinel: ""); + details.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + keyComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "varchar(64)", + size: 64, + precision: 3, + scale: 2)); + details.AddAnnotation("foo", "bar"); + details.AddAnnotation("Relational:ColumnName", "Deets"); + details.AddAnnotation("Relational:ColumnType", "varchar"); + details.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + + var number = complexType.AddProperty( + "Number", + typeof(int), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("Number", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + sentinel: 0); + number.TypeMapping = IntTypeMapping.Default.Clone( + comparer: new ValueComparer( + (int v1, int v2) => v1 == v2, + (int v) => v, + (int v) => v), + keyComparer: new ValueComparer( + (int v1, int v2) => v1 == v2, + (int v) => v, + (int v) => v), + providerValueComparer: new ValueComparer( + (int v1, int v2) => v1 == v2, + (int v) => v, + (int v) => v)); + number.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + + var refTypeArray = complexType.AddProperty( + "RefTypeArray", + typeof(IPAddress[]), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("RefTypeArray", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("_refTypeArray", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); + refTypeArray.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v)), + keyComparer: new ListComparer(new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v)), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), + converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter( + new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v))))), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter( + new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v)))), +elementMapping: SqlServerStringTypeMapping.Default.Clone( + comparer: new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v), + keyComparer: new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(45)", + size: 45, + dbType: System.Data.DbType.String), + converter: new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v)), + jsonValueReaderWriter: new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v))))); +refTypeArray.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var refTypeEnumerable = complexType.AddProperty( + "RefTypeEnumerable", + typeof(IEnumerable), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("RefTypeEnumerable", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("_refTypeEnumerable", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +refTypeEnumerable.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, string>( + JsonStringReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, string>( + JsonStringReaderWriter.Instance), +elementMapping: SqlServerStringTypeMapping.Default.Clone( + comparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + keyComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), + storeTypePostfix: StoreTypePostfix.None)); +refTypeEnumerable.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var refTypeIList = complexType.AddProperty( + "RefTypeIList", + typeof(IList), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("RefTypeIList", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("_refTypeIList", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +refTypeIList.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, string>( + JsonStringReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, string>( + JsonStringReaderWriter.Instance), +elementMapping: SqlServerStringTypeMapping.Default.Clone( + comparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + keyComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), + storeTypePostfix: StoreTypePostfix.None)); +refTypeIList.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var refTypeList = complexType.AddProperty( + "RefTypeList", + typeof(List), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("RefTypeList", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("_refTypeList", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +refTypeList.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, IPAddress>( + new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v))))), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, IPAddress>( + new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v)))), +elementMapping: SqlServerStringTypeMapping.Default.Clone( + comparer: new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v), + keyComparer: new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(45)", + size: 45, + dbType: System.Data.DbType.String), + converter: new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v)), + jsonValueReaderWriter: new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v))))); +refTypeList.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var valueTypeArray = complexType.AddProperty( + "ValueTypeArray", + typeof(DateTime[]), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("ValueTypeArray", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("_valueTypeArray", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +valueTypeArray.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter( + JsonDateTimeReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter( + JsonDateTimeReaderWriter.Instance), +elementMapping: SqlServerDateTimeTypeMapping.Default.Clone( + comparer: new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v), + keyComparer: new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v), + providerValueComparer: new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v))); +valueTypeArray.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var valueTypeEnumerable = complexType.AddProperty( + "ValueTypeEnumerable", + typeof(IEnumerable), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("ValueTypeEnumerable", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("_valueTypeEnumerable", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +valueTypeEnumerable.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, byte>( + JsonByteReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, byte>( + JsonByteReaderWriter.Instance), +elementMapping: SqlServerByteTypeMapping.Default.Clone( + comparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v), + keyComparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v), + providerValueComparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v))); +valueTypeEnumerable.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var valueTypeIList = complexType.AddProperty( + "ValueTypeIList", + typeof(IList), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("ValueTypeIList", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +valueTypeIList.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, byte>( + JsonByteReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, byte>( + JsonByteReaderWriter.Instance), +elementMapping: SqlServerByteTypeMapping.Default.Clone( + comparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v), + keyComparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v), + providerValueComparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v))); +valueTypeIList.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var valueTypeList = complexType.AddProperty( + "ValueTypeList", + typeof(List), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("ValueTypeList", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("_valueTypeList", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +valueTypeList.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, short>( + JsonInt16ReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, short>( + JsonInt16ReaderWriter.Instance), +elementMapping: SqlServerShortTypeMapping.Default.Clone( + comparer: new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v), + keyComparer: new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v), + providerValueComparer: new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v))); +valueTypeList.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +PrincipalComplexProperty.Create(complexType); +complexType.AddAnnotation("go", "brr"); +complexType.AddAnnotation("Relational:FunctionName", "PrincipalBaseTvf"); +complexType.AddAnnotation("Relational:Schema", null); +complexType.AddAnnotation("Relational:SqlQuery", "select * from PrincipalBase"); +complexType.AddAnnotation("Relational:TableName", "PrincipalBase"); +complexType.AddAnnotation("Relational:ViewName", "PrincipalBaseView"); +complexType.AddAnnotation("Relational:ViewSchema", null); +complexProperty.AddAnnotation("goo", "ber"); +return complexProperty; +} + + private static class PrincipalComplexProperty + { + public static RuntimeComplexProperty Create(RuntimeComplexType declaringType) + { + var complexProperty = declaringType.AddComplexProperty("Principal", + typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase), + "Microsoft.EntityFrameworkCore.Scaffolding.Internal.CSharpRuntimeModelCodeGeneratorTest+PrincipalBase.Owned#OwnedType.Principal#PrincipalBase", + typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("Principal", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); + + var complexType = complexProperty.ComplexType; + var alternateId = complexType.AddProperty( + "AlternateId", + typeof(Guid), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("AlternateId", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + sentinel: new Guid("00000000-0000-0000-0000-000000000000")); + alternateId.TypeMapping = GuidTypeMapping.Default.Clone( + comparer: new ValueComparer( + (Guid v1, Guid v2) => v1 == v2, + (Guid v) => v.GetHashCode(), + (Guid v) => v), + keyComparer: new ValueComparer( + (Guid v1, Guid v2) => v1 == v2, + (Guid v) => v.GetHashCode(), + (Guid v) => v), + providerValueComparer: new ValueComparer( + (Guid v1, Guid v2) => v1 == v2, + (Guid v) => v.GetHashCode(), + (Guid v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "uniqueidentifier")); + alternateId.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + + var enum1 = complexType.AddProperty( + "Enum1", + typeof(CSharpRuntimeModelCodeGeneratorTest.AnEnum), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("Enum1", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + sentinel: (CSharpRuntimeModelCodeGeneratorTest.AnEnum)0); + enum1.TypeMapping = IntTypeMapping.Default.Clone( + comparer: new ValueComparer( + (CSharpRuntimeModelCodeGeneratorTest.AnEnum v1, CSharpRuntimeModelCodeGeneratorTest.AnEnum v2) => object.Equals((object)v1, (object)v2), + (CSharpRuntimeModelCodeGeneratorTest.AnEnum v) => v.GetHashCode(), + (CSharpRuntimeModelCodeGeneratorTest.AnEnum v) => v), + keyComparer: new ValueComparer( + (CSharpRuntimeModelCodeGeneratorTest.AnEnum v1, CSharpRuntimeModelCodeGeneratorTest.AnEnum v2) => object.Equals((object)v1, (object)v2), + (CSharpRuntimeModelCodeGeneratorTest.AnEnum v) => v.GetHashCode(), + (CSharpRuntimeModelCodeGeneratorTest.AnEnum v) => v), + providerValueComparer: new ValueComparer( + (int v1, int v2) => v1 == v2, + (int v) => v, + (int v) => v), + converter: new ValueConverter( + (CSharpRuntimeModelCodeGeneratorTest.AnEnum value) => (int)value, + (int value) => (CSharpRuntimeModelCodeGeneratorTest.AnEnum)value), + jsonValueReaderWriter: new JsonConvertedValueReaderWriter( + JsonInt32ReaderWriter.Instance, + new ValueConverter( + (CSharpRuntimeModelCodeGeneratorTest.AnEnum value) => (int)value, + (int value) => (CSharpRuntimeModelCodeGeneratorTest.AnEnum)value))); + enum1.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + + var enum2 = complexType.AddProperty( + "Enum2", + typeof(CSharpRuntimeModelCodeGeneratorTest.AnEnum?), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("Enum2", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); + enum2.TypeMapping = IntTypeMapping.Default.Clone( + comparer: new ValueComparer( + (Nullable v1, Nullable v2) => v1.HasValue && v2.HasValue && object.Equals((object)(CSharpRuntimeModelCodeGeneratorTest.AnEnum)v1, (object)(CSharpRuntimeModelCodeGeneratorTest.AnEnum)v2) || !v1.HasValue && !v2.HasValue, + (Nullable v) => v.HasValue ? ((CSharpRuntimeModelCodeGeneratorTest.AnEnum)v).GetHashCode() : 0, + (Nullable v) => v.HasValue ? (Nullable)(CSharpRuntimeModelCodeGeneratorTest.AnEnum)v : default(Nullable)), + keyComparer: new ValueComparer( + (Nullable v1, Nullable v2) => v1.HasValue && v2.HasValue && object.Equals((object)(CSharpRuntimeModelCodeGeneratorTest.AnEnum)v1, (object)(CSharpRuntimeModelCodeGeneratorTest.AnEnum)v2) || !v1.HasValue && !v2.HasValue, + (Nullable v) => v.HasValue ? ((CSharpRuntimeModelCodeGeneratorTest.AnEnum)v).GetHashCode() : 0, + (Nullable v) => v.HasValue ? (Nullable)(CSharpRuntimeModelCodeGeneratorTest.AnEnum)v : default(Nullable)), + providerValueComparer: new ValueComparer( + (int v1, int v2) => v1 == v2, + (int v) => v, + (int v) => v), + converter: new ValueConverter( + (CSharpRuntimeModelCodeGeneratorTest.AnEnum value) => (int)value, + (int value) => (CSharpRuntimeModelCodeGeneratorTest.AnEnum)value), + jsonValueReaderWriter: new JsonConvertedValueReaderWriter( + JsonInt32ReaderWriter.Instance, + new ValueConverter( + (CSharpRuntimeModelCodeGeneratorTest.AnEnum value) => (int)value, + (int value) => (CSharpRuntimeModelCodeGeneratorTest.AnEnum)value))); + enum2.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + + var flagsEnum1 = complexType.AddProperty( + "FlagsEnum1", + typeof(CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("FlagsEnum1", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + sentinel: (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum)0); + flagsEnum1.TypeMapping = IntTypeMapping.Default.Clone( + comparer: new ValueComparer( + (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum v1, CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum v2) => object.Equals((object)v1, (object)v2), + (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum v) => v.GetHashCode(), + (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum v) => v), + keyComparer: new ValueComparer( + (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum v1, CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum v2) => object.Equals((object)v1, (object)v2), + (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum v) => v.GetHashCode(), + (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum v) => v), + providerValueComparer: new ValueComparer( + (int v1, int v2) => v1 == v2, + (int v) => v, + (int v) => v), + converter: new ValueConverter( + (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum value) => (int)value, + (int value) => (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum)value), + jsonValueReaderWriter: new JsonConvertedValueReaderWriter( + JsonInt32ReaderWriter.Instance, + new ValueConverter( + (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum value) => (int)value, + (int value) => (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum)value))); + flagsEnum1.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + + var flagsEnum2 = complexType.AddProperty( "FlagsEnum2", typeof(CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum), propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("FlagsEnum2", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), @@ -7344,11 +11604,13 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType bas (int value) => (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum)value))); flagsEnum2.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); - var principalBaseId = runtimeEntityType.AddProperty( - "PrincipalBaseId", + var id = complexType.AddProperty( + "Id", typeof(long?), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("Id", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), nullable: true); - principalBaseId.TypeMapping = SqlServerLongTypeMapping.Default.Clone( + id.TypeMapping = SqlServerLongTypeMapping.Default.Clone( comparer: new ValueComparer( (Nullable v1, Nullable v2) => v1.HasValue && v2.HasValue && (long)v1 == (long)v2 || !v1.HasValue && !v2.HasValue, (Nullable v) => v.HasValue ? ((long)v).GetHashCode() : 0, @@ -7361,371 +11623,519 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType bas (Nullable v1, Nullable v2) => v1.HasValue && v2.HasValue && (long)v1 == (long)v2 || !v1.HasValue && !v2.HasValue, (Nullable v) => v.HasValue ? ((long)v).GetHashCode() : 0, (Nullable v) => v.HasValue ? (Nullable)(long)v : default(Nullable))); - principalBaseId.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); - - OwnedComplexProperty.Create(runtimeEntityType); - var key = runtimeEntityType.AddKey( - new[] { id }); - runtimeEntityType.SetPrimaryKey(key); - - var index = runtimeEntityType.AddIndex( - new[] { principalBaseId }); - - return runtimeEntityType; - } - - private static class OwnedComplexProperty - { - public static RuntimeComplexProperty Create(RuntimeEntityType declaringType) - { - var complexProperty = declaringType.AddComplexProperty("Owned", - typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType), - "Microsoft.EntityFrameworkCore.Scaffolding.Internal.CSharpRuntimeModelCodeGeneratorTest+PrincipalBase.Owned#OwnedType", - typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType), - propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("Owned", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), - fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("_ownedField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), - propertyAccessMode: PropertyAccessMode.Field, - changeTrackingStrategy: ChangeTrackingStrategy.ChangingAndChangedNotificationsWithOriginalValues); - - var complexType = complexProperty.ComplexType; - var details = complexType.AddProperty( - "Details", - typeof(string), - propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("Details", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), - fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("_details", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), - propertyAccessMode: PropertyAccessMode.FieldDuringConstruction, - nullable: true, - concurrencyToken: true, - valueGenerated: ValueGenerated.OnAddOrUpdate, - beforeSaveBehavior: PropertySaveBehavior.Ignore, - afterSaveBehavior: PropertySaveBehavior.Ignore, - maxLength: 64, - unicode: false, - precision: 3, - scale: 2, - sentinel: ""); - details.TypeMapping = SqlServerStringTypeMapping.Default.Clone( - comparer: new ValueComparer( - (string v1, string v2) => v1 == v2, - (string v) => v.GetHashCode(), - (string v) => v), - keyComparer: new ValueComparer( - (string v1, string v2) => v1 == v2, - (string v) => v.GetHashCode(), - (string v) => v), - providerValueComparer: new ValueComparer( - (string v1, string v2) => v1 == v2, - (string v) => v.GetHashCode(), - (string v) => v), - mappingInfo: new RelationalTypeMappingInfo( - storeTypeName: "varchar(64)", - size: 64, - precision: 3, - scale: 2)); - details.AddAnnotation("foo", "bar"); - details.AddAnnotation("Relational:ColumnName", "Deets"); - details.AddAnnotation("Relational:ColumnType", "varchar"); - details.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); - - var number = complexType.AddProperty( - "Number", - typeof(int), - propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("Number", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), - fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), - sentinel: 0); - number.TypeMapping = IntTypeMapping.Default.Clone( - comparer: new ValueComparer( - (int v1, int v2) => v1 == v2, - (int v) => v, - (int v) => v), - keyComparer: new ValueComparer( - (int v1, int v2) => v1 == v2, - (int v) => v, - (int v) => v), - providerValueComparer: new ValueComparer( - (int v1, int v2) => v1 == v2, - (int v) => v, - (int v) => v)); - number.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); - - PrincipalComplexProperty.Create(complexType); - complexType.AddAnnotation("go", "brr"); - complexType.AddAnnotation("Relational:FunctionName", "PrincipalBaseTvf"); - complexType.AddAnnotation("Relational:Schema", null); - complexType.AddAnnotation("Relational:SqlQuery", "select * from PrincipalBase"); - complexType.AddAnnotation("Relational:TableName", "PrincipalBase"); - complexType.AddAnnotation("Relational:ViewName", "PrincipalBaseView"); - complexType.AddAnnotation("Relational:ViewSchema", null); - complexProperty.AddAnnotation("goo", "ber"); - return complexProperty; - } - - private static class PrincipalComplexProperty - { - public static RuntimeComplexProperty Create(RuntimeComplexType declaringType) - { - var complexProperty = declaringType.AddComplexProperty("Principal", - typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase), - "Microsoft.EntityFrameworkCore.Scaffolding.Internal.CSharpRuntimeModelCodeGeneratorTest+PrincipalBase.Owned#OwnedType.Principal#PrincipalBase", - typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase), - propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty("Principal", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), - fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); - - var complexType = complexProperty.ComplexType; - var alternateId = complexType.AddProperty( - "AlternateId", - typeof(Guid), - fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("AlternateId", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), - sentinel: new Guid("00000000-0000-0000-0000-000000000000")); - alternateId.TypeMapping = GuidTypeMapping.Default.Clone( - comparer: new ValueComparer( - (Guid v1, Guid v2) => v1 == v2, - (Guid v) => v.GetHashCode(), - (Guid v) => v), - keyComparer: new ValueComparer( - (Guid v1, Guid v2) => v1 == v2, - (Guid v) => v.GetHashCode(), - (Guid v) => v), - providerValueComparer: new ValueComparer( - (Guid v1, Guid v2) => v1 == v2, - (Guid v) => v.GetHashCode(), - (Guid v) => v), - mappingInfo: new RelationalTypeMappingInfo( - storeTypeName: "uniqueidentifier")); - alternateId.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); - - var enum1 = complexType.AddProperty( - "Enum1", - typeof(CSharpRuntimeModelCodeGeneratorTest.AnEnum), - propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("Enum1", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), - fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), - sentinel: (CSharpRuntimeModelCodeGeneratorTest.AnEnum)0); - enum1.TypeMapping = IntTypeMapping.Default.Clone( - comparer: new ValueComparer( - (CSharpRuntimeModelCodeGeneratorTest.AnEnum v1, CSharpRuntimeModelCodeGeneratorTest.AnEnum v2) => object.Equals((object)v1, (object)v2), - (CSharpRuntimeModelCodeGeneratorTest.AnEnum v) => v.GetHashCode(), - (CSharpRuntimeModelCodeGeneratorTest.AnEnum v) => v), - keyComparer: new ValueComparer( - (CSharpRuntimeModelCodeGeneratorTest.AnEnum v1, CSharpRuntimeModelCodeGeneratorTest.AnEnum v2) => object.Equals((object)v1, (object)v2), - (CSharpRuntimeModelCodeGeneratorTest.AnEnum v) => v.GetHashCode(), - (CSharpRuntimeModelCodeGeneratorTest.AnEnum v) => v), - providerValueComparer: new ValueComparer( - (int v1, int v2) => v1 == v2, - (int v) => v, - (int v) => v), - converter: new ValueConverter( - (CSharpRuntimeModelCodeGeneratorTest.AnEnum value) => (int)value, - (int value) => (CSharpRuntimeModelCodeGeneratorTest.AnEnum)value), - jsonValueReaderWriter: new JsonConvertedValueReaderWriter( - JsonInt32ReaderWriter.Instance, - new ValueConverter( - (CSharpRuntimeModelCodeGeneratorTest.AnEnum value) => (int)value, - (int value) => (CSharpRuntimeModelCodeGeneratorTest.AnEnum)value))); - enum1.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); - - var enum2 = complexType.AddProperty( - "Enum2", - typeof(CSharpRuntimeModelCodeGeneratorTest.AnEnum?), - propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("Enum2", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), - fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), - nullable: true); - enum2.TypeMapping = IntTypeMapping.Default.Clone( - comparer: new ValueComparer( - (Nullable v1, Nullable v2) => v1.HasValue && v2.HasValue && object.Equals((object)(CSharpRuntimeModelCodeGeneratorTest.AnEnum)v1, (object)(CSharpRuntimeModelCodeGeneratorTest.AnEnum)v2) || !v1.HasValue && !v2.HasValue, - (Nullable v) => v.HasValue ? ((CSharpRuntimeModelCodeGeneratorTest.AnEnum)v).GetHashCode() : 0, - (Nullable v) => v.HasValue ? (Nullable)(CSharpRuntimeModelCodeGeneratorTest.AnEnum)v : default(Nullable)), - keyComparer: new ValueComparer( - (Nullable v1, Nullable v2) => v1.HasValue && v2.HasValue && object.Equals((object)(CSharpRuntimeModelCodeGeneratorTest.AnEnum)v1, (object)(CSharpRuntimeModelCodeGeneratorTest.AnEnum)v2) || !v1.HasValue && !v2.HasValue, - (Nullable v) => v.HasValue ? ((CSharpRuntimeModelCodeGeneratorTest.AnEnum)v).GetHashCode() : 0, - (Nullable v) => v.HasValue ? (Nullable)(CSharpRuntimeModelCodeGeneratorTest.AnEnum)v : default(Nullable)), - providerValueComparer: new ValueComparer( - (int v1, int v2) => v1 == v2, - (int v) => v, - (int v) => v), - converter: new ValueConverter( - (CSharpRuntimeModelCodeGeneratorTest.AnEnum value) => (int)value, - (int value) => (CSharpRuntimeModelCodeGeneratorTest.AnEnum)value), - jsonValueReaderWriter: new JsonConvertedValueReaderWriter( - JsonInt32ReaderWriter.Instance, - new ValueConverter( - (CSharpRuntimeModelCodeGeneratorTest.AnEnum value) => (int)value, - (int value) => (CSharpRuntimeModelCodeGeneratorTest.AnEnum)value))); - enum2.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); - - var flagsEnum1 = complexType.AddProperty( - "FlagsEnum1", - typeof(CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum), - propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("FlagsEnum1", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), - fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), - sentinel: (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum)0); - flagsEnum1.TypeMapping = IntTypeMapping.Default.Clone( - comparer: new ValueComparer( - (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum v1, CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum v2) => object.Equals((object)v1, (object)v2), - (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum v) => v.GetHashCode(), - (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum v) => v), - keyComparer: new ValueComparer( - (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum v1, CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum v2) => object.Equals((object)v1, (object)v2), - (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum v) => v.GetHashCode(), - (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum v) => v), - providerValueComparer: new ValueComparer( - (int v1, int v2) => v1 == v2, - (int v) => v, - (int v) => v), - converter: new ValueConverter( - (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum value) => (int)value, - (int value) => (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum)value), - jsonValueReaderWriter: new JsonConvertedValueReaderWriter( - JsonInt32ReaderWriter.Instance, - new ValueConverter( - (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum value) => (int)value, - (int value) => (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum)value))); - flagsEnum1.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); - - var flagsEnum2 = complexType.AddProperty( - "FlagsEnum2", - typeof(CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum), - propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("FlagsEnum2", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), - fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), - sentinel: (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum)0); - flagsEnum2.TypeMapping = IntTypeMapping.Default.Clone( - comparer: new ValueComparer( - (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum v1, CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum v2) => object.Equals((object)v1, (object)v2), - (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum v) => v.GetHashCode(), - (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum v) => v), - keyComparer: new ValueComparer( - (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum v1, CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum v2) => object.Equals((object)v1, (object)v2), - (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum v) => v.GetHashCode(), - (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum v) => v), - providerValueComparer: new ValueComparer( - (int v1, int v2) => v1 == v2, - (int v) => v, - (int v) => v), - converter: new ValueConverter( - (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum value) => (int)value, - (int value) => (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum)value), - jsonValueReaderWriter: new JsonConvertedValueReaderWriter( - JsonInt32ReaderWriter.Instance, - new ValueConverter( - (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum value) => (int)value, - (int value) => (CSharpRuntimeModelCodeGeneratorTest.AFlagsEnum)value))); - flagsEnum2.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); - - var id = complexType.AddProperty( - "Id", - typeof(long?), - propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("Id", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), - fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), - nullable: true); - id.TypeMapping = SqlServerLongTypeMapping.Default.Clone( - comparer: new ValueComparer( - (Nullable v1, Nullable v2) => v1.HasValue && v2.HasValue && (long)v1 == (long)v2 || !v1.HasValue && !v2.HasValue, - (Nullable v) => v.HasValue ? ((long)v).GetHashCode() : 0, - (Nullable v) => v.HasValue ? (Nullable)(long)v : default(Nullable)), - keyComparer: new ValueComparer( - (Nullable v1, Nullable v2) => v1.HasValue && v2.HasValue && (long)v1 == (long)v2 || !v1.HasValue && !v2.HasValue, - (Nullable v) => v.HasValue ? ((long)v).GetHashCode() : 0, - (Nullable v) => v.HasValue ? (Nullable)(long)v : default(Nullable)), - providerValueComparer: new ValueComparer( - (Nullable v1, Nullable v2) => v1.HasValue && v2.HasValue && (long)v1 == (long)v2 || !v1.HasValue && !v2.HasValue, - (Nullable v) => v.HasValue ? ((long)v).GetHashCode() : 0, - (Nullable v) => v.HasValue ? (Nullable)(long)v : default(Nullable))); - id.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); - - complexType.AddAnnotation("Relational:FunctionName", "PrincipalBaseTvf"); - complexType.AddAnnotation("Relational:Schema", null); - complexType.AddAnnotation("Relational:SqlQuery", "select * from PrincipalBase"); - complexType.AddAnnotation("Relational:TableName", "PrincipalBase"); - complexType.AddAnnotation("Relational:ViewName", "PrincipalBaseView"); - complexType.AddAnnotation("Relational:ViewSchema", null); - return complexProperty; - } - } - } - - public static RuntimeForeignKey CreateForeignKey1(RuntimeEntityType declaringEntityType, RuntimeEntityType principalEntityType) - { - var runtimeForeignKey = declaringEntityType.AddForeignKey(new[] { declaringEntityType.FindProperty("PrincipalBaseId") }, - principalEntityType.FindKey(new[] { principalEntityType.FindProperty("Id") }), - principalEntityType); - - var deriveds = principalEntityType.AddNavigation("Deriveds", - runtimeForeignKey, - onDependent: false, - typeof(ICollection), - propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("Deriveds", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), - fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); - - return runtimeForeignKey; - } - - public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) - { - var insertSproc = new RuntimeStoredProcedure( - runtimeEntityType, - "PrincipalBase_Insert", - null, - false); - - var principalBaseId = insertSproc.AddParameter( - "PrincipalBaseId", System.Data.ParameterDirection.Input, false, "PrincipalBaseId", false); - var enum1 = insertSproc.AddParameter( - "Enum1", System.Data.ParameterDirection.Input, false, "Enum1", false); - var enum2 = insertSproc.AddParameter( - "Enum2", System.Data.ParameterDirection.Input, false, "Enum2", false); - var flagsEnum1 = insertSproc.AddParameter( - "FlagsEnum1", System.Data.ParameterDirection.Input, false, "FlagsEnum1", false); - var flagsEnum2 = insertSproc.AddParameter( - "FlagsEnum2", System.Data.ParameterDirection.Input, false, "FlagsEnum2", false); - var discriminator = insertSproc.AddParameter( - "Discriminator", System.Data.ParameterDirection.Input, false, "Discriminator", false); - var id = insertSproc.AddParameter( - "Id", System.Data.ParameterDirection.Output, false, "Id", false); - runtimeEntityType.AddAnnotation("Relational:InsertStoredProcedure", insertSproc); - - var deleteSproc = new RuntimeStoredProcedure( - runtimeEntityType, - "PrincipalBase_Delete", - null, - true); - - var id0 = deleteSproc.AddParameter( - "Id_Original", System.Data.ParameterDirection.Input, false, "Id", true); - runtimeEntityType.AddAnnotation("Relational:DeleteStoredProcedure", deleteSproc); - - var updateSproc = new RuntimeStoredProcedure( - runtimeEntityType, - "PrincipalBase_Update", - null, - false); + id.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); - var principalBaseId0 = updateSproc.AddParameter( - "PrincipalBaseId", System.Data.ParameterDirection.Input, false, "PrincipalBaseId", false); - var enum10 = updateSproc.AddParameter( - "Enum1", System.Data.ParameterDirection.Input, false, "Enum1", false); - var enum20 = updateSproc.AddParameter( - "Enum2", System.Data.ParameterDirection.Input, false, "Enum2", false); - var flagsEnum10 = updateSproc.AddParameter( - "FlagsEnum1", System.Data.ParameterDirection.Input, false, "FlagsEnum1", false); - var flagsEnum20 = updateSproc.AddParameter( - "FlagsEnum2", System.Data.ParameterDirection.Input, false, "FlagsEnum2", false); - var id1 = updateSproc.AddParameter( - "Id_Original", System.Data.ParameterDirection.Input, false, "Id", true); - runtimeEntityType.AddAnnotation("Relational:UpdateStoredProcedure", updateSproc); + var refTypeArray = complexType.AddProperty( + "RefTypeArray", + typeof(IPAddress[]), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("RefTypeArray", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); + refTypeArray.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v)), + keyComparer: new ListComparer(new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v)), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), + converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter( + new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v))))), + storeTypePostfix: StoreTypePostfix.None, + jsonValueReaderWriter: new JsonCollectionReaderWriter( + new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v)))), + elementMapping: SqlServerStringTypeMapping.Default.Clone( + comparer: new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v), + keyComparer: new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(45)", + size: 45, + dbType: System.Data.DbType.String), + converter: new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v)), + jsonValueReaderWriter: new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v))))); +refTypeArray.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var refTypeEnumerable = complexType.AddProperty( + "RefTypeEnumerable", + typeof(IEnumerable), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("RefTypeEnumerable", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +refTypeEnumerable.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, string>( + JsonStringReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, string>( + JsonStringReaderWriter.Instance), +elementMapping: SqlServerStringTypeMapping.Default.Clone( + comparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + keyComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), + storeTypePostfix: StoreTypePostfix.None)); +refTypeEnumerable.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var refTypeIList = complexType.AddProperty( + "RefTypeIList", + typeof(IList), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("RefTypeIList", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +refTypeIList.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, string>( + JsonStringReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, string>( + JsonStringReaderWriter.Instance), +elementMapping: SqlServerStringTypeMapping.Default.Clone( + comparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + keyComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), + storeTypePostfix: StoreTypePostfix.None)); +refTypeIList.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var refTypeList = complexType.AddProperty( + "RefTypeList", + typeof(List), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("RefTypeList", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +refTypeList.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, IPAddress>( + new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v))))), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, IPAddress>( + new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v)))), +elementMapping: SqlServerStringTypeMapping.Default.Clone( + comparer: new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v), + keyComparer: new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(45)", + size: 45, + dbType: System.Data.DbType.String), + converter: new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v)), + jsonValueReaderWriter: new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v))))); +refTypeList.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var valueTypeArray = complexType.AddProperty( + "ValueTypeArray", + typeof(DateTime[]), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("ValueTypeArray", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +valueTypeArray.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter( + JsonDateTimeReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter( + JsonDateTimeReaderWriter.Instance), +elementMapping: SqlServerDateTimeTypeMapping.Default.Clone( + comparer: new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v), + keyComparer: new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v), + providerValueComparer: new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v))); +valueTypeArray.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var valueTypeEnumerable = complexType.AddProperty( + "ValueTypeEnumerable", + typeof(IEnumerable), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("ValueTypeEnumerable", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +valueTypeEnumerable.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, byte>( + JsonByteReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, byte>( + JsonByteReaderWriter.Instance), +elementMapping: SqlServerByteTypeMapping.Default.Clone( + comparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v), + keyComparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v), + providerValueComparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v))); +valueTypeEnumerable.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var valueTypeIList = complexType.AddProperty( + "ValueTypeIList", + typeof(IList), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("ValueTypeIList", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +valueTypeIList.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, byte>( + JsonByteReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, byte>( + JsonByteReaderWriter.Instance), +elementMapping: SqlServerByteTypeMapping.Default.Clone( + comparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v), + keyComparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v), + providerValueComparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v))); +valueTypeIList.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var valueTypeList = complexType.AddProperty( + "ValueTypeList", + typeof(List), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("ValueTypeList", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +valueTypeList.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, short>( + JsonInt16ReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, short>( + JsonInt16ReaderWriter.Instance), +elementMapping: SqlServerShortTypeMapping.Default.Clone( + comparer: new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v), + keyComparer: new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v), + providerValueComparer: new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v))); +valueTypeList.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +complexType.AddAnnotation("Relational:FunctionName", "PrincipalBaseTvf"); +complexType.AddAnnotation("Relational:Schema", null); +complexType.AddAnnotation("Relational:SqlQuery", "select * from PrincipalBase"); +complexType.AddAnnotation("Relational:TableName", "PrincipalBase"); +complexType.AddAnnotation("Relational:ViewName", "PrincipalBaseView"); +complexType.AddAnnotation("Relational:ViewSchema", null); +return complexProperty; +} +} +} - runtimeEntityType.AddAnnotation("Relational:FunctionName", "PrincipalBaseTvf"); - runtimeEntityType.AddAnnotation("Relational:MappingStrategy", "TPH"); - runtimeEntityType.AddAnnotation("Relational:Schema", null); - runtimeEntityType.AddAnnotation("Relational:SqlQuery", "select * from PrincipalBase"); - runtimeEntityType.AddAnnotation("Relational:TableName", "PrincipalBase"); - runtimeEntityType.AddAnnotation("Relational:ViewDefinitionSql", null); - runtimeEntityType.AddAnnotation("Relational:ViewName", "PrincipalBaseView"); - runtimeEntityType.AddAnnotation("Relational:ViewSchema", null); +public static RuntimeForeignKey CreateForeignKey1(RuntimeEntityType declaringEntityType, RuntimeEntityType principalEntityType) +{ + var runtimeForeignKey = declaringEntityType.AddForeignKey(new[] { declaringEntityType.FindProperty("PrincipalBaseId") }, + principalEntityType.FindKey(new[] { principalEntityType.FindProperty("Id") }), + principalEntityType); + + var deriveds = principalEntityType.AddNavigation("Deriveds", + runtimeForeignKey, + onDependent: false, + typeof(ICollection), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("Deriveds", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); + + return runtimeForeignKey; +} - Customize(runtimeEntityType); - } +public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) +{ + var insertSproc = new RuntimeStoredProcedure( + runtimeEntityType, + "PrincipalBase_Insert", + null, + false); + + var principalBaseId = insertSproc.AddParameter( + "PrincipalBaseId", System.Data.ParameterDirection.Input, false, "PrincipalBaseId", false); + var enum1 = insertSproc.AddParameter( + "Enum1", System.Data.ParameterDirection.Input, false, "Enum1", false); + var enum2 = insertSproc.AddParameter( + "Enum2", System.Data.ParameterDirection.Input, false, "Enum2", false); + var flagsEnum1 = insertSproc.AddParameter( + "FlagsEnum1", System.Data.ParameterDirection.Input, false, "FlagsEnum1", false); + var flagsEnum2 = insertSproc.AddParameter( + "FlagsEnum2", System.Data.ParameterDirection.Input, false, "FlagsEnum2", false); + var valueTypeList = insertSproc.AddParameter( + "ValueTypeList", System.Data.ParameterDirection.Input, false, "ValueTypeList", false); + var valueTypeIList = insertSproc.AddParameter( + "ValueTypeIList", System.Data.ParameterDirection.Input, false, "ValueTypeIList", false); + var valueTypeArray = insertSproc.AddParameter( + "ValueTypeArray", System.Data.ParameterDirection.Input, false, "ValueTypeArray", false); + var valueTypeEnumerable = insertSproc.AddParameter( + "ValueTypeEnumerable", System.Data.ParameterDirection.Input, false, "ValueTypeEnumerable", false); + var refTypeList = insertSproc.AddParameter( + "RefTypeList", System.Data.ParameterDirection.Input, false, "RefTypeList", false); + var refTypeIList = insertSproc.AddParameter( + "RefTypeIList", System.Data.ParameterDirection.Input, false, "RefTypeIList", false); + var refTypeArray = insertSproc.AddParameter( + "RefTypeArray", System.Data.ParameterDirection.Input, false, "RefTypeArray", false); + var refTypeEnumerable = insertSproc.AddParameter( + "RefTypeEnumerable", System.Data.ParameterDirection.Input, false, "RefTypeEnumerable", false); + var discriminator = insertSproc.AddParameter( + "Discriminator", System.Data.ParameterDirection.Input, false, "Discriminator", false); + var id = insertSproc.AddParameter( + "Id", System.Data.ParameterDirection.Output, false, "Id", false); + runtimeEntityType.AddAnnotation("Relational:InsertStoredProcedure", insertSproc); + + var deleteSproc = new RuntimeStoredProcedure( + runtimeEntityType, + "PrincipalBase_Delete", + null, + true); + + var id0 = deleteSproc.AddParameter( + "Id_Original", System.Data.ParameterDirection.Input, false, "Id", true); + runtimeEntityType.AddAnnotation("Relational:DeleteStoredProcedure", deleteSproc); + + var updateSproc = new RuntimeStoredProcedure( + runtimeEntityType, + "PrincipalBase_Update", + null, + false); + + var principalBaseId0 = updateSproc.AddParameter( + "PrincipalBaseId", System.Data.ParameterDirection.Input, false, "PrincipalBaseId", false); + var enum10 = updateSproc.AddParameter( + "Enum1", System.Data.ParameterDirection.Input, false, "Enum1", false); + var enum20 = updateSproc.AddParameter( + "Enum2", System.Data.ParameterDirection.Input, false, "Enum2", false); + var flagsEnum10 = updateSproc.AddParameter( + "FlagsEnum1", System.Data.ParameterDirection.Input, false, "FlagsEnum1", false); + var flagsEnum20 = updateSproc.AddParameter( + "FlagsEnum2", System.Data.ParameterDirection.Input, false, "FlagsEnum2", false); + var valueTypeList0 = updateSproc.AddParameter( + "ValueTypeList", System.Data.ParameterDirection.Input, false, "ValueTypeList", false); + var valueTypeIList0 = updateSproc.AddParameter( + "ValueTypeIList", System.Data.ParameterDirection.Input, false, "ValueTypeIList", false); + var valueTypeArray0 = updateSproc.AddParameter( + "ValueTypeArray", System.Data.ParameterDirection.Input, false, "ValueTypeArray", false); + var valueTypeEnumerable0 = updateSproc.AddParameter( + "ValueTypeEnumerable", System.Data.ParameterDirection.Input, false, "ValueTypeEnumerable", false); + var refTypeList0 = updateSproc.AddParameter( + "RefTypeList", System.Data.ParameterDirection.Input, false, "RefTypeList", false); + var refTypeIList0 = updateSproc.AddParameter( + "RefTypeIList", System.Data.ParameterDirection.Input, false, "RefTypeIList", false); + var refTypeArray0 = updateSproc.AddParameter( + "RefTypeArray", System.Data.ParameterDirection.Input, false, "RefTypeArray", false); + var refTypeEnumerable0 = updateSproc.AddParameter( + "RefTypeEnumerable", System.Data.ParameterDirection.Input, false, "RefTypeEnumerable", false); + var id1 = updateSproc.AddParameter( + "Id_Original", System.Data.ParameterDirection.Input, false, "Id", true); + runtimeEntityType.AddAnnotation("Relational:UpdateStoredProcedure", updateSproc); + + runtimeEntityType.AddAnnotation("Relational:FunctionName", "PrincipalBaseTvf"); + runtimeEntityType.AddAnnotation("Relational:MappingStrategy", "TPH"); + runtimeEntityType.AddAnnotation("Relational:Schema", null); + runtimeEntityType.AddAnnotation("Relational:SqlQuery", "select * from PrincipalBase"); + runtimeEntityType.AddAnnotation("Relational:TableName", "PrincipalBase"); + runtimeEntityType.AddAnnotation("Relational:ViewDefinitionSql", null); + runtimeEntityType.AddAnnotation("Relational:ViewName", "PrincipalBaseView"); + runtimeEntityType.AddAnnotation("Relational:ViewSchema", null); + + Customize(runtimeEntityType); +} - static partial void Customize(RuntimeEntityType runtimeEntityType); - } +static partial void Customize(RuntimeEntityType runtimeEntityType); +} } """, c), c => AssertFileContents( @@ -7875,7 +12285,7 @@ public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) var nestedComplexType = complexType.FindComplexProperty(nameof(OwnedType.Principal)).ComplexType; - Assert.Equal(6, nestedComplexType.GetProperties().Count()); + Assert.Equal(14, nestedComplexType.GetProperties().Count()); var principalTable = StoreObjectIdentifier.Create(complexType, StoreObjectType.Table).Value; @@ -8158,6 +12568,14 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasParameter("Enum2") .HasParameter("FlagsEnum1") .HasParameter("FlagsEnum2") + .HasParameter("ValueTypeList") + .HasParameter("ValueTypeIList") + .HasParameter("ValueTypeArray") + .HasParameter("ValueTypeEnumerable") + .HasParameter("RefTypeList") + .HasParameter("RefTypeIList") + .HasParameter("RefTypeArray") + .HasParameter("RefTypeEnumerable") .HasParameter("Discriminator") .HasParameter(p => p.Id, p => p.IsOutput())); eb.UpdateUsingStoredProcedure( @@ -8167,6 +12585,14 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasParameter("Enum2") .HasParameter("FlagsEnum1") .HasParameter("FlagsEnum2") + .HasParameter("ValueTypeList") + .HasParameter("ValueTypeIList") + .HasParameter("ValueTypeArray") + .HasParameter("ValueTypeEnumerable") + .HasParameter("RefTypeList") + .HasParameter("RefTypeIList") + .HasParameter("RefTypeArray") + .HasParameter("RefTypeEnumerable") .HasOriginalValueParameter(p => p.Id)); eb.DeleteUsingStoredProcedure( s => s @@ -8363,6 +12789,46 @@ private IRelationalModel CreateRelationalModel() IsNullable = true }; microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("PrincipalDerived>Id", principalDerivedDependentBasebyteIdColumnBase); + var refTypeArrayColumnBase = new ColumnBase("RefTypeArray", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("RefTypeArray", refTypeArrayColumnBase); + var refTypeEnumerableColumnBase = new ColumnBase("RefTypeEnumerable", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("RefTypeEnumerable", refTypeEnumerableColumnBase); + var refTypeIListColumnBase = new ColumnBase("RefTypeIList", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("RefTypeIList", refTypeIListColumnBase); + var refTypeListColumnBase = new ColumnBase("RefTypeList", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("RefTypeList", refTypeListColumnBase); + var valueTypeArrayColumnBase = new ColumnBase("ValueTypeArray", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("ValueTypeArray", valueTypeArrayColumnBase); + var valueTypeEnumerableColumnBase = new ColumnBase("ValueTypeEnumerable", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("ValueTypeEnumerable", valueTypeEnumerableColumnBase); + var valueTypeIListColumnBase = new ColumnBase("ValueTypeIList", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("ValueTypeIList", valueTypeIListColumnBase); + var valueTypeListColumnBase = new ColumnBase("ValueTypeList", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("ValueTypeList", valueTypeListColumnBase); relationalModel.DefaultTables.Add("Microsoft.EntityFrameworkCore.Scaffolding.Internal.CSharpRuntimeModelCodeGeneratorTest+PrincipalBase", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase); var microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase = new TableMappingBase(principalBase, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase, false); microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.AddTypeMapping(microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase, false); @@ -8374,6 +12840,14 @@ private IRelationalModel CreateRelationalModel() RelationalModel.CreateColumnMapping((ColumnBase)flagsEnum2ColumnBase, principalBase.FindProperty("FlagsEnum2")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); RelationalModel.CreateColumnMapping((ColumnBase)principalBaseIdColumnBase, principalBase.FindProperty("PrincipalBaseId")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); RelationalModel.CreateColumnMapping((ColumnBase)principalDerivedDependentBasebyteIdColumnBase, principalBase.FindProperty("PrincipalDerivedId")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)refTypeArrayColumnBase, principalBase.FindProperty("RefTypeArray")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)refTypeEnumerableColumnBase, principalBase.FindProperty("RefTypeEnumerable")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)refTypeIListColumnBase, principalBase.FindProperty("RefTypeIList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)refTypeListColumnBase, principalBase.FindProperty("RefTypeList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)valueTypeArrayColumnBase, principalBase.FindProperty("ValueTypeArray")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)valueTypeEnumerableColumnBase, principalBase.FindProperty("ValueTypeEnumerable")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)valueTypeIListColumnBase, principalBase.FindProperty("ValueTypeIList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)valueTypeListColumnBase, principalBase.FindProperty("ValueTypeList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase); var tableMappings0 = new List(); principalBase.SetRuntimeAnnotation("Relational:TableMappings", tableMappings0); @@ -8401,6 +12875,46 @@ private IRelationalModel CreateRelationalModel() IsNullable = true }; principalBaseTable.Columns.Add("PrincipalDerived>Id", principalDerivedDependentBasebyteIdColumn); + var refTypeArrayColumn = new Column("RefTypeArray", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("RefTypeArray", refTypeArrayColumn); + var refTypeEnumerableColumn = new Column("RefTypeEnumerable", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("RefTypeEnumerable", refTypeEnumerableColumn); + var refTypeIListColumn = new Column("RefTypeIList", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("RefTypeIList", refTypeIListColumn); + var refTypeListColumn = new Column("RefTypeList", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("RefTypeList", refTypeListColumn); + var valueTypeArrayColumn = new Column("ValueTypeArray", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("ValueTypeArray", valueTypeArrayColumn); + var valueTypeEnumerableColumn = new Column("ValueTypeEnumerable", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("ValueTypeEnumerable", valueTypeEnumerableColumn); + var valueTypeIListColumn = new Column("ValueTypeIList", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("ValueTypeIList", valueTypeIListColumn); + var valueTypeListColumn = new Column("ValueTypeList", "nvarchar(max)", principalBaseTable) + { + IsNullable = true + }; + principalBaseTable.Columns.Add("ValueTypeList", valueTypeListColumn); var pK_PrincipalBase = new UniqueConstraint("PK_PrincipalBase", principalBaseTable, new[] { idColumn0 }); principalBaseTable.PrimaryKey = pK_PrincipalBase; var pK_PrincipalBaseUc = RelationalModel.GetKey(this, @@ -8436,6 +12950,14 @@ private IRelationalModel CreateRelationalModel() RelationalModel.CreateColumnMapping(flagsEnum2Column, principalBase.FindProperty("FlagsEnum2")!, principalBaseTableMapping); RelationalModel.CreateColumnMapping(principalBaseIdColumn, principalBase.FindProperty("PrincipalBaseId")!, principalBaseTableMapping); RelationalModel.CreateColumnMapping(principalDerivedDependentBasebyteIdColumn, principalBase.FindProperty("PrincipalDerivedId")!, principalBaseTableMapping); + RelationalModel.CreateColumnMapping(refTypeArrayColumn, principalBase.FindProperty("RefTypeArray")!, principalBaseTableMapping); + RelationalModel.CreateColumnMapping(refTypeEnumerableColumn, principalBase.FindProperty("RefTypeEnumerable")!, principalBaseTableMapping); + RelationalModel.CreateColumnMapping(refTypeIListColumn, principalBase.FindProperty("RefTypeIList")!, principalBaseTableMapping); + RelationalModel.CreateColumnMapping(refTypeListColumn, principalBase.FindProperty("RefTypeList")!, principalBaseTableMapping); + RelationalModel.CreateColumnMapping(valueTypeArrayColumn, principalBase.FindProperty("ValueTypeArray")!, principalBaseTableMapping); + RelationalModel.CreateColumnMapping(valueTypeEnumerableColumn, principalBase.FindProperty("ValueTypeEnumerable")!, principalBaseTableMapping); + RelationalModel.CreateColumnMapping(valueTypeIListColumn, principalBase.FindProperty("ValueTypeIList")!, principalBaseTableMapping); + RelationalModel.CreateColumnMapping(valueTypeListColumn, principalBase.FindProperty("ValueTypeList")!, principalBaseTableMapping); var viewMappings = new List(); principalBase.SetRuntimeAnnotation("Relational:ViewMappings", viewMappings); @@ -8463,6 +12985,46 @@ private IRelationalModel CreateRelationalModel() IsNullable = true }; principalBaseViewView.Columns.Add("PrincipalDerivedId", principalDerivedIdViewColumn); + var refTypeArrayViewColumn = new ViewColumn("RefTypeArray", "nvarchar(max)", principalBaseViewView) + { + IsNullable = true + }; + principalBaseViewView.Columns.Add("RefTypeArray", refTypeArrayViewColumn); + var refTypeEnumerableViewColumn = new ViewColumn("RefTypeEnumerable", "nvarchar(max)", principalBaseViewView) + { + IsNullable = true + }; + principalBaseViewView.Columns.Add("RefTypeEnumerable", refTypeEnumerableViewColumn); + var refTypeIListViewColumn = new ViewColumn("RefTypeIList", "nvarchar(max)", principalBaseViewView) + { + IsNullable = true + }; + principalBaseViewView.Columns.Add("RefTypeIList", refTypeIListViewColumn); + var refTypeListViewColumn = new ViewColumn("RefTypeList", "nvarchar(max)", principalBaseViewView) + { + IsNullable = true + }; + principalBaseViewView.Columns.Add("RefTypeList", refTypeListViewColumn); + var valueTypeArrayViewColumn = new ViewColumn("ValueTypeArray", "nvarchar(max)", principalBaseViewView) + { + IsNullable = true + }; + principalBaseViewView.Columns.Add("ValueTypeArray", valueTypeArrayViewColumn); + var valueTypeEnumerableViewColumn = new ViewColumn("ValueTypeEnumerable", "nvarchar(max)", principalBaseViewView) + { + IsNullable = true + }; + principalBaseViewView.Columns.Add("ValueTypeEnumerable", valueTypeEnumerableViewColumn); + var valueTypeIListViewColumn = new ViewColumn("ValueTypeIList", "nvarchar(max)", principalBaseViewView) + { + IsNullable = true + }; + principalBaseViewView.Columns.Add("ValueTypeIList", valueTypeIListViewColumn); + var valueTypeListViewColumn = new ViewColumn("ValueTypeList", "nvarchar(max)", principalBaseViewView) + { + IsNullable = true + }; + principalBaseViewView.Columns.Add("ValueTypeList", valueTypeListViewColumn); relationalModel.Views.Add(("PrincipalBaseView", "TPC"), principalBaseViewView); var principalBaseViewViewMapping = new ViewMapping(principalBase, principalBaseViewView, false); principalBaseViewView.AddTypeMapping(principalBaseViewViewMapping, false); @@ -8474,6 +13036,14 @@ private IRelationalModel CreateRelationalModel() RelationalModel.CreateViewColumnMapping(flagsEnum2ViewColumn, principalBase.FindProperty("FlagsEnum2")!, principalBaseViewViewMapping); RelationalModel.CreateViewColumnMapping(principalBaseIdViewColumn, principalBase.FindProperty("PrincipalBaseId")!, principalBaseViewViewMapping); RelationalModel.CreateViewColumnMapping(principalDerivedIdViewColumn, principalBase.FindProperty("PrincipalDerivedId")!, principalBaseViewViewMapping); + RelationalModel.CreateViewColumnMapping(refTypeArrayViewColumn, principalBase.FindProperty("RefTypeArray")!, principalBaseViewViewMapping); + RelationalModel.CreateViewColumnMapping(refTypeEnumerableViewColumn, principalBase.FindProperty("RefTypeEnumerable")!, principalBaseViewViewMapping); + RelationalModel.CreateViewColumnMapping(refTypeIListViewColumn, principalBase.FindProperty("RefTypeIList")!, principalBaseViewViewMapping); + RelationalModel.CreateViewColumnMapping(refTypeListViewColumn, principalBase.FindProperty("RefTypeList")!, principalBaseViewViewMapping); + RelationalModel.CreateViewColumnMapping(valueTypeArrayViewColumn, principalBase.FindProperty("ValueTypeArray")!, principalBaseViewViewMapping); + RelationalModel.CreateViewColumnMapping(valueTypeEnumerableViewColumn, principalBase.FindProperty("ValueTypeEnumerable")!, principalBaseViewViewMapping); + RelationalModel.CreateViewColumnMapping(valueTypeIListViewColumn, principalBase.FindProperty("ValueTypeIList")!, principalBaseViewViewMapping); + RelationalModel.CreateViewColumnMapping(valueTypeListViewColumn, principalBase.FindProperty("ValueTypeList")!, principalBaseViewViewMapping); var deleteSprocMappings = new List(); principalBase.SetRuntimeAnnotation("Relational:DeleteStoredProcedureMappings", deleteSprocMappings); @@ -8514,7 +13084,47 @@ private IRelationalModel CreateRelationalModel() principalBase_InsertStoreSproc.AddParameter(flagsEnum1Parameter); var flagsEnum2Parameter = new StoreStoredProcedureParameter("FlagsEnum2", "int", 5, principalBase_InsertStoreSproc, System.Data.ParameterDirection.Input); principalBase_InsertStoreSproc.AddParameter(flagsEnum2Parameter); - var baseIdParameter = new StoreStoredProcedureParameter("BaseId", "bigint", 6, principalBase_InsertStoreSproc, System.Data.ParameterDirection.Output); + var valueTypeListParameter = new StoreStoredProcedureParameter("ValueTypeList", "nvarchar(max)", 6, principalBase_InsertStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + principalBase_InsertStoreSproc.AddParameter(valueTypeListParameter); + var valueTypeIListParameter = new StoreStoredProcedureParameter("ValueTypeIList", "nvarchar(max)", 7, principalBase_InsertStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + principalBase_InsertStoreSproc.AddParameter(valueTypeIListParameter); + var valueTypeArrayParameter = new StoreStoredProcedureParameter("ValueTypeArray", "nvarchar(max)", 8, principalBase_InsertStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + principalBase_InsertStoreSproc.AddParameter(valueTypeArrayParameter); + var valueTypeEnumerableParameter = new StoreStoredProcedureParameter("ValueTypeEnumerable", "nvarchar(max)", 9, principalBase_InsertStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + principalBase_InsertStoreSproc.AddParameter(valueTypeEnumerableParameter); + var refTypeListParameter = new StoreStoredProcedureParameter("RefTypeList", "nvarchar(max)", 10, principalBase_InsertStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + principalBase_InsertStoreSproc.AddParameter(refTypeListParameter); + var refTypeIListParameter = new StoreStoredProcedureParameter("RefTypeIList", "nvarchar(max)", 11, principalBase_InsertStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + principalBase_InsertStoreSproc.AddParameter(refTypeIListParameter); + var refTypeArrayParameter = new StoreStoredProcedureParameter("RefTypeArray", "nvarchar(max)", 12, principalBase_InsertStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + principalBase_InsertStoreSproc.AddParameter(refTypeArrayParameter); + var refTypeEnumerableParameter = new StoreStoredProcedureParameter("RefTypeEnumerable", "nvarchar(max)", 13, principalBase_InsertStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + principalBase_InsertStoreSproc.AddParameter(refTypeEnumerableParameter); + var baseIdParameter = new StoreStoredProcedureParameter("BaseId", "bigint", 14, principalBase_InsertStoreSproc, System.Data.ParameterDirection.Output); principalBase_InsertStoreSproc.AddParameter(baseIdParameter); principalBase_InsertStoreSproc.AddStoredProcedure((IRuntimeStoredProcedure)principalBase.GetInsertStoredProcedure()!); relationalModel.StoredProcedures.Add(("PrincipalBase_Insert", "TPC"), principalBase_InsertStoreSproc); @@ -8530,6 +13140,14 @@ private IRelationalModel CreateRelationalModel() RelationalModel.CreateStoredProcedureParameterMapping(flagsEnum2Parameter, principalBase_InsertISproc.FindParameter("FlagsEnum2")!, principalBase.FindProperty("FlagsEnum2")!, principalBase_InsertSprocMapping); RelationalModel.CreateStoredProcedureParameterMapping(principalBaseIdParameter, principalBase_InsertISproc.FindParameter("PrincipalBaseId")!, principalBase.FindProperty("PrincipalBaseId")!, principalBase_InsertSprocMapping); RelationalModel.CreateStoredProcedureParameterMapping(principalDerivedIdParameter, principalBase_InsertISproc.FindParameter("PrincipalDerivedId")!, principalBase.FindProperty("PrincipalDerivedId")!, principalBase_InsertSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(refTypeArrayParameter, principalBase_InsertISproc.FindParameter("RefTypeArray")!, principalBase.FindProperty("RefTypeArray")!, principalBase_InsertSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(refTypeEnumerableParameter, principalBase_InsertISproc.FindParameter("RefTypeEnumerable")!, principalBase.FindProperty("RefTypeEnumerable")!, principalBase_InsertSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(refTypeIListParameter, principalBase_InsertISproc.FindParameter("RefTypeIList")!, principalBase.FindProperty("RefTypeIList")!, principalBase_InsertSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(refTypeListParameter, principalBase_InsertISproc.FindParameter("RefTypeList")!, principalBase.FindProperty("RefTypeList")!, principalBase_InsertSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(valueTypeArrayParameter, principalBase_InsertISproc.FindParameter("ValueTypeArray")!, principalBase.FindProperty("ValueTypeArray")!, principalBase_InsertSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(valueTypeEnumerableParameter, principalBase_InsertISproc.FindParameter("ValueTypeEnumerable")!, principalBase.FindProperty("ValueTypeEnumerable")!, principalBase_InsertSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(valueTypeIListParameter, principalBase_InsertISproc.FindParameter("ValueTypeIList")!, principalBase.FindProperty("ValueTypeIList")!, principalBase_InsertSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(valueTypeListParameter, principalBase_InsertISproc.FindParameter("ValueTypeList")!, principalBase.FindProperty("ValueTypeList")!, principalBase_InsertSprocMapping); var updateSprocMappings = new List(); principalBase.SetRuntimeAnnotation("Relational:UpdateStoredProcedureMappings", updateSprocMappings); @@ -8555,7 +13173,47 @@ private IRelationalModel CreateRelationalModel() principalBase_UpdateStoreSproc.AddParameter(flagsEnum1Parameter0); var flagsEnum2Parameter0 = new StoreStoredProcedureParameter("FlagsEnum2", "int", 5, principalBase_UpdateStoreSproc, System.Data.ParameterDirection.Input); principalBase_UpdateStoreSproc.AddParameter(flagsEnum2Parameter0); - var id_OriginalParameter0 = new StoreStoredProcedureParameter("Id_Original", "bigint", 6, principalBase_UpdateStoreSproc, System.Data.ParameterDirection.Input); + var valueTypeListParameter0 = new StoreStoredProcedureParameter("ValueTypeList", "nvarchar(max)", 6, principalBase_UpdateStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + principalBase_UpdateStoreSproc.AddParameter(valueTypeListParameter0); + var valueTypeIListParameter0 = new StoreStoredProcedureParameter("ValueTypeIList", "nvarchar(max)", 7, principalBase_UpdateStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + principalBase_UpdateStoreSproc.AddParameter(valueTypeIListParameter0); + var valueTypeArrayParameter0 = new StoreStoredProcedureParameter("ValueTypeArray", "nvarchar(max)", 8, principalBase_UpdateStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + principalBase_UpdateStoreSproc.AddParameter(valueTypeArrayParameter0); + var valueTypeEnumerableParameter0 = new StoreStoredProcedureParameter("ValueTypeEnumerable", "nvarchar(max)", 9, principalBase_UpdateStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + principalBase_UpdateStoreSproc.AddParameter(valueTypeEnumerableParameter0); + var refTypeListParameter0 = new StoreStoredProcedureParameter("RefTypeList", "nvarchar(max)", 10, principalBase_UpdateStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + principalBase_UpdateStoreSproc.AddParameter(refTypeListParameter0); + var refTypeIListParameter0 = new StoreStoredProcedureParameter("RefTypeIList", "nvarchar(max)", 11, principalBase_UpdateStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + principalBase_UpdateStoreSproc.AddParameter(refTypeIListParameter0); + var refTypeArrayParameter0 = new StoreStoredProcedureParameter("RefTypeArray", "nvarchar(max)", 12, principalBase_UpdateStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + principalBase_UpdateStoreSproc.AddParameter(refTypeArrayParameter0); + var refTypeEnumerableParameter0 = new StoreStoredProcedureParameter("RefTypeEnumerable", "nvarchar(max)", 13, principalBase_UpdateStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + principalBase_UpdateStoreSproc.AddParameter(refTypeEnumerableParameter0); + var id_OriginalParameter0 = new StoreStoredProcedureParameter("Id_Original", "bigint", 14, principalBase_UpdateStoreSproc, System.Data.ParameterDirection.Input); principalBase_UpdateStoreSproc.AddParameter(id_OriginalParameter0); principalBase_UpdateStoreSproc.AddStoredProcedure((IRuntimeStoredProcedure)principalBase.GetUpdateStoredProcedure()!); relationalModel.StoredProcedures.Add(("PrincipalBase_Update", "TPC"), principalBase_UpdateStoreSproc); @@ -8571,6 +13229,14 @@ private IRelationalModel CreateRelationalModel() RelationalModel.CreateStoredProcedureParameterMapping(flagsEnum2Parameter0, principalBase_UpdateUSproc.FindParameter("FlagsEnum2")!, principalBase.FindProperty("FlagsEnum2")!, principalBase_UpdateSprocMapping); RelationalModel.CreateStoredProcedureParameterMapping(principalBaseIdParameter0, principalBase_UpdateUSproc.FindParameter("PrincipalBaseId")!, principalBase.FindProperty("PrincipalBaseId")!, principalBase_UpdateSprocMapping); RelationalModel.CreateStoredProcedureParameterMapping(principalDerivedIdParameter0, principalBase_UpdateUSproc.FindParameter("PrincipalDerivedId")!, principalBase.FindProperty("PrincipalDerivedId")!, principalBase_UpdateSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(refTypeArrayParameter0, principalBase_UpdateUSproc.FindParameter("RefTypeArray")!, principalBase.FindProperty("RefTypeArray")!, principalBase_UpdateSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(refTypeEnumerableParameter0, principalBase_UpdateUSproc.FindParameter("RefTypeEnumerable")!, principalBase.FindProperty("RefTypeEnumerable")!, principalBase_UpdateSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(refTypeIListParameter0, principalBase_UpdateUSproc.FindParameter("RefTypeIList")!, principalBase.FindProperty("RefTypeIList")!, principalBase_UpdateSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(refTypeListParameter0, principalBase_UpdateUSproc.FindParameter("RefTypeList")!, principalBase.FindProperty("RefTypeList")!, principalBase_UpdateSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(valueTypeArrayParameter0, principalBase_UpdateUSproc.FindParameter("ValueTypeArray")!, principalBase.FindProperty("ValueTypeArray")!, principalBase_UpdateSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(valueTypeEnumerableParameter0, principalBase_UpdateUSproc.FindParameter("ValueTypeEnumerable")!, principalBase.FindProperty("ValueTypeEnumerable")!, principalBase_UpdateSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(valueTypeIListParameter0, principalBase_UpdateUSproc.FindParameter("ValueTypeIList")!, principalBase.FindProperty("ValueTypeIList")!, principalBase_UpdateSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(valueTypeListParameter0, principalBase_UpdateUSproc.FindParameter("ValueTypeList")!, principalBase.FindProperty("ValueTypeList")!, principalBase_UpdateSprocMapping); var principalDerived = FindEntityType("Microsoft.EntityFrameworkCore.Scaffolding.Internal.CSharpRuntimeModelCodeGeneratorTest+PrincipalDerived>")!; @@ -8583,23 +13249,63 @@ private IRelationalModel CreateRelationalModel() { IsNullable = true }; - microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteTableBase.Columns.Add("Enum2", enum2ColumnBase0); - var flagsEnum1ColumnBase0 = new ColumnBase("FlagsEnum1", "int", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteTableBase); - microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteTableBase.Columns.Add("FlagsEnum1", flagsEnum1ColumnBase0); - var flagsEnum2ColumnBase0 = new ColumnBase("FlagsEnum2", "int", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteTableBase); - microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteTableBase.Columns.Add("FlagsEnum2", flagsEnum2ColumnBase0); - var idColumnBase1 = new ColumnBase("Id", "bigint", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteTableBase); - microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteTableBase.Columns.Add("Id", idColumnBase1); - var principalBaseIdColumnBase0 = new ColumnBase("PrincipalBaseId", "bigint", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteTableBase) + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteTableBase.Columns.Add("Enum2", enum2ColumnBase0); + var flagsEnum1ColumnBase0 = new ColumnBase("FlagsEnum1", "int", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteTableBase); + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteTableBase.Columns.Add("FlagsEnum1", flagsEnum1ColumnBase0); + var flagsEnum2ColumnBase0 = new ColumnBase("FlagsEnum2", "int", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteTableBase); + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteTableBase.Columns.Add("FlagsEnum2", flagsEnum2ColumnBase0); + var idColumnBase1 = new ColumnBase("Id", "bigint", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteTableBase); + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteTableBase.Columns.Add("Id", idColumnBase1); + var principalBaseIdColumnBase0 = new ColumnBase("PrincipalBaseId", "bigint", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteTableBase.Columns.Add("PrincipalBaseId", principalBaseIdColumnBase0); + var principalDerivedDependentBasebyteIdColumnBase0 = new ColumnBase("PrincipalDerived>Id", "bigint", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteTableBase.Columns.Add("PrincipalDerived>Id", principalDerivedDependentBasebyteIdColumnBase0); + var refTypeArrayColumnBase0 = new ColumnBase("RefTypeArray", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteTableBase.Columns.Add("RefTypeArray", refTypeArrayColumnBase0); + var refTypeEnumerableColumnBase0 = new ColumnBase("RefTypeEnumerable", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteTableBase.Columns.Add("RefTypeEnumerable", refTypeEnumerableColumnBase0); + var refTypeIListColumnBase0 = new ColumnBase("RefTypeIList", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteTableBase.Columns.Add("RefTypeIList", refTypeIListColumnBase0); + var refTypeListColumnBase0 = new ColumnBase("RefTypeList", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteTableBase.Columns.Add("RefTypeList", refTypeListColumnBase0); + var valueTypeArrayColumnBase0 = new ColumnBase("ValueTypeArray", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteTableBase.Columns.Add("ValueTypeArray", valueTypeArrayColumnBase0); + var valueTypeEnumerableColumnBase0 = new ColumnBase("ValueTypeEnumerable", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteTableBase) + { + IsNullable = true + }; + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteTableBase.Columns.Add("ValueTypeEnumerable", valueTypeEnumerableColumnBase0); + var valueTypeIListColumnBase0 = new ColumnBase("ValueTypeIList", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteTableBase) { IsNullable = true }; - microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteTableBase.Columns.Add("PrincipalBaseId", principalBaseIdColumnBase0); - var principalDerivedDependentBasebyteIdColumnBase0 = new ColumnBase("PrincipalDerived>Id", "bigint", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteTableBase) + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteTableBase.Columns.Add("ValueTypeIList", valueTypeIListColumnBase0); + var valueTypeListColumnBase0 = new ColumnBase("ValueTypeList", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteTableBase) { IsNullable = true }; - microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteTableBase.Columns.Add("PrincipalDerived>Id", principalDerivedDependentBasebyteIdColumnBase0); + microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteTableBase.Columns.Add("ValueTypeList", valueTypeListColumnBase0); relationalModel.DefaultTables.Add("Microsoft.EntityFrameworkCore.Scaffolding.Internal.CSharpRuntimeModelCodeGeneratorTest+PrincipalDerived>", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteTableBase); var microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteMappingBase = new TableMappingBase(principalDerived, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteTableBase, false); microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteTableBase.AddTypeMapping(microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteMappingBase, false); @@ -8611,6 +13317,14 @@ private IRelationalModel CreateRelationalModel() RelationalModel.CreateColumnMapping((ColumnBase)flagsEnum2ColumnBase0, principalDerived.FindProperty("FlagsEnum2")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteMappingBase); RelationalModel.CreateColumnMapping((ColumnBase)principalBaseIdColumnBase0, principalDerived.FindProperty("PrincipalBaseId")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteMappingBase); RelationalModel.CreateColumnMapping((ColumnBase)principalDerivedDependentBasebyteIdColumnBase0, principalDerived.FindProperty("PrincipalDerivedId")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)refTypeArrayColumnBase0, principalDerived.FindProperty("RefTypeArray")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)refTypeEnumerableColumnBase0, principalDerived.FindProperty("RefTypeEnumerable")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)refTypeIListColumnBase0, principalDerived.FindProperty("RefTypeIList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)refTypeListColumnBase0, principalDerived.FindProperty("RefTypeList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)valueTypeArrayColumnBase0, principalDerived.FindProperty("ValueTypeArray")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)valueTypeEnumerableColumnBase0, principalDerived.FindProperty("ValueTypeEnumerable")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)valueTypeIListColumnBase0, principalDerived.FindProperty("ValueTypeIList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteMappingBase); + RelationalModel.CreateColumnMapping((ColumnBase)valueTypeListColumnBase0, principalDerived.FindProperty("ValueTypeList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalDerivedMicrosoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestDependentBasebyteMappingBase); var tableMappings1 = new List(); principalDerived.SetRuntimeAnnotation("Relational:TableMappings", tableMappings1); @@ -8638,6 +13352,46 @@ private IRelationalModel CreateRelationalModel() IsNullable = true }; principalDerivedTable.Columns.Add("PrincipalDerived>Id", principalDerivedDependentBasebyteIdColumn0); + var refTypeArrayColumn0 = new Column("RefTypeArray", "nvarchar(max)", principalDerivedTable) + { + IsNullable = true + }; + principalDerivedTable.Columns.Add("RefTypeArray", refTypeArrayColumn0); + var refTypeEnumerableColumn0 = new Column("RefTypeEnumerable", "nvarchar(max)", principalDerivedTable) + { + IsNullable = true + }; + principalDerivedTable.Columns.Add("RefTypeEnumerable", refTypeEnumerableColumn0); + var refTypeIListColumn0 = new Column("RefTypeIList", "nvarchar(max)", principalDerivedTable) + { + IsNullable = true + }; + principalDerivedTable.Columns.Add("RefTypeIList", refTypeIListColumn0); + var refTypeListColumn0 = new Column("RefTypeList", "nvarchar(max)", principalDerivedTable) + { + IsNullable = true + }; + principalDerivedTable.Columns.Add("RefTypeList", refTypeListColumn0); + var valueTypeArrayColumn0 = new Column("ValueTypeArray", "nvarchar(max)", principalDerivedTable) + { + IsNullable = true + }; + principalDerivedTable.Columns.Add("ValueTypeArray", valueTypeArrayColumn0); + var valueTypeEnumerableColumn0 = new Column("ValueTypeEnumerable", "nvarchar(max)", principalDerivedTable) + { + IsNullable = true + }; + principalDerivedTable.Columns.Add("ValueTypeEnumerable", valueTypeEnumerableColumn0); + var valueTypeIListColumn0 = new Column("ValueTypeIList", "nvarchar(max)", principalDerivedTable) + { + IsNullable = true + }; + principalDerivedTable.Columns.Add("ValueTypeIList", valueTypeIListColumn0); + var valueTypeListColumn0 = new Column("ValueTypeList", "nvarchar(max)", principalDerivedTable) + { + IsNullable = true + }; + principalDerivedTable.Columns.Add("ValueTypeList", valueTypeListColumn0); var pK_PrincipalDerived = new UniqueConstraint("PK_PrincipalDerived", principalDerivedTable, new[] { idColumn1 }); principalDerivedTable.PrimaryKey = pK_PrincipalDerived; var pK_PrincipalDerivedUc = RelationalModel.GetKey(this, @@ -8673,6 +13427,14 @@ private IRelationalModel CreateRelationalModel() RelationalModel.CreateColumnMapping(flagsEnum2Column0, principalDerived.FindProperty("FlagsEnum2")!, principalDerivedTableMapping); RelationalModel.CreateColumnMapping(principalBaseIdColumn0, principalDerived.FindProperty("PrincipalBaseId")!, principalDerivedTableMapping); RelationalModel.CreateColumnMapping(principalDerivedDependentBasebyteIdColumn0, principalDerived.FindProperty("PrincipalDerivedId")!, principalDerivedTableMapping); + RelationalModel.CreateColumnMapping(refTypeArrayColumn0, principalDerived.FindProperty("RefTypeArray")!, principalDerivedTableMapping); + RelationalModel.CreateColumnMapping(refTypeEnumerableColumn0, principalDerived.FindProperty("RefTypeEnumerable")!, principalDerivedTableMapping); + RelationalModel.CreateColumnMapping(refTypeIListColumn0, principalDerived.FindProperty("RefTypeIList")!, principalDerivedTableMapping); + RelationalModel.CreateColumnMapping(refTypeListColumn0, principalDerived.FindProperty("RefTypeList")!, principalDerivedTableMapping); + RelationalModel.CreateColumnMapping(valueTypeArrayColumn0, principalDerived.FindProperty("ValueTypeArray")!, principalDerivedTableMapping); + RelationalModel.CreateColumnMapping(valueTypeEnumerableColumn0, principalDerived.FindProperty("ValueTypeEnumerable")!, principalDerivedTableMapping); + RelationalModel.CreateColumnMapping(valueTypeIListColumn0, principalDerived.FindProperty("ValueTypeIList")!, principalDerivedTableMapping); + RelationalModel.CreateColumnMapping(valueTypeListColumn0, principalDerived.FindProperty("ValueTypeList")!, principalDerivedTableMapping); var viewMappings0 = new List(); principalDerived.SetRuntimeAnnotation("Relational:ViewMappings", viewMappings0); @@ -8700,6 +13462,46 @@ private IRelationalModel CreateRelationalModel() IsNullable = true }; principalDerivedViewView.Columns.Add("PrincipalDerivedId", principalDerivedIdViewColumn0); + var refTypeArrayViewColumn0 = new ViewColumn("RefTypeArray", "nvarchar(max)", principalDerivedViewView) + { + IsNullable = true + }; + principalDerivedViewView.Columns.Add("RefTypeArray", refTypeArrayViewColumn0); + var refTypeEnumerableViewColumn0 = new ViewColumn("RefTypeEnumerable", "nvarchar(max)", principalDerivedViewView) + { + IsNullable = true + }; + principalDerivedViewView.Columns.Add("RefTypeEnumerable", refTypeEnumerableViewColumn0); + var refTypeIListViewColumn0 = new ViewColumn("RefTypeIList", "nvarchar(max)", principalDerivedViewView) + { + IsNullable = true + }; + principalDerivedViewView.Columns.Add("RefTypeIList", refTypeIListViewColumn0); + var refTypeListViewColumn0 = new ViewColumn("RefTypeList", "nvarchar(max)", principalDerivedViewView) + { + IsNullable = true + }; + principalDerivedViewView.Columns.Add("RefTypeList", refTypeListViewColumn0); + var valueTypeArrayViewColumn0 = new ViewColumn("ValueTypeArray", "nvarchar(max)", principalDerivedViewView) + { + IsNullable = true + }; + principalDerivedViewView.Columns.Add("ValueTypeArray", valueTypeArrayViewColumn0); + var valueTypeEnumerableViewColumn0 = new ViewColumn("ValueTypeEnumerable", "nvarchar(max)", principalDerivedViewView) + { + IsNullable = true + }; + principalDerivedViewView.Columns.Add("ValueTypeEnumerable", valueTypeEnumerableViewColumn0); + var valueTypeIListViewColumn0 = new ViewColumn("ValueTypeIList", "nvarchar(max)", principalDerivedViewView) + { + IsNullable = true + }; + principalDerivedViewView.Columns.Add("ValueTypeIList", valueTypeIListViewColumn0); + var valueTypeListViewColumn0 = new ViewColumn("ValueTypeList", "nvarchar(max)", principalDerivedViewView) + { + IsNullable = true + }; + principalDerivedViewView.Columns.Add("ValueTypeList", valueTypeListViewColumn0); relationalModel.Views.Add(("PrincipalDerivedView", "TPC"), principalDerivedViewView); var principalDerivedViewViewMapping = new ViewMapping(principalDerived, principalDerivedViewView, false); principalDerivedViewView.AddTypeMapping(principalDerivedViewViewMapping, false); @@ -8711,6 +13513,14 @@ private IRelationalModel CreateRelationalModel() RelationalModel.CreateViewColumnMapping(flagsEnum2ViewColumn0, principalDerived.FindProperty("FlagsEnum2")!, principalDerivedViewViewMapping); RelationalModel.CreateViewColumnMapping(principalBaseIdViewColumn0, principalDerived.FindProperty("PrincipalBaseId")!, principalDerivedViewViewMapping); RelationalModel.CreateViewColumnMapping(principalDerivedIdViewColumn0, principalDerived.FindProperty("PrincipalDerivedId")!, principalDerivedViewViewMapping); + RelationalModel.CreateViewColumnMapping(refTypeArrayViewColumn0, principalDerived.FindProperty("RefTypeArray")!, principalDerivedViewViewMapping); + RelationalModel.CreateViewColumnMapping(refTypeEnumerableViewColumn0, principalDerived.FindProperty("RefTypeEnumerable")!, principalDerivedViewViewMapping); + RelationalModel.CreateViewColumnMapping(refTypeIListViewColumn0, principalDerived.FindProperty("RefTypeIList")!, principalDerivedViewViewMapping); + RelationalModel.CreateViewColumnMapping(refTypeListViewColumn0, principalDerived.FindProperty("RefTypeList")!, principalDerivedViewViewMapping); + RelationalModel.CreateViewColumnMapping(valueTypeArrayViewColumn0, principalDerived.FindProperty("ValueTypeArray")!, principalDerivedViewViewMapping); + RelationalModel.CreateViewColumnMapping(valueTypeEnumerableViewColumn0, principalDerived.FindProperty("ValueTypeEnumerable")!, principalDerivedViewViewMapping); + RelationalModel.CreateViewColumnMapping(valueTypeIListViewColumn0, principalDerived.FindProperty("ValueTypeIList")!, principalDerivedViewViewMapping); + RelationalModel.CreateViewColumnMapping(valueTypeListViewColumn0, principalDerived.FindProperty("ValueTypeList")!, principalDerivedViewViewMapping); var deleteSprocMappings0 = new List(); principalDerived.SetRuntimeAnnotation("Relational:DeleteStoredProcedureMappings", deleteSprocMappings0); @@ -8750,6 +13560,46 @@ private IRelationalModel CreateRelationalModel() derived_InsertStoreSproc.AddParameter(flagsEnum1Parameter1); var flagsEnum2Parameter1 = new StoreStoredProcedureParameter("FlagsEnum2", "int", 5, derived_InsertStoreSproc, System.Data.ParameterDirection.Input); derived_InsertStoreSproc.AddParameter(flagsEnum2Parameter1); + var valueTypeListParameter1 = new StoreStoredProcedureParameter("ValueTypeList", "nvarchar(max)", 6, derived_InsertStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + derived_InsertStoreSproc.AddParameter(valueTypeListParameter1); + var valueTypeIListParameter1 = new StoreStoredProcedureParameter("ValueTypeIList", "nvarchar(max)", 7, derived_InsertStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + derived_InsertStoreSproc.AddParameter(valueTypeIListParameter1); + var valueTypeArrayParameter1 = new StoreStoredProcedureParameter("ValueTypeArray", "nvarchar(max)", 8, derived_InsertStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + derived_InsertStoreSproc.AddParameter(valueTypeArrayParameter1); + var valueTypeEnumerableParameter1 = new StoreStoredProcedureParameter("ValueTypeEnumerable", "nvarchar(max)", 9, derived_InsertStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + derived_InsertStoreSproc.AddParameter(valueTypeEnumerableParameter1); + var refTypeListParameter1 = new StoreStoredProcedureParameter("RefTypeList", "nvarchar(max)", 10, derived_InsertStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + derived_InsertStoreSproc.AddParameter(refTypeListParameter1); + var refTypeIListParameter1 = new StoreStoredProcedureParameter("RefTypeIList", "nvarchar(max)", 11, derived_InsertStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + derived_InsertStoreSproc.AddParameter(refTypeIListParameter1); + var refTypeArrayParameter1 = new StoreStoredProcedureParameter("RefTypeArray", "nvarchar(max)", 12, derived_InsertStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + derived_InsertStoreSproc.AddParameter(refTypeArrayParameter1); + var refTypeEnumerableParameter1 = new StoreStoredProcedureParameter("RefTypeEnumerable", "nvarchar(max)", 13, derived_InsertStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + derived_InsertStoreSproc.AddParameter(refTypeEnumerableParameter1); var derivedIdFunctionColumn = new StoreStoredProcedureResultColumn("DerivedId", "bigint", 0, derived_InsertStoreSproc); derived_InsertStoreSproc.AddResultColumn(derivedIdFunctionColumn); derived_InsertStoreSproc.AddStoredProcedure((IRuntimeStoredProcedure)principalDerived.GetInsertStoredProcedure()!); @@ -8765,6 +13615,14 @@ private IRelationalModel CreateRelationalModel() RelationalModel.CreateStoredProcedureParameterMapping(flagsEnum2Parameter1, derived_InsertISproc.FindParameter("FlagsEnum2")!, principalDerived.FindProperty("FlagsEnum2")!, derived_InsertSprocMapping); RelationalModel.CreateStoredProcedureParameterMapping(principalBaseIdParameter1, derived_InsertISproc.FindParameter("PrincipalBaseId")!, principalDerived.FindProperty("PrincipalBaseId")!, derived_InsertSprocMapping); RelationalModel.CreateStoredProcedureParameterMapping(principalDerivedIdParameter1, derived_InsertISproc.FindParameter("PrincipalDerivedId")!, principalDerived.FindProperty("PrincipalDerivedId")!, derived_InsertSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(refTypeArrayParameter1, derived_InsertISproc.FindParameter("RefTypeArray")!, principalDerived.FindProperty("RefTypeArray")!, derived_InsertSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(refTypeEnumerableParameter1, derived_InsertISproc.FindParameter("RefTypeEnumerable")!, principalDerived.FindProperty("RefTypeEnumerable")!, derived_InsertSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(refTypeIListParameter1, derived_InsertISproc.FindParameter("RefTypeIList")!, principalDerived.FindProperty("RefTypeIList")!, derived_InsertSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(refTypeListParameter1, derived_InsertISproc.FindParameter("RefTypeList")!, principalDerived.FindProperty("RefTypeList")!, derived_InsertSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(valueTypeArrayParameter1, derived_InsertISproc.FindParameter("ValueTypeArray")!, principalDerived.FindProperty("ValueTypeArray")!, derived_InsertSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(valueTypeEnumerableParameter1, derived_InsertISproc.FindParameter("ValueTypeEnumerable")!, principalDerived.FindProperty("ValueTypeEnumerable")!, derived_InsertSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(valueTypeIListParameter1, derived_InsertISproc.FindParameter("ValueTypeIList")!, principalDerived.FindProperty("ValueTypeIList")!, derived_InsertSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(valueTypeListParameter1, derived_InsertISproc.FindParameter("ValueTypeList")!, principalDerived.FindProperty("ValueTypeList")!, derived_InsertSprocMapping); RelationalModel.CreateStoredProcedureResultColumnMapping(derivedIdFunctionColumn, derived_InsertISproc.FindResultColumn("DerivedId")!, principalDerived.FindProperty("Id")!, derived_InsertSprocMapping); var updateSprocMappings0 = new List(); @@ -8791,7 +13649,47 @@ private IRelationalModel CreateRelationalModel() derived_UpdateStoreSproc.AddParameter(flagsEnum1Parameter2); var flagsEnum2Parameter2 = new StoreStoredProcedureParameter("FlagsEnum2", "int", 5, derived_UpdateStoreSproc, System.Data.ParameterDirection.Input); derived_UpdateStoreSproc.AddParameter(flagsEnum2Parameter2); - var id_OriginalParameter2 = new StoreStoredProcedureParameter("Id_Original", "bigint", 6, derived_UpdateStoreSproc, System.Data.ParameterDirection.Input); + var valueTypeListParameter2 = new StoreStoredProcedureParameter("ValueTypeList", "nvarchar(max)", 6, derived_UpdateStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + derived_UpdateStoreSproc.AddParameter(valueTypeListParameter2); + var valueTypeIListParameter2 = new StoreStoredProcedureParameter("ValueTypeIList", "nvarchar(max)", 7, derived_UpdateStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + derived_UpdateStoreSproc.AddParameter(valueTypeIListParameter2); + var valueTypeArrayParameter2 = new StoreStoredProcedureParameter("ValueTypeArray", "nvarchar(max)", 8, derived_UpdateStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + derived_UpdateStoreSproc.AddParameter(valueTypeArrayParameter2); + var valueTypeEnumerableParameter2 = new StoreStoredProcedureParameter("ValueTypeEnumerable", "nvarchar(max)", 9, derived_UpdateStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + derived_UpdateStoreSproc.AddParameter(valueTypeEnumerableParameter2); + var refTypeListParameter2 = new StoreStoredProcedureParameter("RefTypeList", "nvarchar(max)", 10, derived_UpdateStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + derived_UpdateStoreSproc.AddParameter(refTypeListParameter2); + var refTypeIListParameter2 = new StoreStoredProcedureParameter("RefTypeIList", "nvarchar(max)", 11, derived_UpdateStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + derived_UpdateStoreSproc.AddParameter(refTypeIListParameter2); + var refTypeArrayParameter2 = new StoreStoredProcedureParameter("RefTypeArray", "nvarchar(max)", 12, derived_UpdateStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + derived_UpdateStoreSproc.AddParameter(refTypeArrayParameter2); + var refTypeEnumerableParameter2 = new StoreStoredProcedureParameter("RefTypeEnumerable", "nvarchar(max)", 13, derived_UpdateStoreSproc, System.Data.ParameterDirection.Input) + { + IsNullable = true + }; + derived_UpdateStoreSproc.AddParameter(refTypeEnumerableParameter2); + var id_OriginalParameter2 = new StoreStoredProcedureParameter("Id_Original", "bigint", 14, derived_UpdateStoreSproc, System.Data.ParameterDirection.Input); derived_UpdateStoreSproc.AddParameter(id_OriginalParameter2); derived_UpdateStoreSproc.AddStoredProcedure((IRuntimeStoredProcedure)principalDerived.GetUpdateStoredProcedure()!); relationalModel.StoredProcedures.Add(("Derived_Update", "Derived"), derived_UpdateStoreSproc); @@ -8807,6 +13705,14 @@ private IRelationalModel CreateRelationalModel() RelationalModel.CreateStoredProcedureParameterMapping(flagsEnum2Parameter2, derived_UpdateUSproc.FindParameter("FlagsEnum2")!, principalDerived.FindProperty("FlagsEnum2")!, derived_UpdateSprocMapping); RelationalModel.CreateStoredProcedureParameterMapping(principalBaseIdParameter2, derived_UpdateUSproc.FindParameter("PrincipalBaseId")!, principalDerived.FindProperty("PrincipalBaseId")!, derived_UpdateSprocMapping); RelationalModel.CreateStoredProcedureParameterMapping(principalDerivedIdParameter2, derived_UpdateUSproc.FindParameter("PrincipalDerivedId")!, principalDerived.FindProperty("PrincipalDerivedId")!, derived_UpdateSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(refTypeArrayParameter2, derived_UpdateUSproc.FindParameter("RefTypeArray")!, principalDerived.FindProperty("RefTypeArray")!, derived_UpdateSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(refTypeEnumerableParameter2, derived_UpdateUSproc.FindParameter("RefTypeEnumerable")!, principalDerived.FindProperty("RefTypeEnumerable")!, derived_UpdateSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(refTypeIListParameter2, derived_UpdateUSproc.FindParameter("RefTypeIList")!, principalDerived.FindProperty("RefTypeIList")!, derived_UpdateSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(refTypeListParameter2, derived_UpdateUSproc.FindParameter("RefTypeList")!, principalDerived.FindProperty("RefTypeList")!, derived_UpdateSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(valueTypeArrayParameter2, derived_UpdateUSproc.FindParameter("ValueTypeArray")!, principalDerived.FindProperty("ValueTypeArray")!, derived_UpdateSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(valueTypeEnumerableParameter2, derived_UpdateUSproc.FindParameter("ValueTypeEnumerable")!, principalDerived.FindProperty("ValueTypeEnumerable")!, derived_UpdateSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(valueTypeIListParameter2, derived_UpdateUSproc.FindParameter("ValueTypeIList")!, principalDerived.FindProperty("ValueTypeIList")!, derived_UpdateSprocMapping); + RelationalModel.CreateStoredProcedureParameterMapping(valueTypeListParameter2, derived_UpdateUSproc.FindParameter("ValueTypeList")!, principalDerived.FindProperty("ValueTypeList")!, derived_UpdateSprocMapping); var fK_DependentBasebyte_PrincipalDerived_PrincipalId = new ForeignKeyConstraint( "FK_DependentBase_PrincipalDerived_PrincipalId", dependentBasebyteTable, principalDerivedTable, new[] { principalIdColumn }, @@ -8976,6 +13882,7 @@ public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) // using System; using System.Collections.Generic; +using System.Net; using System.Reflection; using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.Metadata; @@ -9194,126 +14101,542 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType bas (Nullable v) => v.HasValue ? (Nullable)(long)v : default(Nullable))); principalDerivedId.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); - var key = runtimeEntityType.AddKey( - new[] { id }); - runtimeEntityType.SetPrimaryKey(key); - - var index = runtimeEntityType.AddIndex( - new[] { principalDerivedId }); - - var principalIndex = runtimeEntityType.AddIndex( - new[] { principalBaseId }, - name: "PrincipalIndex", - unique: true); - principalIndex.AddAnnotation("Relational:Name", "PIX"); - - return runtimeEntityType; - } - - public static RuntimeForeignKey CreateForeignKey1(RuntimeEntityType declaringEntityType, RuntimeEntityType principalEntityType) - { - var runtimeForeignKey = declaringEntityType.AddForeignKey(new[] { declaringEntityType.FindProperty("PrincipalBaseId") }, - principalEntityType.FindKey(new[] { principalEntityType.FindProperty("Id") }), - principalEntityType); - - var deriveds = principalEntityType.AddNavigation("Deriveds", - runtimeForeignKey, - onDependent: false, - typeof(ICollection), - propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("Deriveds", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), - fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); - - return runtimeForeignKey; - } - - public static RuntimeForeignKey CreateForeignKey2(RuntimeEntityType declaringEntityType, RuntimeEntityType principalEntityType) - { - var runtimeForeignKey = declaringEntityType.AddForeignKey(new[] { declaringEntityType.FindProperty("PrincipalDerivedId") }, - principalEntityType.FindKey(new[] { principalEntityType.FindProperty("Id") }), - principalEntityType); - - var principals = principalEntityType.AddNavigation("Principals", - runtimeForeignKey, - onDependent: false, - typeof(ICollection), - propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalDerived>).GetProperty("Principals", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), - fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalDerived>).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); - - return runtimeForeignKey; - } - - public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) - { - var insertSproc = new RuntimeStoredProcedure( - runtimeEntityType, - "PrincipalBase_Insert", - "TPC", - false); - - var principalBaseId = insertSproc.AddParameter( - "PrincipalBaseId", System.Data.ParameterDirection.Input, false, "PrincipalBaseId", false); - var principalDerivedId = insertSproc.AddParameter( - "PrincipalDerivedId", System.Data.ParameterDirection.Input, false, "PrincipalDerivedId", false); - var enum1 = insertSproc.AddParameter( - "Enum1", System.Data.ParameterDirection.Input, false, "Enum1", false); - var enum2 = insertSproc.AddParameter( - "Enum2", System.Data.ParameterDirection.Input, false, "Enum2", false); - var flagsEnum1 = insertSproc.AddParameter( - "FlagsEnum1", System.Data.ParameterDirection.Input, false, "FlagsEnum1", false); - var flagsEnum2 = insertSproc.AddParameter( - "FlagsEnum2", System.Data.ParameterDirection.Input, false, "FlagsEnum2", false); - var id = insertSproc.AddParameter( - "BaseId", System.Data.ParameterDirection.Output, false, "Id", false); - id.AddAnnotation("foo", "bar"); - insertSproc.AddAnnotation("foo", "bar1"); - runtimeEntityType.AddAnnotation("Relational:InsertStoredProcedure", insertSproc); - - var deleteSproc = new RuntimeStoredProcedure( - runtimeEntityType, - "PrincipalBase_Delete", - "TPC", - true); - - var id0 = deleteSproc.AddParameter( - "Id_Original", System.Data.ParameterDirection.Input, false, "Id", true); - runtimeEntityType.AddAnnotation("Relational:DeleteStoredProcedure", deleteSproc); - - var updateSproc = new RuntimeStoredProcedure( - runtimeEntityType, - "PrincipalBase_Update", - "TPC", - false); + var refTypeArray = runtimeEntityType.AddProperty( + "RefTypeArray", + typeof(IPAddress[]), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("RefTypeArray", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); + refTypeArray.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v)), + keyComparer: new ListComparer(new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v)), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), + converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter( + new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v))))), + storeTypePostfix: StoreTypePostfix.None, + jsonValueReaderWriter: new JsonCollectionReaderWriter( + new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v)))), + elementMapping: SqlServerStringTypeMapping.Default.Clone( + comparer: new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v), + keyComparer: new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(45)", + size: 45, + dbType: System.Data.DbType.String), + converter: new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v)), + jsonValueReaderWriter: new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v))))); +refTypeArray.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var refTypeEnumerable = runtimeEntityType.AddProperty( + "RefTypeEnumerable", + typeof(IEnumerable), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("RefTypeEnumerable", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +refTypeEnumerable.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, string>( + JsonStringReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, string>( + JsonStringReaderWriter.Instance), +elementMapping: SqlServerStringTypeMapping.Default.Clone( + comparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + keyComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), + storeTypePostfix: StoreTypePostfix.None)); +refTypeEnumerable.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var refTypeIList = runtimeEntityType.AddProperty( + "RefTypeIList", + typeof(IList), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("RefTypeIList", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +refTypeIList.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, string>( + JsonStringReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, string>( + JsonStringReaderWriter.Instance), +elementMapping: SqlServerStringTypeMapping.Default.Clone( + comparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + keyComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), + storeTypePostfix: StoreTypePostfix.None)); +refTypeIList.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var refTypeList = runtimeEntityType.AddProperty( + "RefTypeList", + typeof(List), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("RefTypeList", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +refTypeList.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, IPAddress>( + new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v))))), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, IPAddress>( + new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v)))), +elementMapping: SqlServerStringTypeMapping.Default.Clone( + comparer: new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v), + keyComparer: new ValueComparer( + (IPAddress v1, IPAddress v2) => v1 == null && v2 == null || v1 != null && v2 != null && v1.Equals(v2), + (IPAddress v) => v.GetHashCode(), + (IPAddress v) => v), + providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), + mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(45)", + size: 45, + dbType: System.Data.DbType.String), + converter: new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v)), + jsonValueReaderWriter: new JsonConvertedValueReaderWriter( + JsonStringReaderWriter.Instance, + new ValueConverter( + (IPAddress v) => v.ToString(), + (string v) => IPAddress.Parse(v))))); +refTypeList.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var valueTypeArray = runtimeEntityType.AddProperty( + "ValueTypeArray", + typeof(DateTime[]), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("ValueTypeArray", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +valueTypeArray.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter( + JsonDateTimeReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter( + JsonDateTimeReaderWriter.Instance), +elementMapping: SqlServerDateTimeTypeMapping.Default.Clone( + comparer: new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v), + keyComparer: new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v), + providerValueComparer: new ValueComparer( + (DateTime v1, DateTime v2) => v1.Equals(v2), + (DateTime v) => v.GetHashCode(), + (DateTime v) => v))); +valueTypeArray.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var valueTypeEnumerable = runtimeEntityType.AddProperty( + "ValueTypeEnumerable", + typeof(IEnumerable), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("ValueTypeEnumerable", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +valueTypeEnumerable.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, byte>( + JsonByteReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, byte>( + JsonByteReaderWriter.Instance), +elementMapping: SqlServerByteTypeMapping.Default.Clone( + comparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v), + keyComparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v), + providerValueComparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v))); +valueTypeEnumerable.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var valueTypeIList = runtimeEntityType.AddProperty( + "ValueTypeIList", + typeof(IList), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("ValueTypeIList", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +valueTypeIList.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, byte>( + JsonByteReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, byte>( + JsonByteReaderWriter.Instance), +elementMapping: SqlServerByteTypeMapping.Default.Clone( + comparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v), + keyComparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v), + providerValueComparer: new ValueComparer( + (byte v1, byte v2) => v1 == v2, + (byte v) => (int)v, + (byte v) => v))); +valueTypeIList.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var valueTypeList = runtimeEntityType.AddProperty( + "ValueTypeList", + typeof(List), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("ValueTypeList", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); +valueTypeList.TypeMapping = SqlServerStringTypeMapping.Default.Clone( + comparer: new ListComparer(new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v)), +keyComparer: new ListComparer(new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v)), +providerValueComparer: new ValueComparer( + (string v1, string v2) => v1 == v2, + (string v) => v.GetHashCode(), + (string v) => v), +mappingInfo: new RelationalTypeMappingInfo( + storeTypeName: "nvarchar(max)", + dbType: System.Data.DbType.String), +converter: new CollectionToJsonStringConverter(new JsonCollectionReaderWriter, List, short>( + JsonInt16ReaderWriter.Instance)), +storeTypePostfix: StoreTypePostfix.None, +jsonValueReaderWriter: new JsonCollectionReaderWriter, List, short>( + JsonInt16ReaderWriter.Instance), +elementMapping: SqlServerShortTypeMapping.Default.Clone( + comparer: new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v), + keyComparer: new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v), + providerValueComparer: new ValueComparer( + (short v1, short v2) => v1 == v2, + (short v) => (int)v, + (short v) => v))); +valueTypeList.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None); + +var key = runtimeEntityType.AddKey( + new[] { id }); +runtimeEntityType.SetPrimaryKey(key); + +var index = runtimeEntityType.AddIndex( + new[] { principalDerivedId }); + +var principalIndex = runtimeEntityType.AddIndex( + new[] { principalBaseId }, + name: "PrincipalIndex", + unique: true); +principalIndex.AddAnnotation("Relational:Name", "PIX"); + +return runtimeEntityType; +} - var principalBaseId0 = updateSproc.AddParameter( - "PrincipalBaseId", System.Data.ParameterDirection.Input, false, "PrincipalBaseId", false); - var principalDerivedId0 = updateSproc.AddParameter( - "PrincipalDerivedId", System.Data.ParameterDirection.Input, false, "PrincipalDerivedId", false); - var enum10 = updateSproc.AddParameter( - "Enum1", System.Data.ParameterDirection.Input, false, "Enum1", false); - var enum20 = updateSproc.AddParameter( - "Enum2", System.Data.ParameterDirection.Input, false, "Enum2", false); - var flagsEnum10 = updateSproc.AddParameter( - "FlagsEnum1", System.Data.ParameterDirection.Input, false, "FlagsEnum1", false); - var flagsEnum20 = updateSproc.AddParameter( - "FlagsEnum2", System.Data.ParameterDirection.Input, false, "FlagsEnum2", false); - var id1 = updateSproc.AddParameter( - "Id_Original", System.Data.ParameterDirection.Input, false, "Id", true); - runtimeEntityType.AddAnnotation("Relational:UpdateStoredProcedure", updateSproc); +public static RuntimeForeignKey CreateForeignKey1(RuntimeEntityType declaringEntityType, RuntimeEntityType principalEntityType) +{ + var runtimeForeignKey = declaringEntityType.AddForeignKey(new[] { declaringEntityType.FindProperty("PrincipalBaseId") }, + principalEntityType.FindKey(new[] { principalEntityType.FindProperty("Id") }), + principalEntityType); + + var deriveds = principalEntityType.AddNavigation("Deriveds", + runtimeForeignKey, + onDependent: false, + typeof(ICollection), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetProperty("Deriveds", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); + + return runtimeForeignKey; +} - runtimeEntityType.AddAnnotation("Relational:FunctionName", null); - runtimeEntityType.AddAnnotation("Relational:MappingStrategy", "TPC"); - runtimeEntityType.AddAnnotation("Relational:Schema", "TPC"); - runtimeEntityType.AddAnnotation("Relational:SqlQuery", null); - runtimeEntityType.AddAnnotation("Relational:TableName", "PrincipalBase"); - runtimeEntityType.AddAnnotation("Relational:ViewDefinitionSql", null); - runtimeEntityType.AddAnnotation("Relational:ViewName", "PrincipalBaseView"); - runtimeEntityType.AddAnnotation("Relational:ViewSchema", "TPC"); +public static RuntimeForeignKey CreateForeignKey2(RuntimeEntityType declaringEntityType, RuntimeEntityType principalEntityType) +{ + var runtimeForeignKey = declaringEntityType.AddForeignKey(new[] { declaringEntityType.FindProperty("PrincipalDerivedId") }, + principalEntityType.FindKey(new[] { principalEntityType.FindProperty("Id") }), + principalEntityType); + + var principals = principalEntityType.AddNavigation("Principals", + runtimeForeignKey, + onDependent: false, + typeof(ICollection), + propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalDerived>).GetProperty("Principals", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalDerived>).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); + + return runtimeForeignKey; +} - Customize(runtimeEntityType); - } +public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) +{ + var insertSproc = new RuntimeStoredProcedure( + runtimeEntityType, + "PrincipalBase_Insert", + "TPC", + false); + + var principalBaseId = insertSproc.AddParameter( + "PrincipalBaseId", System.Data.ParameterDirection.Input, false, "PrincipalBaseId", false); + var principalDerivedId = insertSproc.AddParameter( + "PrincipalDerivedId", System.Data.ParameterDirection.Input, false, "PrincipalDerivedId", false); + var enum1 = insertSproc.AddParameter( + "Enum1", System.Data.ParameterDirection.Input, false, "Enum1", false); + var enum2 = insertSproc.AddParameter( + "Enum2", System.Data.ParameterDirection.Input, false, "Enum2", false); + var flagsEnum1 = insertSproc.AddParameter( + "FlagsEnum1", System.Data.ParameterDirection.Input, false, "FlagsEnum1", false); + var flagsEnum2 = insertSproc.AddParameter( + "FlagsEnum2", System.Data.ParameterDirection.Input, false, "FlagsEnum2", false); + var valueTypeList = insertSproc.AddParameter( + "ValueTypeList", System.Data.ParameterDirection.Input, false, "ValueTypeList", false); + var valueTypeIList = insertSproc.AddParameter( + "ValueTypeIList", System.Data.ParameterDirection.Input, false, "ValueTypeIList", false); + var valueTypeArray = insertSproc.AddParameter( + "ValueTypeArray", System.Data.ParameterDirection.Input, false, "ValueTypeArray", false); + var valueTypeEnumerable = insertSproc.AddParameter( + "ValueTypeEnumerable", System.Data.ParameterDirection.Input, false, "ValueTypeEnumerable", false); + var refTypeList = insertSproc.AddParameter( + "RefTypeList", System.Data.ParameterDirection.Input, false, "RefTypeList", false); + var refTypeIList = insertSproc.AddParameter( + "RefTypeIList", System.Data.ParameterDirection.Input, false, "RefTypeIList", false); + var refTypeArray = insertSproc.AddParameter( + "RefTypeArray", System.Data.ParameterDirection.Input, false, "RefTypeArray", false); + var refTypeEnumerable = insertSproc.AddParameter( + "RefTypeEnumerable", System.Data.ParameterDirection.Input, false, "RefTypeEnumerable", false); + var id = insertSproc.AddParameter( + "BaseId", System.Data.ParameterDirection.Output, false, "Id", false); + id.AddAnnotation("foo", "bar"); + insertSproc.AddAnnotation("foo", "bar1"); + runtimeEntityType.AddAnnotation("Relational:InsertStoredProcedure", insertSproc); + + var deleteSproc = new RuntimeStoredProcedure( + runtimeEntityType, + "PrincipalBase_Delete", + "TPC", + true); + + var id0 = deleteSproc.AddParameter( + "Id_Original", System.Data.ParameterDirection.Input, false, "Id", true); + runtimeEntityType.AddAnnotation("Relational:DeleteStoredProcedure", deleteSproc); + + var updateSproc = new RuntimeStoredProcedure( + runtimeEntityType, + "PrincipalBase_Update", + "TPC", + false); + + var principalBaseId0 = updateSproc.AddParameter( + "PrincipalBaseId", System.Data.ParameterDirection.Input, false, "PrincipalBaseId", false); + var principalDerivedId0 = updateSproc.AddParameter( + "PrincipalDerivedId", System.Data.ParameterDirection.Input, false, "PrincipalDerivedId", false); + var enum10 = updateSproc.AddParameter( + "Enum1", System.Data.ParameterDirection.Input, false, "Enum1", false); + var enum20 = updateSproc.AddParameter( + "Enum2", System.Data.ParameterDirection.Input, false, "Enum2", false); + var flagsEnum10 = updateSproc.AddParameter( + "FlagsEnum1", System.Data.ParameterDirection.Input, false, "FlagsEnum1", false); + var flagsEnum20 = updateSproc.AddParameter( + "FlagsEnum2", System.Data.ParameterDirection.Input, false, "FlagsEnum2", false); + var valueTypeList0 = updateSproc.AddParameter( + "ValueTypeList", System.Data.ParameterDirection.Input, false, "ValueTypeList", false); + var valueTypeIList0 = updateSproc.AddParameter( + "ValueTypeIList", System.Data.ParameterDirection.Input, false, "ValueTypeIList", false); + var valueTypeArray0 = updateSproc.AddParameter( + "ValueTypeArray", System.Data.ParameterDirection.Input, false, "ValueTypeArray", false); + var valueTypeEnumerable0 = updateSproc.AddParameter( + "ValueTypeEnumerable", System.Data.ParameterDirection.Input, false, "ValueTypeEnumerable", false); + var refTypeList0 = updateSproc.AddParameter( + "RefTypeList", System.Data.ParameterDirection.Input, false, "RefTypeList", false); + var refTypeIList0 = updateSproc.AddParameter( + "RefTypeIList", System.Data.ParameterDirection.Input, false, "RefTypeIList", false); + var refTypeArray0 = updateSproc.AddParameter( + "RefTypeArray", System.Data.ParameterDirection.Input, false, "RefTypeArray", false); + var refTypeEnumerable0 = updateSproc.AddParameter( + "RefTypeEnumerable", System.Data.ParameterDirection.Input, false, "RefTypeEnumerable", false); + var id1 = updateSproc.AddParameter( + "Id_Original", System.Data.ParameterDirection.Input, false, "Id", true); + runtimeEntityType.AddAnnotation("Relational:UpdateStoredProcedure", updateSproc); + + runtimeEntityType.AddAnnotation("Relational:FunctionName", null); + runtimeEntityType.AddAnnotation("Relational:MappingStrategy", "TPC"); + runtimeEntityType.AddAnnotation("Relational:Schema", "TPC"); + runtimeEntityType.AddAnnotation("Relational:SqlQuery", null); + runtimeEntityType.AddAnnotation("Relational:TableName", "PrincipalBase"); + runtimeEntityType.AddAnnotation("Relational:ViewDefinitionSql", null); + runtimeEntityType.AddAnnotation("Relational:ViewName", "PrincipalBaseView"); + runtimeEntityType.AddAnnotation("Relational:ViewSchema", "TPC"); + + Customize(runtimeEntityType); +} - static partial void Customize(RuntimeEntityType runtimeEntityType); - } +static partial void Customize(RuntimeEntityType runtimeEntityType); +} } """, c), c => AssertFileContents( @@ -9363,6 +14686,22 @@ public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) "FlagsEnum1", System.Data.ParameterDirection.Input, false, "FlagsEnum1", false); var flagsEnum2 = insertSproc.AddParameter( "FlagsEnum2", System.Data.ParameterDirection.Input, false, "FlagsEnum2", false); + var valueTypeList = insertSproc.AddParameter( + "ValueTypeList", System.Data.ParameterDirection.Input, false, "ValueTypeList", false); + var valueTypeIList = insertSproc.AddParameter( + "ValueTypeIList", System.Data.ParameterDirection.Input, false, "ValueTypeIList", false); + var valueTypeArray = insertSproc.AddParameter( + "ValueTypeArray", System.Data.ParameterDirection.Input, false, "ValueTypeArray", false); + var valueTypeEnumerable = insertSproc.AddParameter( + "ValueTypeEnumerable", System.Data.ParameterDirection.Input, false, "ValueTypeEnumerable", false); + var refTypeList = insertSproc.AddParameter( + "RefTypeList", System.Data.ParameterDirection.Input, false, "RefTypeList", false); + var refTypeIList = insertSproc.AddParameter( + "RefTypeIList", System.Data.ParameterDirection.Input, false, "RefTypeIList", false); + var refTypeArray = insertSproc.AddParameter( + "RefTypeArray", System.Data.ParameterDirection.Input, false, "RefTypeArray", false); + var refTypeEnumerable = insertSproc.AddParameter( + "RefTypeEnumerable", System.Data.ParameterDirection.Input, false, "RefTypeEnumerable", false); var derivedId = insertSproc.AddResultColumn( "DerivedId", false, "Id"); derivedId.AddAnnotation("foo", "bar3"); @@ -9396,6 +14735,22 @@ public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) "FlagsEnum1", System.Data.ParameterDirection.Input, false, "FlagsEnum1", false); var flagsEnum20 = updateSproc.AddParameter( "FlagsEnum2", System.Data.ParameterDirection.Input, false, "FlagsEnum2", false); + var valueTypeList0 = updateSproc.AddParameter( + "ValueTypeList", System.Data.ParameterDirection.Input, false, "ValueTypeList", false); + var valueTypeIList0 = updateSproc.AddParameter( + "ValueTypeIList", System.Data.ParameterDirection.Input, false, "ValueTypeIList", false); + var valueTypeArray0 = updateSproc.AddParameter( + "ValueTypeArray", System.Data.ParameterDirection.Input, false, "ValueTypeArray", false); + var valueTypeEnumerable0 = updateSproc.AddParameter( + "ValueTypeEnumerable", System.Data.ParameterDirection.Input, false, "ValueTypeEnumerable", false); + var refTypeList0 = updateSproc.AddParameter( + "RefTypeList", System.Data.ParameterDirection.Input, false, "RefTypeList", false); + var refTypeIList0 = updateSproc.AddParameter( + "RefTypeIList", System.Data.ParameterDirection.Input, false, "RefTypeIList", false); + var refTypeArray0 = updateSproc.AddParameter( + "RefTypeArray", System.Data.ParameterDirection.Input, false, "RefTypeArray", false); + var refTypeEnumerable0 = updateSproc.AddParameter( + "RefTypeEnumerable", System.Data.ParameterDirection.Input, false, "RefTypeEnumerable", false); var id0 = updateSproc.AddParameter( "Id_Original", System.Data.ParameterDirection.Input, false, "Id", true); runtimeEntityType.AddAnnotation("Relational:UpdateStoredProcedure", updateSproc); @@ -9472,6 +14827,14 @@ public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) Assert.Equal( CoreStrings.RuntimeModelMissingData, Assert.Throws(() => alternateIndex.GetIncludeProperties()).Message); + Assert.Null(alternateIndex[SqlServerAnnotationNames.SortInTempDb]); + Assert.Equal( + CoreStrings.RuntimeModelMissingData, + Assert.Throws(() => alternateIndex.GetSortInTempDb()).Message); + Assert.Null(alternateIndex[SqlServerAnnotationNames.DataCompression]); + Assert.Equal( + CoreStrings.RuntimeModelMissingData, + Assert.Throws(() => alternateIndex.GetDataCompression()).Message); Assert.Equal(new[] { alternateIndex }, principalBaseId.GetContainingIndexes()); @@ -9479,7 +14842,7 @@ public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) Assert.Equal("PrincipalBase_Insert", insertSproc.Name); Assert.Equal("TPC", insertSproc.Schema); Assert.Equal( - new[] { "PrincipalBaseId", "PrincipalDerivedId", "Enum1", "Enum2", "FlagsEnum1", "FlagsEnum2", "Id" }, + new[] { "PrincipalBaseId", "PrincipalDerivedId", "Enum1", "Enum2", "FlagsEnum1", "FlagsEnum2", "ValueTypeList", "ValueTypeIList", "ValueTypeArray", "ValueTypeEnumerable", "RefTypeList", "RefTypeIList", "RefTypeArray", "RefTypeEnumerable", "Id" }, insertSproc.Parameters.Select(p => p.PropertyName)); Assert.Empty(insertSproc.ResultColumns); Assert.False(insertSproc.IsRowsAffectedReturned); @@ -9493,7 +14856,7 @@ public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) Assert.Equal("PrincipalBase_Update", updateSproc.Name); Assert.Equal("TPC", updateSproc.Schema); Assert.Equal( - new[] { "PrincipalBaseId", "PrincipalDerivedId", "Enum1", "Enum2", "FlagsEnum1", "FlagsEnum2", "Id" }, + new[] { "PrincipalBaseId", "PrincipalDerivedId", "Enum1", "Enum2", "FlagsEnum1", "FlagsEnum2", "ValueTypeList", "ValueTypeIList", "ValueTypeArray", "ValueTypeEnumerable", "RefTypeList", "RefTypeIList", "RefTypeArray", "RefTypeEnumerable", "Id" }, updateSproc.Parameters.Select(p => p.PropertyName)); Assert.Empty(updateSproc.ResultColumns); Assert.False(updateSproc.IsRowsAffectedReturned); @@ -9535,7 +14898,7 @@ public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) Assert.Equal("Derived_Insert", insertSproc.Name); Assert.Equal("TPC", insertSproc.Schema); Assert.Equal( - new[] { "PrincipalBaseId", "PrincipalDerivedId", "Enum1", "Enum2", "FlagsEnum1", "FlagsEnum2" }, + new[] { "PrincipalBaseId", "PrincipalDerivedId", "Enum1", "Enum2", "FlagsEnum1", "FlagsEnum2", "ValueTypeList", "ValueTypeIList", "ValueTypeArray", "ValueTypeEnumerable", "RefTypeList", "RefTypeIList", "RefTypeArray", "RefTypeEnumerable" }, insertSproc.Parameters.Select(p => p.PropertyName)); Assert.Equal(new[] { "Id" }, insertSproc.ResultColumns.Select(p => p.PropertyName)); Assert.Null(insertSproc["foo"]); @@ -9553,7 +14916,7 @@ public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) Assert.Equal("Derived_Update", updateSproc.Name); Assert.Equal("Derived", updateSproc.Schema); Assert.Equal( - new[] { "PrincipalBaseId", "PrincipalDerivedId", "Enum1", "Enum2", "FlagsEnum1", "FlagsEnum2", "Id" }, + new[] { "PrincipalBaseId", "PrincipalDerivedId", "Enum1", "Enum2", "FlagsEnum1", "FlagsEnum2", "ValueTypeList", "ValueTypeIList", "ValueTypeArray", "ValueTypeEnumerable", "RefTypeList", "RefTypeIList", "RefTypeArray", "RefTypeEnumerable", "Id" }, updateSproc.Parameters.Select(p => p.PropertyName)); Assert.Empty(updateSproc.ResultColumns); Assert.Empty(updateSproc.GetAnnotations()); @@ -9651,6 +15014,14 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasParameter("Enum2") .HasParameter("FlagsEnum1") .HasParameter("FlagsEnum2") + .HasParameter("ValueTypeList") + .HasParameter("ValueTypeIList") + .HasParameter("ValueTypeArray") + .HasParameter("ValueTypeEnumerable") + .HasParameter("RefTypeList") + .HasParameter("RefTypeIList") + .HasParameter("RefTypeArray") + .HasParameter("RefTypeEnumerable") .HasParameter(p => p.Id, pb => pb.HasName("BaseId").IsOutput().HasAnnotation("foo", "bar")) .HasAnnotation("foo", "bar1")); eb.UpdateUsingStoredProcedure( @@ -9661,6 +15032,14 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasParameter("Enum2") .HasParameter("FlagsEnum1") .HasParameter("FlagsEnum2") + .HasParameter("ValueTypeList") + .HasParameter("ValueTypeIList") + .HasParameter("ValueTypeArray") + .HasParameter("ValueTypeEnumerable") + .HasParameter("RefTypeList") + .HasParameter("RefTypeIList") + .HasParameter("RefTypeArray") + .HasParameter("RefTypeEnumerable") .HasOriginalValueParameter(p => p.Id)); eb.DeleteUsingStoredProcedure( s => s @@ -9674,7 +15053,9 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasFilter("AlternateId <> NULL") .IsCreatedOnline() .HasFillFactor(40) - .IncludeProperties(e => e.Id); + .IncludeProperties(e => e.Id) + .SortInTempDb() + .UseDataCompression(DataCompressionType.Page); }); modelBuilder.Entity>>( @@ -9697,6 +15078,14 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasParameter("Enum2") .HasParameter("FlagsEnum1") .HasParameter("FlagsEnum2") + .HasParameter("ValueTypeList") + .HasParameter("ValueTypeIList") + .HasParameter("ValueTypeArray") + .HasParameter("ValueTypeEnumerable") + .HasParameter("RefTypeList") + .HasParameter("RefTypeIList") + .HasParameter("RefTypeArray") + .HasParameter("RefTypeEnumerable") .HasResultColumn(p => p.Id, pb => pb.HasName("DerivedId").HasAnnotation("foo", "bar3"))); eb.UpdateUsingStoredProcedure( "Derived_Update", "Derived", s => s @@ -9706,6 +15095,14 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasParameter("Enum2") .HasParameter("FlagsEnum1") .HasParameter("FlagsEnum2") + .HasParameter("ValueTypeList") + .HasParameter("ValueTypeIList") + .HasParameter("ValueTypeArray") + .HasParameter("ValueTypeEnumerable") + .HasParameter("RefTypeList") + .HasParameter("RefTypeIList") + .HasParameter("RefTypeArray") + .HasParameter("RefTypeEnumerable") .HasOriginalValueParameter(p => p.Id)); eb.DeleteUsingStoredProcedure( "Derived_Delete", s => s @@ -9765,6 +15162,16 @@ public class PrincipalBase : AbstractBase public AFlagsEnum FlagsEnum1 { get; set; } public AFlagsEnum FlagsEnum2 { get; set; } + public List ValueTypeList { get; set; } + public IList ValueTypeIList { get; set; } + public DateTime[] ValueTypeArray { get; set; } + public IEnumerable ValueTypeEnumerable { get; set; } + + public List RefTypeList { get; set; } + public IList RefTypeIList { get; set; } + public IPAddress[] RefTypeArray { get; set; } + public IEnumerable RefTypeEnumerable { get; set; } + private OwnedType _ownedField; public OwnedType Owned { get => _ownedField; set => _ownedField = value; } public ICollection Deriveds { get; set; } @@ -9829,6 +15236,13 @@ public DbContext Context public PrincipalBase Principal { get; set; } private string _details; + private List _valueTypeList; + private DateTime[] _valueTypeArray; + private IEnumerable _valueTypeEnumerable; + private List _refTypeList; + private IList _refTypeIList; + private IPAddress[] _refTypeArray; + private IEnumerable _refTypeEnumerable; public string Details { @@ -9838,6 +15252,85 @@ public string Details public event PropertyChangedEventHandler PropertyChanged; public event PropertyChangingEventHandler PropertyChanging; + + public List ValueTypeList + { + get => _valueTypeList; + set + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ValueTypeList))); + _valueTypeList = value; + PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(nameof(ValueTypeList))); + } + } + + public IList ValueTypeIList { get; set; } + + public DateTime[] ValueTypeArray + { + get => _valueTypeArray; + set + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ValueTypeArray))); + _valueTypeArray = value; + PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(nameof(ValueTypeArray))); + } + } + + public IEnumerable ValueTypeEnumerable + { + get => _valueTypeEnumerable; + set + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ValueTypeEnumerable))); + _valueTypeEnumerable = value; + PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(nameof(ValueTypeEnumerable))); + } + } + + public List RefTypeList + { + get => _refTypeList; + set + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(RefTypeList))); + _refTypeList = value; + PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(nameof(RefTypeList))); + } + } + + public IList RefTypeIList + { + get => _refTypeIList; + set + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(RefTypeIList))); + _refTypeIList = value; + PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(nameof(RefTypeIList))); + } + } + + public IPAddress[] RefTypeArray + { + get => _refTypeArray; + set + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(RefTypeArray))); + _refTypeArray = value; + PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(nameof(RefTypeArray))); + } + } + + public IEnumerable RefTypeEnumerable + { + get => _refTypeEnumerable; + set + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(RefTypeEnumerable))); + _refTypeEnumerable = value; + PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(nameof(RefTypeEnumerable))); + } + } } [ConditionalFact] @@ -12340,6 +17833,7 @@ protected void Test( References = { BuildReference.ByName("System.Linq"), + BuildReference.ByName("System.Net.Primitives"), BuildReference.ByName("Microsoft.EntityFrameworkCore"), BuildReference.ByName("Microsoft.EntityFrameworkCore.Abstractions"), BuildReference.ByName("Microsoft.EntityFrameworkCore.Cosmos"), diff --git a/test/EFCore.Design.Tests/Scaffolding/Internal/ScaffoldingTypeMapperSqlServerTest.cs b/test/EFCore.Design.Tests/Scaffolding/Internal/ScaffoldingTypeMapperSqlServerTest.cs index 0c1f0d4311e..f0c8ea07dc9 100644 --- a/test/EFCore.Design.Tests/Scaffolding/Internal/ScaffoldingTypeMapperSqlServerTest.cs +++ b/test/EFCore.Design.Tests/Scaffolding/Internal/ScaffoldingTypeMapperSqlServerTest.cs @@ -404,6 +404,5 @@ private static ScaffoldingTypeMapper CreateMapper() => new( new SqlServerTypeMappingSource( TestServiceFactory.Instance.Create(), - TestServiceFactory.Instance.Create(), - new SqlServerSingletonOptions())); + TestServiceFactory.Instance.Create())); } diff --git a/test/EFCore.InMemory.FunctionalTests/ComplexTypesTrackingInMemoryTest.cs b/test/EFCore.InMemory.FunctionalTests/ComplexTypesTrackingInMemoryTest.cs index 6cad13b626a..6dca46eafba 100644 --- a/test/EFCore.InMemory.FunctionalTests/ComplexTypesTrackingInMemoryTest.cs +++ b/test/EFCore.InMemory.FunctionalTests/ComplexTypesTrackingInMemoryTest.cs @@ -47,5 +47,11 @@ protected override ITestStoreFactory TestStoreFactory public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder) => base.AddOptions(builder).ConfigureWarnings(w => w.Log(InMemoryEventId.TransactionIgnoredWarning)); + + protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context) + { + modelBuilder.UsePropertyAccessMode(PropertyAccessMode.PreferProperty); + base.OnModelCreating(modelBuilder, context); + } } } diff --git a/test/EFCore.NativeAotTests/EFCore.NativeAotTests.csproj b/test/EFCore.NativeAotTests/EFCore.NativeAotTests.csproj index 912cc073883..1dd35abe704 100644 --- a/test/EFCore.NativeAotTests/EFCore.NativeAotTests.csproj +++ b/test/EFCore.NativeAotTests/EFCore.NativeAotTests.csproj @@ -19,7 +19,7 @@ - + diff --git a/test/EFCore.OData.FunctionalTests/EFCore.OData.FunctionalTests.csproj b/test/EFCore.OData.FunctionalTests/EFCore.OData.FunctionalTests.csproj index aa0243a6805..aa9c7fcd207 100644 --- a/test/EFCore.OData.FunctionalTests/EFCore.OData.FunctionalTests.csproj +++ b/test/EFCore.OData.FunctionalTests/EFCore.OData.FunctionalTests.csproj @@ -41,7 +41,7 @@ - + diff --git a/test/EFCore.Relational.Specification.Tests/Query/ComplexTypeQueryRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/ComplexTypeQueryRelationalTestBase.cs index a12178ec127..59e03282f82 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/ComplexTypeQueryRelationalTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/ComplexTypeQueryRelationalTestBase.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.EntityFrameworkCore.TestModels.ComplexTypeModel; - namespace Microsoft.EntityFrameworkCore.Query; public abstract class ComplexTypeQueryRelationalTestBase : ComplexTypeQueryTestBase @@ -18,6 +16,8 @@ public override async Task Subquery_over_complex_type(bool async) var exception = await Assert.ThrowsAsync(() => base.Subquery_over_complex_type(async)); Assert.Equal(RelationalStrings.SubqueryOverComplexTypesNotSupported("Customer.ShippingAddress#Address"), exception.Message); + + AssertSql(); } public override async Task Concat_two_different_complex_type(bool async) @@ -27,6 +27,8 @@ public override async Task Concat_two_different_complex_type(bool async) Assert.Equal( RelationalStrings.SetOperationOverDifferentStructuralTypes( "Customer.ShippingAddress#Address", "Customer.BillingAddress#Address"), exception.Message); + + AssertSql(); } public override async Task Union_two_different_complex_type(bool async) @@ -36,5 +38,19 @@ public override async Task Union_two_different_complex_type(bool async) Assert.Equal( RelationalStrings.SetOperationOverDifferentStructuralTypes( "Customer.ShippingAddress#Address", "Customer.BillingAddress#Address"), exception.Message); + + AssertSql(); + } + + public override async Task Complex_type_equals_null(bool async) + { + var exception = await Assert.ThrowsAsync(() => base.Complex_type_equals_null(async)); + + Assert.Equal(RelationalStrings.CannotCompareComplexTypeToNull, exception.Message); + + AssertSql(); } + + private void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); } diff --git a/test/EFCore.Relational.Specification.Tests/Query/JsonQueryAdHocTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/JsonQueryAdHocTestBase.cs index 360cd51c519..99652dafaff 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/JsonQueryAdHocTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/JsonQueryAdHocTestBase.cs @@ -704,6 +704,112 @@ public MyJsonEntityShadowPropertiesWithCtor(string name) #endregion + #region LazyLoadingProxies + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual async Task Project_proxies_entity_with_json(bool async) + { + var contextFactory = await InitializeAsync( + seed: SeedLazyLoadingProxies, + onConfiguring: OnConfiguringLazyLoadingProxies, + addServices: AddServicesLazyLoadingProxies); + + using (var context = contextFactory.CreateContext()) + { + var query = context.Entities; + + var result = async + ? await query.ToListAsync() + : query.ToList(); + + Assert.Equal(2, result.Count); + } + } + + protected void OnConfiguringLazyLoadingProxies(DbContextOptionsBuilder optionsBuilder) + => optionsBuilder.UseLazyLoadingProxies(); + + protected void AddServicesLazyLoadingProxies(IServiceCollection addServices) + => addServices.AddEntityFrameworkProxies(); + + private void SeedLazyLoadingProxies(MyContextLazyLoadingProxies ctx) + { + var r1 = new MyJsonEntityLazyLoadingProxiesWithCtor("r1", 1); + var c11 = new MyJsonEntityLazyLoadingProxies { Name = "c11", Number = 11 }; + var c12 = new MyJsonEntityLazyLoadingProxies { Name = "c12", Number = 12 }; + var c13 = new MyJsonEntityLazyLoadingProxies { Name = "c13", Number = 13 }; + + var r2 = new MyJsonEntityLazyLoadingProxiesWithCtor("r2", 2); + var c21 = new MyJsonEntityLazyLoadingProxies { Name = "c21", Number = 21 }; + var c22 = new MyJsonEntityLazyLoadingProxies { Name = "c22", Number = 22 }; + + var e1 = new MyEntityLazyLoadingProxies + { + Id = 1, + Name = "e1", + Reference = r1, + Collection = new List { c11, c12, c13 } + }; + + var e2 = new MyEntityLazyLoadingProxies + { + Id = 2, + Name = "e2", + Reference = r2, + Collection = new List { c21, c22 } + }; + + ctx.Entities.AddRange(e1, e2); + ctx.SaveChanges(); + } + + protected class MyContextLazyLoadingProxies : DbContext + { + public MyContextLazyLoadingProxies(DbContextOptions options) + : base(options) + { + } + + public DbSet Entities { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity().Property(x => x.Id).ValueGeneratedNever(); + modelBuilder.Entity().OwnsOne(x => x.Reference, b => b.ToJson()); + modelBuilder.Entity().OwnsMany(x => x.Collection, b => b.ToJson()); + } + } + + public class MyEntityLazyLoadingProxies + { + public int Id { get; set; } + public string Name { get; set; } + + public virtual MyJsonEntityLazyLoadingProxiesWithCtor Reference { get; set; } + public virtual List Collection { get; set; } + } + + public class MyJsonEntityLazyLoadingProxiesWithCtor + { + public MyJsonEntityLazyLoadingProxiesWithCtor(string name, int number) + { + Name = name; + Number = number; + } + + public string Name { get; set; } + public int Number { get; set; } + } + + public class MyJsonEntityLazyLoadingProxies + { + public string Name { get; set; } + public int Number { get; set; } + } + + #endregion + protected TestSqlLoggerFactory TestSqlLoggerFactory => (TestSqlLoggerFactory)ListLoggerFactory; } diff --git a/test/EFCore.Relational.Specification.Tests/Query/JsonQueryFixtureBase.cs b/test/EFCore.Relational.Specification.Tests/Query/JsonQueryFixtureBase.cs index 7fa889511ef..a6d6e12fd46 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/JsonQueryFixtureBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/JsonQueryFixtureBase.cs @@ -393,6 +393,8 @@ public static void AssertAllTypes(JsonOwnedAllTypes expected, JsonOwnedAllTypes Assert.Equal(expected.TestSignedByte, actual.TestSignedByte); Assert.Equal(expected.TestSingle, actual.TestSingle); Assert.Equal(expected.TestTimeSpan, actual.TestTimeSpan); + Assert.Equal(expected.TestDateOnly, actual.TestDateOnly); + Assert.Equal(expected.TestTimeOnly, actual.TestTimeOnly); Assert.Equal(expected.TestUnsignedInt16, actual.TestUnsignedInt16); Assert.Equal(expected.TestUnsignedInt32, actual.TestUnsignedInt32); Assert.Equal(expected.TestUnsignedInt64, actual.TestUnsignedInt64); @@ -417,6 +419,8 @@ public static void AssertAllTypes(JsonOwnedAllTypes expected, JsonOwnedAllTypes AssertPrimitiveCollection(expected.TestSignedByteCollection, actual.TestSignedByteCollection); AssertPrimitiveCollection(expected.TestSingleCollection, actual.TestSingleCollection); AssertPrimitiveCollection(expected.TestTimeSpanCollection, actual.TestTimeSpanCollection); + AssertPrimitiveCollection(expected.TestDateOnlyCollection, actual.TestDateOnlyCollection); + AssertPrimitiveCollection(expected.TestTimeOnlyCollection, actual.TestTimeOnlyCollection); AssertPrimitiveCollection(expected.TestUnsignedInt16Collection, actual.TestUnsignedInt16Collection); AssertPrimitiveCollection(expected.TestUnsignedInt32Collection, actual.TestUnsignedInt32Collection); AssertPrimitiveCollection(expected.TestUnsignedInt64Collection, actual.TestUnsignedInt64Collection); @@ -471,7 +475,13 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con { modelBuilder.Entity().Property(x => x.Id).ValueGeneratedNever(); modelBuilder.Entity().Property(x => x.Id).ValueGeneratedNever(); - modelBuilder.Entity().Property(x => x.Id).ValueGeneratedNever(); + modelBuilder.Entity( + b => + { + b.Property(x => x.Id).ValueGeneratedNever(); + b.Property(x => x.Name); + }); + modelBuilder.Entity().Property(x => x.Id).ValueGeneratedNever(); modelBuilder.Entity().OwnsOne( x => x.OwnedReferenceRoot, b => diff --git a/test/EFCore.Relational.Specification.Tests/Query/JsonQueryTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/JsonQueryTestBase.cs index 4e53cc0c647..d23a51345ce 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/JsonQueryTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/JsonQueryTestBase.cs @@ -1171,7 +1171,7 @@ public virtual Task Json_collection_in_projection_with_composition_where_and_ano .Select(xx => new { xx.Names, xx.Numbers }) .ToList())); - [ConditionalTheory(Skip = "issue #31365")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Json_collection_filter_in_projection(bool async) => AssertQuery( @@ -1179,9 +1179,56 @@ public virtual Task Json_collection_filter_in_projection(bool async) ss => ss.Set() .OrderBy(x => x.Id) .Select(x => x.OwnedCollectionRoot.Where(xx => xx.Name != "Foo").ToList()) - .AsNoTracking()); + .AsNoTracking(), + assertOrder: true, + elementAsserter: (e, a) => + { + AssertCollection(e, a, ordered: true, elementAsserter: (ee, aa) => AssertEqual(ee, aa)); + }); - [ConditionalTheory(Skip = "issue #31365")] + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Json_nested_collection_filter_in_projection(bool async) + => AssertQuery( + async, + ss => ss.Set() + .OrderBy(x => x.Id) + .Select(x => x.OwnedCollectionRoot + .Select(xx => xx.OwnedCollectionBranch.Where(xxx => xxx.Date != new DateTime(2000, 1, 1)).ToList())) + .AsNoTracking(), + assertOrder: true, + elementAsserter: (e, a) => AssertCollection(e, a, ordered: true, elementAsserter: (ee, aa) => AssertCollection(ee, aa, ordered: true))); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Json_nested_collection_anonymous_projection_in_projection(bool async) + => AssertQuery( + async, + ss => ss.Set() + .OrderBy(x => x.Id) + .Select(x => x.OwnedCollectionRoot + .Select(xx => xx.OwnedCollectionBranch.Select(xxx => new + { + xxx.Date, + xxx.Enum, + xxx.Enums, + xxx.Fraction, + xxx.OwnedReferenceLeaf, + xxx.OwnedCollectionLeaf + }).ToList())) + .AsNoTracking(), + assertOrder: true, + elementAsserter: (e, a) => AssertCollection(e, a, ordered: true, elementAsserter: (ee, aa) => AssertCollection(ee, aa, ordered: true, elementAsserter: (eee, aaa) => + { + AssertEqual(eee.Date, aaa.Date); + AssertEqual(eee.Enum, aaa.Enum); + AssertCollection(eee.Enums, aaa.Enums, ordered: true); + AssertEqual(eee.Fraction, aaa.Fraction); + AssertEqual(eee.OwnedReferenceLeaf, aaa.OwnedReferenceLeaf); + AssertCollection(eee.OwnedCollectionLeaf, aaa.OwnedCollectionLeaf, ordered: true); + }))); + + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Json_collection_skip_take_in_projection(bool async) => AssertQuery( @@ -1189,7 +1236,9 @@ public virtual Task Json_collection_skip_take_in_projection(bool async) ss => ss.Set() .OrderBy(x => x.Id) .Select(x => x.OwnedCollectionRoot.OrderBy(xx => xx.Name).Skip(1).Take(5).ToList()) - .AsNoTracking()); + .AsNoTracking(), + assertOrder: true, + elementAsserter: (e, a) => AssertCollection(e, a, ordered: true)); [ConditionalTheory] [MemberData(nameof(IsAsyncData))] @@ -1240,12 +1289,9 @@ public virtual Task Json_collection_skip_take_in_projection_with_json_reference_ .Select(xx => xx.OwnedReferenceBranch).ToList()) .AsNoTracking(), assertOrder: true, - elementAsserter: (e, a) => - { - AssertCollection(e, a, ordered: true); - }); + elementAsserter: (e, a) => AssertCollection(e, a, ordered: true)); - [ConditionalTheory(Skip = "issue #31365")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Json_collection_distinct_in_projection(bool async) => AssertQuery( @@ -1253,9 +1299,23 @@ public virtual Task Json_collection_distinct_in_projection(bool async) ss => ss.Set() .OrderBy(x => x.Id) .Select(x => x.OwnedCollectionRoot.Distinct()) - .AsNoTracking()); + .AsNoTracking(), + assertOrder: true, + elementAsserter: (e, a) => AssertCollection(e, a, elementSorter: ee => (ee.Name, ee.Number))); - [ConditionalTheory(Skip = "issue #31365")] + [ConditionalTheory(Skip = "issue #31397")] + [MemberData(nameof(IsAsyncData))] + public virtual Task Json_collection_anonymous_projection_distinct_in_projection(bool async) + => AssertQuery( + async, + ss => ss.Set() + .OrderBy(x => x.Id) + .Select(x => x.OwnedCollectionRoot.Select(xx => xx.Name).Distinct().ToList()) + .AsNoTracking(), + assertOrder: true, + elementAsserter: (e, a) => AssertCollection(e, a, elementSorter: ee => ee)); + + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Json_collection_leaf_filter_in_projection(bool async) => AssertQuery( @@ -1264,25 +1324,96 @@ public virtual Task Json_collection_leaf_filter_in_projection(bool async) .OrderBy(x => x.Id) .Select(x => x.OwnedReferenceRoot.OwnedReferenceBranch.OwnedCollectionLeaf .Where(xx => xx.SomethingSomething != "Baz").ToList()) - .AsNoTracking()); + .AsNoTracking(), + assertOrder: true, + elementAsserter: (e, a) => AssertCollection(e, a, ordered: true)); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Json_multiple_collection_projections(bool async) + => AssertQuery( + async, + ss => ss.Set() + .OrderBy(x => x.Id) + .Select(x => new + { + First = x.OwnedReferenceRoot.OwnedReferenceBranch.OwnedCollectionLeaf + .Where(xx => xx.SomethingSomething != "Baz").ToList(), + Second = x.OwnedCollectionRoot.Distinct().ToList(), + Third = x.OwnedCollectionRoot + .Select(xx => xx.OwnedCollectionBranch.Where(xxx => xxx.Date != new DateTime(2000, 1, 1)).ToList()), + Fourth = x.EntityCollection.ToList() + }) + .AsNoTracking(), + assertOrder: true, + elementAsserter: (e, a) => + { + AssertCollection(e.First, a.First, ordered: true); + AssertCollection(e.Second, a.Second, elementSorter: ee => (ee.Name, ee.Number)); + AssertCollection(e.Third, a.Third, ordered: true, elementAsserter: (ee, aa) => AssertCollection(ee, aa, ordered: true)); + AssertCollection(e.Fourth, a.Fourth); + }); - [ConditionalTheory(Skip = "issue #31365")] + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Json_branch_collection_distinct_and_other_collection(bool async) + => AssertQuery( + async, + ss => ss.Set() + .OrderBy(x => x.Id) + .Select(x => new + { + First = x.OwnedReferenceRoot.OwnedCollectionBranch.Distinct().ToList(), + Second = x.EntityCollection.ToList() + }) + .AsNoTracking(), + assertOrder: true, + elementAsserter: (e, a) => + { + AssertCollection(e.First, a.First, ordered: true); + AssertCollection(e.Second, a.Second); + }); + + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Json_leaf_collection_distinct_and_other_collection(bool async) + => AssertQuery( + async, + ss => ss.Set() + .OrderBy(x => x.Id) + .Select(x => new + { + First = x.OwnedReferenceRoot.OwnedReferenceBranch.OwnedCollectionLeaf.Distinct().ToList(), + Second = x.EntityCollection.ToList() + }) + .AsNoTracking(), + assertOrder: true, + elementAsserter: (e, a) => + { + AssertCollection(e.First, a.First, ordered: true); + AssertCollection(e.Second, a.Second); + }); + + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Json_collection_SelectMany(bool async) => AssertQuery( async, ss => ss.Set() .SelectMany(x => x.OwnedCollectionRoot) - .AsNoTracking()); + .AsNoTracking(), + elementSorter: e => (e.Number, e.Name)); - [ConditionalTheory(Skip = "issue #31365")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Json_nested_collection_SelectMany(bool async) => AssertQuery( async, ss => ss.Set() .SelectMany(x => x.OwnedReferenceRoot.OwnedCollectionBranch) - .AsNoTracking()); + .AsNoTracking(), + elementSorter: e => (e.Enum, e.Date, e.NullableEnum, e.Fraction)); [ConditionalTheory(Skip = "issue #31364")] [MemberData(nameof(IsAsyncData))] @@ -2066,6 +2197,8 @@ public virtual Task Json_all_types_projection_individual_properties(bool async) x.Reference.TestSignedByte, x.Reference.TestSingle, x.Reference.TestTimeSpan, + x.Reference.TestDateOnly, + x.Reference.TestTimeOnly, x.Reference.TestUnsignedInt16, x.Reference.TestUnsignedInt32, x.Reference.TestUnsignedInt64, @@ -2234,6 +2367,22 @@ public virtual Task Json_predicate_on_timespan(bool async) ss => ss.Set().Where(x => x.Reference.TestTimeSpan != new TimeSpan(3, 2, 0)), entryCount: 6); + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Json_predicate_on_dateonly(bool async) + => AssertQuery( + async, + ss => ss.Set().Where(x => x.Reference.TestDateOnly != new DateOnly(3, 2, 1)), + entryCount: 6); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Json_predicate_on_timeonly(bool async) + => AssertQuery( + async, + ss => ss.Set().Where(x => x.Reference.TestTimeOnly != new TimeOnly(3, 2, 0)), + entryCount: 6); + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Json_predicate_on_unisgnedint16(bool async) diff --git a/test/EFCore.Relational.Specification.Tests/Query/OwnedEntityQueryRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/OwnedEntityQueryRelationalTestBase.cs index 54e78c7f789..f559e7266ea 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/OwnedEntityQueryRelationalTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/OwnedEntityQueryRelationalTestBase.cs @@ -284,6 +284,64 @@ public virtual async Task Owned_entity_with_all_null_properties_entity_equality_ }); } + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual async Task Owned_entity_with_all_null_properties_in_compared_to_null_in_conditional_projection(bool async) + { + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using var context = contextFactory.CreateContext(); + var query = context.RotRutCases + .AsNoTracking() + .OrderBy(e => e.Id) + .Select(e => e.Rot == null ? null : new RotDto { MyApartmentNo = e.Rot.ApartmentNo, MyServiceType = e.Rot.ServiceType }); + + var result = async + ? await query.ToListAsync() + : query.ToList(); + + Assert.Collection( + result, + t => + { + Assert.Equal("1", t.MyApartmentNo); + Assert.Equal(1, t.MyServiceType); + }, + t => + { + Assert.Null(t); + }); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual async Task Owned_entity_with_all_null_properties_in_compared_to_non_null_in_conditional_projection(bool async) + { + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using var context = contextFactory.CreateContext(); + var query = context.RotRutCases + .AsNoTracking() + .OrderBy(e => e.Id) + .Select(e => e.Rot != null ? new RotDto { MyApartmentNo = e.Rot.ApartmentNo, MyServiceType = e.Rot.ServiceType } : null); + + var result = async + ? await query.ToListAsync() + : query.ToList(); + + Assert.Collection( + result, + t => + { + Assert.Equal("1", t.MyApartmentNo); + Assert.Equal(1, t.MyServiceType); + }, + t => + { + Assert.Null(t); + }); + } + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual async Task Owned_entity_with_all_null_properties_property_access_when_not_containing_another_owned_entity(bool async) @@ -364,6 +422,12 @@ public class Rot public string ApartmentNo { get; set; } } + public class RotDto + { + public int? MyServiceType { get; set; } + public string MyApartmentNo { get; set; } + } + public class Rut { public int? Value { get; set; } diff --git a/test/EFCore.Relational.Specification.Tests/Query/PrimitiveCollectionsQueryRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/PrimitiveCollectionsQueryRelationalTestBase.cs new file mode 100644 index 00000000000..e2e7ae9e91e --- /dev/null +++ b/test/EFCore.Relational.Specification.Tests/Query/PrimitiveCollectionsQueryRelationalTestBase.cs @@ -0,0 +1,44 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query; + +public class PrimitiveCollectionsQueryRelationalTestBase : PrimitiveCollectionsQueryTestBase + where TFixture : PrimitiveCollectionsQueryTestBase.PrimitiveCollectionsQueryFixtureBase, new() +{ + public PrimitiveCollectionsQueryRelationalTestBase(TFixture fixture) + : base(fixture) + { + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public override Task Column_collection_Concat_parameter_collection_equality_inline_collection(bool async) + => AssertTranslationFailed(() => base.Column_collection_Concat_parameter_collection_equality_inline_collection(async)); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public override Task Column_collection_equality_inline_collection_with_parameters(bool async) + => AssertTranslationFailed(() => base.Column_collection_equality_inline_collection_with_parameters(async)); + + [ConditionalFact] + public override void Parameter_collection_in_subquery_and_Convert_as_compiled_query() + { + // The array indexing is translated as a subquery over e.g. OPENJSON with LIMIT/OFFSET. + // Since there's a CAST over that, the type mapping inference from the other side (p.String) doesn't propagate inside to the + // subquery. In this case, the CAST operand gets the default CLR type mapping, but that's object in this case. + // We should apply the default type mapping to the parameter, but need to figure out the exact rules when to do this. + var exception = + Assert.Throws(() => base.Parameter_collection_in_subquery_and_Convert_as_compiled_query()); + + Assert.Contains("in the SQL tree does not have a type mapping assigned", exception.Message); + } + + public override async Task Parameter_collection_in_subquery_Union_another_parameter_collection_as_compiled_query(bool async) + { + var message = (await Assert.ThrowsAsync( + () => base.Parameter_collection_in_subquery_Union_another_parameter_collection_as_compiled_query(async))).Message; + + Assert.Equal(RelationalStrings.SetOperationsRequireAtLeastOneSideWithValidTypeMapping("Union"), message); + } +} diff --git a/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonEntityAllTypes.cs b/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonEntityAllTypes.cs index fb821d4989d..4e8ae9199b0 100644 --- a/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonEntityAllTypes.cs +++ b/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonEntityAllTypes.cs @@ -19,11 +19,11 @@ public class JsonEntityAllTypes private Collection _testNullableEnumWithIntConverterCollectionX = new() { JsonEnum.Three }; public int Id { get; set; } - public JsonOwnedAllTypes Reference { get; set; } - public List Collection { get; set; } + public JsonOwnedAllTypes Reference { get; init; } + public List Collection { get; init; } - public string[] TestDefaultStringCollection { get; set; } - public List TestMaxLengthStringCollection { get; set; } + public string[] TestDefaultStringCollection { get; init; } + public List TestMaxLengthStringCollection { get; init; } public IList TestInt16Collection { get; set; } public int[] TestInt32Collection { get; set; } = Array.Empty(); diff --git a/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonEntityBasicForCollection.cs b/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonEntityBasicForCollection.cs index d1b4c7d1c8a..7459c64ac34 100644 --- a/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonEntityBasicForCollection.cs +++ b/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonEntityBasicForCollection.cs @@ -5,8 +5,8 @@ namespace Microsoft.EntityFrameworkCore.TestModels.JsonQuery; public class JsonEntityBasicForCollection { - public int Id { get; set; } - public string Name { get; set; } + public int Id { get; init; } + public string Name { get; init; } public int? ParentId { get; set; } public JsonEntityBasic Parent { get; set; } diff --git a/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonEntityBasicForReference.cs b/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonEntityBasicForReference.cs index c0228606e2e..d4b9e76854e 100644 --- a/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonEntityBasicForReference.cs +++ b/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonEntityBasicForReference.cs @@ -5,9 +5,21 @@ namespace Microsoft.EntityFrameworkCore.TestModels.JsonQuery; public class JsonEntityBasicForReference { - public int Id { get; set; } - public string Name { get; set; } + private int _id; + private string _name; + + public int Id + => _id; + + public string Name + => _name; public int? ParentId { get; set; } public JsonEntityBasic Parent { get; set; } + + public void SetIdAndName(int id, string name) + { + _id = id; + _name = name; + } } diff --git a/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonOwnedAllTypes.cs b/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonOwnedAllTypes.cs index 7b7b06518ee..9682b74d71d 100644 --- a/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonOwnedAllTypes.cs +++ b/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonOwnedAllTypes.cs @@ -27,6 +27,8 @@ public class JsonOwnedAllTypes public DateTime TestDateTime { get; set; } public DateTimeOffset TestDateTimeOffset { get; set; } public TimeSpan TestTimeSpan { get; set; } + public DateOnly TestDateOnly { get; set; } + public TimeOnly TestTimeOnly { get; set; } public float TestSingle { get; set; } public bool TestBoolean { get; set; } public byte TestByte { get; set; } @@ -73,6 +75,8 @@ public IList TestDoubleCollection public List TestDateTimeCollection { get; set; } public IList TestDateTimeOffsetCollection { get; set; } public TimeSpan[] TestTimeSpanCollection { get; set; } = { new(1, 1, 1) }; + public DateOnly[] TestDateOnlyCollection { get; set; } + public TimeOnly[] TestTimeOnlyCollection { get; set; } public List TestSingleCollection { diff --git a/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonOwnedBranch.cs b/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonOwnedBranch.cs index 999b6c8556c..ffa0eed4b50 100644 --- a/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonOwnedBranch.cs +++ b/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonOwnedBranch.cs @@ -9,9 +9,9 @@ public class JsonOwnedBranch public decimal Fraction { get; set; } public JsonEnum Enum { get; set; } - public JsonEnum? NullableEnum { get; set; } - public JsonEnum[] Enums { get; set; } - public JsonEnum?[] NullableEnums { get; set; } + public JsonEnum? NullableEnum { get; init; } + public JsonEnum[] Enums { get; init; } + public JsonEnum?[] NullableEnums { get; init; } public JsonOwnedLeaf OwnedReferenceLeaf { get; set; } public List OwnedCollectionLeaf { get; set; } diff --git a/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonQueryData.cs b/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonQueryData.cs index 81796136632..a5255ece1a8 100644 --- a/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonQueryData.cs +++ b/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonQueryData.cs @@ -329,7 +329,8 @@ public static IReadOnlyList CreateEntitiesBasic() public static IReadOnlyList CreateJsonEntitiesBasicForReference() { - var entity1 = new JsonEntityBasicForReference { Id = 1, Name = "EntityReference1" }; + var entity1 = new JsonEntityBasicForReference(); + entity1.SetIdAndName(1, "EntityReference1"); return new List { entity1 }; } @@ -698,6 +699,8 @@ public static IReadOnlyList CreateJsonEntitiesAllTypes() TestDateTime = DateTime.Parse("01/01/2000 12:34:56"), TestDateTimeOffset = new DateTimeOffset(DateTime.Parse("01/01/2000 12:34:56"), TimeSpan.FromHours(-8.0)), TestTimeSpan = new TimeSpan(0, 10, 9, 8, 7), + TestDateOnly = new DateOnly(2023, 10, 10), + TestTimeOnly = new TimeOnly(11, 12, 13), TestSingle = -1.234F, TestBoolean = true, TestByte = 255, @@ -748,6 +751,8 @@ public static IReadOnlyList CreateJsonEntitiesAllTypes() -1.234F }, TestTimeSpanCollection = new[] { new TimeSpan(0, 10, 9, 8, 7), new TimeSpan(0, -10, 9, 8, 7) }, + TestDateOnlyCollection = new[] { new DateOnly(1234, 1, 23), new DateOnly(4321, 1, 21) }, + TestTimeOnlyCollection = new[] { new TimeOnly(11, 42, 23), new TimeOnly(7, 17, 27) }, TestUnsignedInt16Collection = new List { ushort.MinValue, @@ -801,6 +806,8 @@ public static IReadOnlyList CreateJsonEntitiesAllTypes() TestDateTime = DateTime.Parse("01/01/3000 12:34:56"), TestDateTimeOffset = new DateTimeOffset(DateTime.Parse("01/01/3000 12:34:56"), TimeSpan.FromHours(-8.0)), TestTimeSpan = new TimeSpan(0, 5, 9, 8, 7), + TestDateOnly = new DateOnly(2123, 7, 8), + TestTimeOnly = new TimeOnly(9, 10, 11), TestSingle = -1.24F, TestBoolean = true, TestByte = 25, @@ -851,6 +858,8 @@ public static IReadOnlyList CreateJsonEntitiesAllTypes() -1.234F }, TestTimeSpanCollection = new[] { new TimeSpan(0, 10, 9, 8, 7), new TimeSpan(0, -10, 9, 8, 7) }, + TestDateOnlyCollection = new[] { new DateOnly(2234, 1, 23), new DateOnly(5321, 1, 21) }, + TestTimeOnlyCollection = new[] { new TimeOnly(21, 42, 23), new TimeOnly(17, 17, 27) }, TestUnsignedInt16Collection = new[] { ushort.MinValue, (ushort)0, ushort.MaxValue }, TestUnsignedInt32Collection = new[] { uint.MinValue, (uint)0, uint.MaxValue }, TestUnsignedInt64Collection = new ObservableCollection @@ -899,6 +908,8 @@ public static IReadOnlyList CreateJsonEntitiesAllTypes() TestDateTime = DateTime.Parse("11/11/2100 12:34:56"), TestDateTimeOffset = new DateTimeOffset(DateTime.Parse("11/11/2200 12:34:56"), TimeSpan.FromHours(-5.0)), TestTimeSpan = new TimeSpan(0, 6, 5, 4, 3), + TestDateOnly = new DateOnly(2323, 4, 3), + TestTimeOnly = new TimeOnly(5, 7, 8), TestSingle = -1.4F, TestBoolean = false, TestByte = 25, @@ -949,6 +960,8 @@ public static IReadOnlyList CreateJsonEntitiesAllTypes() -1.234F }, TestTimeSpanCollection = new[] { new TimeSpan(0, 10, 9, 8, 7), new TimeSpan(0, -10, 9, 8, 7) }, + TestDateOnlyCollection = new[] { new DateOnly(3234, 1, 23), new DateOnly(4331, 1, 21) }, + TestTimeOnlyCollection = new[] { new TimeOnly(13, 42, 23), new TimeOnly(7, 17, 25) }, TestUnsignedInt16Collection = new[] { ushort.MinValue, (ushort)0, ushort.MaxValue }, TestUnsignedInt32Collection = new[] { uint.MinValue, (uint)0, uint.MaxValue }, TestUnsignedInt64Collection = new ObservableCollection @@ -997,6 +1010,8 @@ public static IReadOnlyList CreateJsonEntitiesAllTypes() TestDateTime = DateTime.Parse("11/11/3100 12:34:56"), TestDateTimeOffset = new DateTimeOffset(DateTime.Parse("11/11/3200 12:34:56"), TimeSpan.FromHours(-5.0)), TestTimeSpan = new TimeSpan(0, 6, 5, 2, 3), + TestDateOnly = new DateOnly(4019, 2, 25), + TestTimeOnly = new TimeOnly(5, 30, 42), TestSingle = -1.4F, TestBoolean = false, TestByte = 25, @@ -1047,6 +1062,8 @@ public static IReadOnlyList CreateJsonEntitiesAllTypes() -1.234F }, TestTimeSpanCollection = new[] { new TimeSpan(0, 10, 9, 8, 7), new TimeSpan(0, -10, 9, 8, 7) }, + TestDateOnlyCollection = new[] { new DateOnly(1638, 1, 23), new DateOnly(4321, 1, 21) }, + TestTimeOnlyCollection = new[] { new TimeOnly(8, 22, 23), new TimeOnly(7, 27, 37) }, TestUnsignedInt16Collection = new[] { ushort.MinValue, (ushort)0, ushort.MaxValue }, TestUnsignedInt32Collection = new[] { uint.MinValue, (uint)0, uint.MaxValue }, TestUnsignedInt64Collection = new ObservableCollection diff --git a/test/EFCore.Relational.Specification.Tests/Update/JsonUpdateFixtureBase.cs b/test/EFCore.Relational.Specification.Tests/Update/JsonUpdateFixtureBase.cs index c6b108148b0..ad8905611be 100644 --- a/test/EFCore.Relational.Specification.Tests/Update/JsonUpdateFixtureBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Update/JsonUpdateFixtureBase.cs @@ -192,6 +192,13 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con x => x == true ? "Y" : "N")); }); + modelBuilder.Entity( + b => + { + b.Property(x => x.Id); + b.Property(x => x.Name); + }); + base.OnModelCreating(modelBuilder, context); } diff --git a/test/EFCore.Relational.Specification.Tests/Update/JsonUpdateTestBase.cs b/test/EFCore.Relational.Specification.Tests/Update/JsonUpdateTestBase.cs index 34523ea581f..ce7aa5f34b7 100644 --- a/test/EFCore.Relational.Specification.Tests/Update/JsonUpdateTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Update/JsonUpdateTestBase.cs @@ -1071,6 +1071,50 @@ public virtual Task Edit_single_property_timespan() Assert.Equal(new TimeSpan(0, 10, 1, 1, 7), result.Collection[0].TestTimeSpan); }); + [ConditionalFact] + public virtual Task Edit_single_property_dateonly() + => TestHelpers.ExecuteWithStrategyInTransactionAsync( + CreateContext, + UseTransaction, + async context => + { + var query = await context.JsonEntitiesAllTypes.ToListAsync(); + var entity = query.Single(x => x.Id == 1); + entity.Reference.TestDateOnly = new DateOnly(1023, 1, 1); + entity.Collection[0].TestDateOnly = new DateOnly(2000, 2, 4); + + ClearLog(); + await context.SaveChangesAsync(); + }, + async context => + { + var result = await context.Set().SingleAsync(x => x.Id == 1); + Assert.Equal(new DateOnly(1023, 1, 1), result.Reference.TestDateOnly); + Assert.Equal(new DateOnly(2000, 2, 4), result.Collection[0].TestDateOnly); + }); + + [ConditionalFact] + public virtual Task Edit_single_property_timeonly() + => TestHelpers.ExecuteWithStrategyInTransactionAsync( + CreateContext, + UseTransaction, + async context => + { + var query = await context.JsonEntitiesAllTypes.ToListAsync(); + var entity = query.Single(x => x.Id == 1); + entity.Reference.TestTimeOnly = new TimeOnly(1, 1, 7); + entity.Collection[0].TestTimeOnly = new TimeOnly(1, 1, 7); + + ClearLog(); + await context.SaveChangesAsync(); + }, + async context => + { + var result = await context.Set().SingleAsync(x => x.Id == 1); + Assert.Equal(new TimeOnly(1, 1, 7), result.Reference.TestTimeOnly); + Assert.Equal(new TimeOnly(1, 1, 7), result.Collection[0].TestTimeOnly); + }); + [ConditionalFact] public virtual Task Edit_single_property_uint16() => TestHelpers.ExecuteWithStrategyInTransactionAsync( @@ -2016,6 +2060,60 @@ public virtual Task Edit_single_property_collection_of_timespan() Assert.False(result.Collection[0].NewCollectionSet); }); + [ConditionalFact] + public virtual Task Edit_single_property_collection_of_dateonly() + => TestHelpers.ExecuteWithStrategyInTransactionAsync( + CreateContext, + UseTransaction, + async context => + { + var query = await context.JsonEntitiesAllTypes.ToListAsync(); + var entity = query.Single(x => x.Id == 1); + entity.Reference.TestDateOnlyCollection[0] = new DateOnly(1, 1, 7); + entity.Collection[0].TestDateOnlyCollection[1] = new DateOnly(1, 1, 7); + + ClearLog(); + await context.SaveChangesAsync(); + }, + async context => + { + var result = await context.Set().SingleAsync(x => x.Id == 1); + Assert.Equal( + new[] { new DateOnly(1, 1, 7), new DateOnly(4321, 1, 21) }, result.Reference.TestDateOnlyCollection); + Assert.Equal( + new[] { new DateOnly(3234, 1, 23), new DateOnly(1, 1, 7) }, result.Collection[0].TestDateOnlyCollection); + + Assert.False(result.Reference.NewCollectionSet); + Assert.False(result.Collection[0].NewCollectionSet); + }); + + [ConditionalFact] + public virtual Task Edit_single_property_collection_of_timeonly() + => TestHelpers.ExecuteWithStrategyInTransactionAsync( + CreateContext, + UseTransaction, + async context => + { + var query = await context.JsonEntitiesAllTypes.ToListAsync(); + var entity = query.Single(x => x.Id == 1); + entity.Reference.TestTimeOnlyCollection[0] = new TimeOnly(1, 1, 7); + entity.Collection[0].TestTimeOnlyCollection[1] = new TimeOnly(1, 1, 7); + + ClearLog(); + await context.SaveChangesAsync(); + }, + async context => + { + var result = await context.Set().SingleAsync(x => x.Id == 1); + Assert.Equal( + new[] { new TimeOnly(1, 1, 7), new TimeOnly(7, 17, 27) }, result.Reference.TestTimeOnlyCollection); + Assert.Equal( + new[] { new TimeOnly(13, 42, 23), new TimeOnly(1, 1, 7) }, result.Collection[0].TestTimeOnlyCollection); + + Assert.False(result.Reference.NewCollectionSet); + Assert.False(result.Collection[0].NewCollectionSet); + }); + [ConditionalFact] public virtual Task Edit_single_property_collection_of_uint16() => TestHelpers.ExecuteWithStrategyInTransactionAsync( diff --git a/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs b/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs index 6efa346ff7f..97377dab774 100644 --- a/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs +++ b/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs @@ -726,9 +726,7 @@ public virtual void Detects_shared_table_root_cycle() modelBuilder.Entity().HasOne().WithOne().HasForeignKey(a => a.Id).HasPrincipalKey(b => b.Id).IsRequired(); modelBuilder.Entity().ToTable("Table"); - VerifyError( - CoreStrings.IdentifyingRelationshipCycle("A -> B"), - modelBuilder); + VerifyError(CoreStrings.RelationshipCycle("B", "AId", "ValueConverter"), modelBuilder); } [ConditionalFact] diff --git a/test/EFCore.Relational.Tests/Migrations/Internal/MigrationsModelDifferTest.cs b/test/EFCore.Relational.Tests/Migrations/Internal/MigrationsModelDifferTest.cs index 4007248e37b..b0f732c5b56 100644 --- a/test/EFCore.Relational.Tests/Migrations/Internal/MigrationsModelDifferTest.cs +++ b/test/EFCore.Relational.Tests/Migrations/Internal/MigrationsModelDifferTest.cs @@ -8284,6 +8284,62 @@ public void Change_TPT_to_TPC_with_FKs_and_seed_data() Assert.Equal(ReferentialAction.Cascade, operation.OnDelete); })); + [ConditionalFact] + public void Change_TPT_to_TPC_with_excluded_base() + => Execute( + common => + { + common.Entity( + "Order", + x => + { + x.ToTable("Order", t => t.ExcludeFromMigrations()); + x.Property("Id"); + x.Property("Address"); + }); + common.Entity( + "DetailedOrder", + x => + { + x.ToTable("DetailedOrder"); + x.HasBaseType("Order"); + x.Property("Description").HasColumnName("Description"); + }); + }, + _ => { }, + target => + { + target.Entity("Order").UseTpcMappingStrategy(); + }, + upOperations => + { + Assert.Equal(2, upOperations.Count); + + var dropForeignKeyOperation = Assert.IsType(upOperations[0]); + Assert.Null(dropForeignKeyOperation.Schema); + Assert.Equal("DetailedOrder", dropForeignKeyOperation.Table); + Assert.Equal("FK_DetailedOrder_Order_Id", dropForeignKeyOperation.Name); + + var addColumnOperation = Assert.IsType(upOperations[1]); + Assert.Null(addColumnOperation.Schema); + Assert.Equal("DetailedOrder", addColumnOperation.Table); + Assert.Equal("Address", addColumnOperation.Name); + }, + downOperations => + { + Assert.Equal(2, downOperations.Count); + + var dropColumnOperation = Assert.IsType(downOperations[0]); + Assert.Null(dropColumnOperation.Schema); + Assert.Equal("DetailedOrder", dropColumnOperation.Table); + Assert.Equal("Address", dropColumnOperation.Name); + + var addForeignKeyOperation = Assert.IsType(downOperations[1]); + Assert.Null(addForeignKeyOperation.Schema); + Assert.Equal("DetailedOrder", addForeignKeyOperation.Table); + Assert.Equal("FK_DetailedOrder_Order_Id", addForeignKeyOperation.Name); + }); + [ConditionalFact] public void Add_foreign_key_on_base_type() => Execute( diff --git a/test/EFCore.Relational.Tests/ModelBuilding/RelationalModelBuilderTest.cs b/test/EFCore.Relational.Tests/ModelBuilding/RelationalModelBuilderTest.cs index a888a5810ae..d0fb6786d21 100644 --- a/test/EFCore.Relational.Tests/ModelBuilding/RelationalModelBuilderTest.cs +++ b/test/EFCore.Relational.Tests/ModelBuilding/RelationalModelBuilderTest.cs @@ -341,6 +341,30 @@ public virtual void Configuring_direction_on_RowsAffectedParameter_throws() public abstract class RelationalComplexTypeTestBase : ComplexTypeTestBase { + [ConditionalFact] + public virtual void Can_use_TPH() + { + var modelBuilder = CreateModelBuilder(); + + modelBuilder + .Ignore() + .Ignore() + .Entity(); + + modelBuilder + .Entity() + .ComplexProperty(e => e.Customer) + .Ignore(c => c.Details) + .Ignore(c => c.Orders) + .Property(c => c.Name).IsRequired(); + + var model = modelBuilder.FinalizeModel(); + var complexProperty = model.FindEntityType(typeof(ComplexProperties))!.GetComplexProperties().Single(); + var property = complexProperty.ComplexType.FindProperty(nameof(Customer.Name))!; + + Assert.True(property.IsColumnNullable()); + } + [ConditionalFact] public virtual void Can_use_table_splitting() { diff --git a/test/EFCore.Relational.Tests/Storage/RelationalCommandTest.cs b/test/EFCore.Relational.Tests/Storage/RelationalCommandTest.cs index 1cb19610735..1eb3ef7d558 100644 --- a/test/EFCore.Relational.Tests/Storage/RelationalCommandTest.cs +++ b/test/EFCore.Relational.Tests/Storage/RelationalCommandTest.cs @@ -1372,7 +1372,7 @@ public void Validate(IDbContextOptions options) public WarningsConfiguration WarningsConfiguration => null; - public virtual bool ShouldWarnForEnumType(Type type) + public virtual bool ShouldWarnForStringEnumValueInJson(Type enumType) => true; } diff --git a/test/EFCore.Relational.Tests/Storage/RelationalTypeMapperTest.cs b/test/EFCore.Relational.Tests/Storage/RelationalTypeMapperTest.cs index 1b2310f06b3..387f89697ac 100644 --- a/test/EFCore.Relational.Tests/Storage/RelationalTypeMapperTest.cs +++ b/test/EFCore.Relational.Tests/Storage/RelationalTypeMapperTest.cs @@ -103,7 +103,7 @@ public void Does_type_mapping_from_named_binary_with_no_MaxLength() public void Key_with_store_type_is_picked_up_by_FK() { var model = CreateModel(); - var mapper = CreateRelationalTypeMappingSource(); + var mapper = CreateRelationalTypeMappingSource(model); Assert.Equal( "money", @@ -118,7 +118,7 @@ public void Key_with_store_type_is_picked_up_by_FK() public void Does_default_type_mapping_from_decimal() { var model = CreateModel(); - var mapper = CreateRelationalTypeMappingSource(); + var mapper = CreateRelationalTypeMappingSource(model); Assert.Equal( "default_decimal_mapping", @@ -129,7 +129,7 @@ public void Does_default_type_mapping_from_decimal() public void Does_type_mapping_from_decimal_with_precision_only() { var model = CreateModel(); - var mapper = CreateRelationalTypeMappingSource(); + var mapper = CreateRelationalTypeMappingSource(model); Assert.Equal( "decimal_mapping(16)", @@ -140,7 +140,7 @@ public void Does_type_mapping_from_decimal_with_precision_only() public void Does_type_mapping_from_decimal_with_precision_and_scale() { var model = CreateModel(); - var mapper = CreateRelationalTypeMappingSource(); + var mapper = CreateRelationalTypeMappingSource(model); Assert.Equal( "decimal_mapping(18,7)", @@ -237,22 +237,22 @@ public void StoreTypeNameBase_is_trimmed() Assert.False(mapping.IsFixedLength); } - protected override IRelationalTypeMappingSource CreateRelationalTypeMappingSource() + protected override IRelationalTypeMappingSource CreateRelationalTypeMappingSource(IModel model) => new TestRelationalTypeMappingSource( TestServiceFactory.Instance.Create(), TestServiceFactory.Instance.Create()); public RelationalTypeMapping GetMapping(Type type) - => CreateRelationalTypeMappingSource().FindMapping(type); + => CreateRelationalTypeMappingSource(CreateModel()).FindMapping(type); public RelationalTypeMapping GetMapping(IProperty property) - => CreateRelationalTypeMappingSource().FindMapping(property); + => CreateRelationalTypeMappingSource(CreateModel()).FindMapping(property); [ConditionalFact] public void String_key_with_max_fixed_length_is_picked_up_by_FK() { var model = CreateModel(); - var mapper = CreateRelationalTypeMappingSource(); + var mapper = CreateRelationalTypeMappingSource(model); Assert.Equal( "just_string_fixed(200)", @@ -267,7 +267,7 @@ public void String_key_with_max_fixed_length_is_picked_up_by_FK() public void Binary_key_with_max_fixed_length_is_picked_up_by_FK() { var model = CreateModel(); - var mapper = CreateRelationalTypeMappingSource(); + var mapper = CreateRelationalTypeMappingSource(model); Assert.Equal( "just_binary_fixed(100)", @@ -282,7 +282,7 @@ public void Binary_key_with_max_fixed_length_is_picked_up_by_FK() public void String_key_with_unicode_is_picked_up_by_FK() { var model = CreateModel(); - var mapper = CreateRelationalTypeMappingSource(); + var mapper = CreateRelationalTypeMappingSource(model); Assert.Equal( "ansi_string(900)", @@ -297,7 +297,7 @@ public void String_key_with_unicode_is_picked_up_by_FK() public void Key_store_type_is_preferred_if_specified() { var model = CreateModel(); - var mapper = CreateRelationalTypeMappingSource(); + var mapper = CreateRelationalTypeMappingSource(model); Assert.Equal( "money", @@ -312,7 +312,7 @@ public void Key_store_type_is_preferred_if_specified() public void String_FK_max_length_is_preferred_if_specified() { var model = CreateModel(); - var mapper = CreateRelationalTypeMappingSource(); + var mapper = CreateRelationalTypeMappingSource(model); Assert.Equal( "just_string_fixed(200)", @@ -327,7 +327,7 @@ public void String_FK_max_length_is_preferred_if_specified() public void Binary_FK_max_length_is_preferred_if_specified() { var model = CreateModel(); - var mapper = CreateRelationalTypeMappingSource(); + var mapper = CreateRelationalTypeMappingSource(model); Assert.Equal( "just_binary_fixed(100)", @@ -342,7 +342,7 @@ public void Binary_FK_max_length_is_preferred_if_specified() public void String_FK_unicode_is_preferred_if_specified() { var model = CreateModel(); - var mapper = CreateRelationalTypeMappingSource(); + var mapper = CreateRelationalTypeMappingSource(model); Assert.Equal( "ansi_string(900)", diff --git a/test/EFCore.Relational.Tests/Storage/RelationalTypeMapperTestBase.cs b/test/EFCore.Relational.Tests/Storage/RelationalTypeMapperTestBase.cs index d80acf7cc44..50021f8ba0e 100644 --- a/test/EFCore.Relational.Tests/Storage/RelationalTypeMapperTestBase.cs +++ b/test/EFCore.Relational.Tests/Storage/RelationalTypeMapperTestBase.cs @@ -85,7 +85,7 @@ protected RelationalTypeMapping GetTypeMapping( } }).FinalizeModel(); - return CreateRelationalTypeMappingSource().GetMapping(propertyType, model); + return CreateRelationalTypeMappingSource(model).GetMapping(propertyType, model); } else { @@ -135,12 +135,12 @@ protected RelationalTypeMapping GetTypeMapping( } var model = modelBuilder.Model.FinalizeModel(); - return CreateRelationalTypeMappingSource().GetMapping(model.FindEntityType(typeof(MyType)).FindProperty(property.Name)); + return CreateRelationalTypeMappingSource(model).GetMapping(model.FindEntityType(typeof(MyType)).FindProperty(property.Name)); } } protected abstract ModelBuilder CreateModelBuilder(Action configureConventions = null); - protected abstract IRelationalTypeMappingSource CreateRelationalTypeMappingSource(); + protected abstract IRelationalTypeMappingSource CreateRelationalTypeMappingSource(IModel model); protected class MyType { diff --git a/test/EFCore.Relational.Tests/Storage/RelationalTypeMappingTest.cs b/test/EFCore.Relational.Tests/Storage/RelationalTypeMappingTest.cs index 5a800c36ce2..d97d869037e 100644 --- a/test/EFCore.Relational.Tests/Storage/RelationalTypeMappingTest.cs +++ b/test/EFCore.Relational.Tests/Storage/RelationalTypeMappingTest.cs @@ -74,7 +74,7 @@ public virtual void Create_and_clone_with_converter(Type mappingType, Type type) protected static RelationalTypeMapping AssertClone(Type type, RelationalTypeMapping mapping) { - var clone = mapping.Clone("", null); + var clone = mapping.WithStoreTypeAndSize("", null); Assert.NotSame(mapping, clone); Assert.Same(mapping.GetType(), clone.GetType()); @@ -89,7 +89,7 @@ protected static RelationalTypeMapping AssertClone(Type type, RelationalTypeMapp Assert.Equal(StoreTypePostfix.PrecisionAndScale, clone.StoreTypePostfix); var newConverter = CreateConverter(typeof(object), type); - clone = (RelationalTypeMapping)mapping.Clone(newConverter); + clone = (RelationalTypeMapping)mapping.WithComposedConverter(newConverter); Assert.NotSame(mapping, clone); Assert.Same(mapping.GetType(), clone.GetType()); @@ -130,7 +130,7 @@ protected virtual void ConversionCloneTest( null, null); - var clone = mapping.Clone("", 66); + var clone = mapping.WithStoreTypeAndSize("", 66); Assert.NotSame(mapping, clone); Assert.Same(mapping.GetType(), clone.GetType()); @@ -150,7 +150,7 @@ protected virtual void ConversionCloneTest( Assert.Equal(StoreTypePostfix.Size, clone.StoreTypePostfix); var newConverter = CreateConverter(typeof(object), type); - clone = (RelationalTypeMapping)mapping.Clone(newConverter); + clone = (RelationalTypeMapping)mapping.WithComposedConverter(newConverter); Assert.NotSame(mapping, clone); Assert.Same(mapping.GetType(), clone.GetType()); @@ -194,7 +194,7 @@ protected virtual void UnicodeConversionCloneTest( null, null); - var clone = mapping.Clone("", 66); + var clone = mapping.WithStoreTypeAndSize("", 66); Assert.NotSame(mapping, clone); Assert.Same(mapping.GetType(), clone.GetType()); @@ -216,7 +216,7 @@ protected virtual void UnicodeConversionCloneTest( Assert.Equal(StoreTypePostfix.Size, clone.StoreTypePostfix); var newConverter = CreateConverter(typeof(object), type); - clone = (RelationalTypeMapping)mapping.Clone(newConverter); + clone = (RelationalTypeMapping)mapping.WithComposedConverter(newConverter); Assert.NotSame(mapping, clone); Assert.Same(mapping.GetType(), clone.GetType()); diff --git a/test/EFCore.Relational.Tests/TestUtilities/TestRelationalTypeMappingSource.cs b/test/EFCore.Relational.Tests/TestUtilities/TestRelationalTypeMappingSource.cs index 434f491c029..cd8ec9d7bb4 100644 --- a/test/EFCore.Relational.Tests/TestUtilities/TestRelationalTypeMappingSource.cs +++ b/test/EFCore.Relational.Tests/TestUtilities/TestRelationalTypeMappingSource.cs @@ -220,7 +220,7 @@ protected override RelationalTypeMapping FindMapping(in RelationalTypeMappingInf { return storeTypeName != null && !mapping.StoreType.Equals(storeTypeName, StringComparison.Ordinal) - ? mapping.Clone(storeTypeName, mapping.Size) + ? mapping.WithStoreTypeAndSize(storeTypeName, mapping.Size) : mapping; } } diff --git a/test/EFCore.Specification.Tests/ComplexTypesTrackingTestBase.cs b/test/EFCore.Specification.Tests/ComplexTypesTrackingTestBase.cs index e20ca73be5f..470c400e128 100644 --- a/test/EFCore.Specification.Tests/ComplexTypesTrackingTestBase.cs +++ b/test/EFCore.Specification.Tests/ComplexTypesTrackingTestBase.cs @@ -7,15 +7,10 @@ namespace Microsoft.EntityFrameworkCore; -public abstract class ComplexTypesTrackingTestBase : IClassFixture +public abstract class ComplexTypesTrackingTestBase(TFixture fixture) : IClassFixture where TFixture : ComplexTypesTrackingTestBase.FixtureBase { - protected ComplexTypesTrackingTestBase(TFixture fixture) - { - Fixture = fixture; - } - - protected TFixture Fixture { get; } + protected TFixture Fixture { get; } = fixture; [ConditionalTheory] [InlineData(EntityState.Added, false)] @@ -95,6 +90,12 @@ public virtual Task Can_track_entity_with_complex_readonly_structs(EntityState s public virtual void Can_mark_complex_readonly_readonly_struct_properties_modified(bool trackFromQuery) => MarkModifiedTest(trackFromQuery, CreatePubWithReadonlyStructs()); + [ConditionalTheory] + [InlineData(false)] + [InlineData(true)] + public virtual void Can_read_original_values_for_properties_of_readonly_structs(bool trackFromQuery) + => ReadOriginalValuesTest(trackFromQuery, CreatePubWithReadonlyStructs()); + [ConditionalTheory] [InlineData(false)] [InlineData(true)] @@ -209,6 +210,12 @@ public virtual Task Can_track_entity_with_complex_readonly_structs_with_fields(E public virtual void Can_mark_complex_readonly_readonly_struct_properties_modified_with_fields(bool trackFromQuery) => MarkModifiedTest(trackFromQuery, CreateFieldPubWithReadonlyStructs()); + [ConditionalTheory(Skip = "Constructor binding")] + [InlineData(false)] + [InlineData(true)] + public virtual void Can_read_original_values_for_properties_of_readonly_structs_with_fields(bool trackFromQuery) + => ReadOriginalValuesTest(trackFromQuery, CreateFieldPubWithReadonlyStructs()); + [ConditionalTheory(Skip = "Constructor binding")] [InlineData(false)] [InlineData(true)] @@ -459,6 +466,63 @@ public virtual void Detect_changes_in_complex_type_properties(bool trackFromQuer Assert.Equal(5.0m, coverChargeEntry.OriginalValue); } + [ConditionalTheory] + [InlineData(false)] + [InlineData(true)] + public virtual async Task Throws_only_when_saving_with_null_top_level_complex_property(bool async) + { + using var context = CreateContext(); + + var yogurt = CreateYogurt(nullMilk: true); + var entry = async ? await context.AddAsync(yogurt) : context.Add(yogurt); + entry.State = EntityState.Unchanged; + context.ChangeTracker.DetectChanges(); + entry.State = EntityState.Modified; + + Assert.Equal( + CoreStrings.NullRequiredComplexProperty("Yogurt", "Milk"), + (await Assert.ThrowsAsync( + () => async ? context.SaveChangesAsync() : Task.FromResult(context.SaveChanges()))).Message); + } + + [ConditionalTheory] + [InlineData(false)] + [InlineData(true)] + public virtual async Task Throws_only_when_saving_with_null_second_level_complex_property(bool async) + { + using var context = CreateContext(); + + var yogurt = CreateYogurt(nullManufacturer: true); + var entry = async ? await context.AddAsync(yogurt) : context.Add(yogurt); + entry.State = EntityState.Unchanged; + context.ChangeTracker.DetectChanges(); + entry.State = EntityState.Modified; + + Assert.Equal( + CoreStrings.NullRequiredComplexProperty("Culture", "Manufacturer"), + (await Assert.ThrowsAsync( + () => async ? context.SaveChangesAsync() : Task.FromResult(context.SaveChanges()))).Message); + } + + [ConditionalTheory] + [InlineData(false)] + [InlineData(true)] + public virtual async Task Throws_only_when_saving_with_null_third_level_complex_property(bool async) + { + using var context = CreateContext(); + + var yogurt = CreateYogurt(nullTag: true); + var entry = async ? await context.AddAsync(yogurt) : context.Add(yogurt); + entry.State = EntityState.Unchanged; + context.ChangeTracker.DetectChanges(); + entry.State = EntityState.Modified; + + Assert.Equal( + CoreStrings.NullRequiredComplexProperty("License", "Tag"), + (await Assert.ThrowsAsync( + () => async ? context.SaveChangesAsync() : Task.FromResult(context.SaveChanges()))).Message); + } + [ConditionalTheory] [InlineData(false)] [InlineData(true)] @@ -952,15 +1016,53 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con b.ComplexProperty(e => e.FeaturedTeam); b.ComplexProperty(e => e.FeaturedTeam); }); + + modelBuilder.Entity( + b => + { + b.ComplexProperty( + e => e.Culture, b => + { + b.ComplexProperty( + e => e.License, b => + { + b.ComplexProperty(e => e.Tag); + b.ComplexProperty(e => e.Tog); + }); + b.ComplexProperty( + e => e.Manufacturer, b => + { + b.ComplexProperty(e => e.Tag); + b.ComplexProperty(e => e.Tog); + }); + }); + + b.ComplexProperty( + e => e.Milk, b => + { + b.ComplexProperty( + e => e.License, b => + { + b.ComplexProperty(e => e.Tag); + b.ComplexProperty(e => e.Tog); + }); + b.ComplexProperty( + e => e.Manufacturer, b => + { + b.ComplexProperty(e => e.Tag); + b.ComplexProperty(e => e.Tog); + }); + }); + }); } } - protected static Pub CreatePub() + protected static Pub CreatePub(bool nullActivity = false, bool nullChampions = false, bool nullRunnersUp = false) => new() { Id = Guid.NewGuid(), Name = "The FBI", - LunchtimeActivity = new() + LunchtimeActivity = nullActivity ? null! : new() { Name = "Pub Quiz", Day = DayOfWeek.Monday, @@ -968,7 +1070,7 @@ protected static Pub CreatePub() Notes = new[] { "One", "Two", "Three" }, CoverCharge = 2.0m, IsTeamBased = true, - Champions = new() + Champions = nullChampions ? null! : new() { Name = "Clueless", Members = @@ -978,7 +1080,7 @@ protected static Pub CreatePub() "Theresa" } }, - RunnersUp = new() + RunnersUp = nullRunnersUp ? null! : new() { Name = "ZZ", Members = @@ -1641,5 +1743,110 @@ protected record FieldTeamRecord public string Name = null!; public List Members = null!; } + + protected class Yogurt + { + public Guid Id { get; set; } + public Culture Culture { get; set; } + public Milk Milk { get; set; } = null!; + } + + protected struct Culture + { + public string Species { get; set; } + public string? Subspecies { get; set; } + public int Rating { get; set; } + public bool? Validation { get; set; } + public Manufacturer Manufacturer { get; set; } + public License License { get; set; } + } + + protected class Milk + { + public string Species { get; set; } = null!; + public string? Subspecies { get; set; } + public int Rating { get; set; } + public bool? Validation { get; set; } + public Manufacturer Manufacturer { get; set; } = null!; + public License License { get; set; } + } + + protected class Manufacturer + { + public string? Name { get; set; } + public int Rating { get; set; } + public Tag Tag { get; set; } = null!; + public Tog Tog { get; set; } + } + + protected struct License + { + public string Title { get; set; } + public decimal Charge { get; set; } + public Tag Tag { get; set; } + public Tog Tog { get; set; } + } + + protected class Tag + { + public string? Text { get; set; } + } + + protected struct Tog + { + public string? Text { get; set; } + } + + protected static Yogurt CreateYogurt(bool nullMilk = false, bool nullManufacturer = false, bool nullTag = false) + => new() + { + Id = Guid.NewGuid(), + Culture = new() + { + License = new() + { + Charge = 1.0m, + Tag = nullTag ? null! : new() { Text = "Ta1" }, + Title = "Ti1", + Tog = new() { Text = "To1" } + }, + Manufacturer = nullManufacturer + ? null! + : new() + { + Name = "M1", + Rating = 7, + Tag = nullTag ? null! : new() { Text = "Ta2" }, + Tog = new() { Text = "To2" } + }, + Rating = 8, + Species = "S1", + Validation = false + }, + Milk = nullMilk + ? null! + : new() + { + License = new() + { + Charge = 1.0m, + Tag = nullTag ? null! : new() { Text = "Ta1" }, + Title = "Ti1", + Tog = new() { Text = "To1" } + }, + Manufacturer = nullManufacturer + ? null! + : new() + { + Name = "M1", + Rating = 7, + Tag = nullTag ? null! : new() { Text = "Ta2" }, + Tog = new() { Text = "To2" } + }, + Rating = 8, + Species = "S1", + Validation = false + } + }; } diff --git a/test/EFCore.Specification.Tests/MaterializationInterceptionTestBase.cs b/test/EFCore.Specification.Tests/MaterializationInterceptionTestBase.cs index 7d0cbef687e..fd8520508a8 100644 --- a/test/EFCore.Specification.Tests/MaterializationInterceptionTestBase.cs +++ b/test/EFCore.Specification.Tests/MaterializationInterceptionTestBase.cs @@ -261,6 +261,97 @@ public virtual async Task Intercept_query_materialization_with_owned_types(bool } } + [ConditionalTheory] // Issue #31365 + [InlineData(false)] + [InlineData(true)] + public virtual async Task Intercept_query_materialization_with_owned_types_projecting_collection(bool async) + { + var creatingInstanceCounts = new Dictionary(); + var createdInstanceCounts = new Dictionary(); + var initializingInstanceCounts = new Dictionary(); + var initializedInstanceCounts = new Dictionary(); + LibraryContext? context = null; + + var interceptors = new[] + { + new ValidatingMaterializationInterceptor( + (data, instance, method) => + { + Assert.Same(context, data.Context); + Assert.Equal(QueryTrackingBehavior.NoTracking, data.QueryTrackingBehavior); + + int count; + var clrType = data.EntityType.ClrType; + switch (method) + { + case nameof(IMaterializationInterceptor.CreatingInstance): + count = creatingInstanceCounts.GetOrAddNew(clrType); + creatingInstanceCounts[clrType] = count + 1; + Assert.Null(instance); + break; + case nameof(IMaterializationInterceptor.CreatedInstance): + count = createdInstanceCounts.GetOrAddNew(clrType); + createdInstanceCounts[clrType] = count + 1; + Assert.Same(clrType, instance!.GetType()); + break; + case nameof(IMaterializationInterceptor.InitializingInstance): + count = initializingInstanceCounts.GetOrAddNew(clrType); + initializingInstanceCounts[clrType] = count + 1; + Assert.Same(clrType, instance!.GetType()); + break; + case nameof(IMaterializationInterceptor.InitializedInstance): + count = initializedInstanceCounts.GetOrAddNew(clrType); + initializedInstanceCounts[clrType] = count + 1; + Assert.Same(clrType, instance!.GetType()); + break; + } + }) + }; + + using (context = CreateContext(interceptors, inject: true)) + { + context.Add( + new TestEntity30244 + { + Id = _id++, + Name = "TestIssue", + Settings = { new("Value1", "1"), new("Value2", "9") } + }); + + _ = async + ? await context.SaveChangesAsync() + : context.SaveChanges(); + + context.ChangeTracker.Clear(); + + var query = context.Set() + .AsNoTracking() + .OrderBy(e => e.Id) + .Select(x => x.Settings.Where(s => s.Key != "Foo").ToList()); + + var collection = async + ? await query.FirstOrDefaultAsync() + : query.FirstOrDefault(); + + Assert.NotNull(collection); + Assert.Equal("Value1", collection[0].Key); + Assert.Equal("1", collection[0].Value); + Assert.Contains(("Value2", "9"), collection.Select(x => (x.Key, x.Value))); + + Assert.Equal(1, creatingInstanceCounts.Count); + Assert.Equal(2, creatingInstanceCounts[typeof(KeyValueSetting30244)]); + + Assert.Equal(1, createdInstanceCounts.Count); + Assert.Equal(2, createdInstanceCounts[typeof(KeyValueSetting30244)]); + + Assert.Equal(1, initializingInstanceCounts.Count); + Assert.Equal(2, initializingInstanceCounts[typeof(KeyValueSetting30244)]); + + Assert.Equal(1, initializedInstanceCounts.Count); + Assert.Equal(2, initializedInstanceCounts[typeof(KeyValueSetting30244)]); + } + } + [ConditionalTheory] [InlineData(false)] [InlineData(true)] diff --git a/test/EFCore.Specification.Tests/Query/ComplexTypeQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/ComplexTypeQueryTestBase.cs index 10e2334bb8d..29038901af2 100644 --- a/test/EFCore.Specification.Tests/Query/ComplexTypeQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/ComplexTypeQueryTestBase.cs @@ -160,6 +160,13 @@ public virtual Task Complex_type_equals_parameter(bool async) ss => ss.Set().Where(c => c.ShippingAddress == address)); } + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Complex_type_equals_null(bool async) + => AssertQuery( + async, + ss => ss.Set().Where(c => c.ShippingAddress == null)); + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Subquery_over_complex_type(bool async) diff --git a/test/EFCore.Specification.Tests/Query/PrimitiveCollectionsQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/PrimitiveCollectionsQueryTestBase.cs index f6bc23f3eec..d56c6bdd7a2 100644 --- a/test/EFCore.Specification.Tests/Query/PrimitiveCollectionsQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/PrimitiveCollectionsQueryTestBase.cs @@ -599,14 +599,15 @@ public virtual Task Column_collection_equality_parameter_collection(bool async) [ConditionalTheory] [MemberData(nameof(IsAsyncData))] - public virtual async Task Column_collection_Concat_parameter_collection_equality_inline_collection_not_supported(bool async) + public virtual async Task Column_collection_Concat_parameter_collection_equality_inline_collection(bool async) { var ints = new[] { 1, 10 }; - await AssertTranslationFailed( - () => AssertQuery( - async, - ss => ss.Set().Where(c => c.Ints.Concat(ints) == new[] { 1, 11, 111, 1, 10 }))); + await AssertQuery( + async, + ss => ss.Set().Where(c => c.Ints.Concat(ints) == new[] { 1, 11, 111, 1, 10 }), + ss => ss.Set().Where(c => c.Ints.Concat(ints).SequenceEqual(new[] { 1, 11, 111, 1, 10 })), + entryCount: 1); } [ConditionalTheory] @@ -620,15 +621,15 @@ public virtual Task Column_collection_equality_inline_collection(bool async) [ConditionalTheory] [MemberData(nameof(IsAsyncData))] - public virtual Task Column_collection_equality_inline_collection_with_parameters(bool async) + public virtual async Task Column_collection_equality_inline_collection_with_parameters(bool async) { var (i, j) = (1, 10); - return AssertTranslationFailed( - () => AssertQuery( - async, - ss => ss.Set().Where(c => c.Ints == new[] { i, j }), - entryCount: 1)); + await AssertQuery( + async, + ss => ss.Set().Where(c => c.Ints == new[] { i, j }), + ss => ss.Set().Where(c => c.Ints.SequenceEqual(new[] { i, j })), + entryCount: 1); } [ConditionalTheory] @@ -696,19 +697,13 @@ public virtual Task Parameter_collection_in_subquery_Union_column_collection_nes [ConditionalFact] public virtual void Parameter_collection_in_subquery_and_Convert_as_compiled_query() { - // The array indexing is translated as a subquery over e.g. OPENJSON with LIMIT/OFFSET. - // Since there's a CAST over that, the type mapping inference from the other side (p.String) doesn't propagate inside to the - // subquery. In this case, the CAST operand gets the default CLR type mapping, but that's object in this case. - // We should apply the default type mapping to the parameter, but need to figure out the exact rules when to do this. var query = EF.CompileQuery( (PrimitiveCollectionsContext context, object[] parameters) => context.Set().Where(p => p.String == (string)parameters[0])); using var context = Fixture.CreateContext(); - var exception = Assert.Throws(() => query(context, new[] { "foo" }).ToList()); - - Assert.Contains("in the SQL tree does not have a type mapping assigned", exception.Message); + _ = query(context, new[] { "foo" }).ToList(); } [ConditionalTheory] @@ -723,12 +718,7 @@ public virtual async Task Parameter_collection_in_subquery_Union_another_paramet var ints1 = new[] { 10, 111 }; var ints2 = new[] { 7, 42 }; - compiledQuery(context, ints1, ints2).ToList(); - - //var message = Assert.Throws( - // () => compiledQuery(context, ints1, ints2).ToList()).Message; - - //Assert.Equal(RelationalStrings.SetOperationsRequireAtLeastOneSideWithValidTypeMapping("Union"), message); + _ = compiledQuery(context, ints1, ints2).ToList(); } [ConditionalTheory] @@ -774,7 +764,7 @@ public virtual Task Project_collection_of_datetimes_filtered(bool async) [ConditionalTheory] [MemberData(nameof(IsAsyncData))] - public virtual Task Project_collection_of_ints_with_paging(bool async) + public virtual Task Project_collection_of_nullable_ints_with_paging(bool async) => AssertQuery( async, ss => ss.Set().OrderBy(x => x.Id).Select(x => x.NullableInts.Take(20).ToList()), @@ -783,7 +773,7 @@ public virtual Task Project_collection_of_ints_with_paging(bool async) [ConditionalTheory] [MemberData(nameof(IsAsyncData))] - public virtual Task Project_collection_of_ints_with_paging2(bool async) + public virtual Task Project_collection_of_nullable_ints_with_paging2(bool async) => AssertQuery( async, ss => ss.Set().OrderBy(x => x.Id).Select(x => x.NullableInts.OrderBy(x => x).Skip(1).ToList()), @@ -792,7 +782,7 @@ public virtual Task Project_collection_of_ints_with_paging2(bool async) [ConditionalTheory] [MemberData(nameof(IsAsyncData))] - public virtual Task Project_collection_of_ints_with_paging3(bool async) + public virtual Task Project_collection_of_nullable_ints_with_paging3(bool async) => AssertQuery( async, ss => ss.Set().OrderBy(x => x.Id).Select(x => x.NullableInts.Skip(2).ToList()), diff --git a/test/EFCore.SqlServer.FunctionalTests/EverythingIsBytesSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/EverythingIsBytesSqlServerTest.cs index 47dd77b4bb5..23ff5ea5552 100644 --- a/test/EFCore.SqlServer.FunctionalTests/EverythingIsBytesSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/EverythingIsBytesSqlServerTest.cs @@ -281,7 +281,7 @@ public SqlServerBytesTypeMappingSource( } protected override RelationalTypeMapping FindMapping(in RelationalTypeMappingInfo mappingInfo) - => FindRawMapping(mappingInfo)?.Clone(mappingInfo); + => FindRawMapping(mappingInfo)?.WithTypeMappingInfo(mappingInfo); private RelationalTypeMapping FindRawMapping(RelationalTypeMappingInfo mappingInfo) { diff --git a/test/EFCore.SqlServer.FunctionalTests/EverythingIsStringsSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/EverythingIsStringsSqlServerTest.cs index 9b0730c98f3..d86bf3b596f 100644 --- a/test/EFCore.SqlServer.FunctionalTests/EverythingIsStringsSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/EverythingIsStringsSqlServerTest.cs @@ -287,7 +287,7 @@ public SqlServerStringsTypeMappingSource( } protected override RelationalTypeMapping FindMapping(in RelationalTypeMappingInfo mappingInfo) - => FindRawMapping(mappingInfo)?.Clone(mappingInfo); + => FindRawMapping(mappingInfo)?.WithTypeMappingInfo(mappingInfo); private RelationalTypeMapping FindRawMapping(RelationalTypeMappingInfo mappingInfo) { diff --git a/test/EFCore.SqlServer.FunctionalTests/JsonTypesCustomMappingSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/JsonTypesCustomMappingSqlServerTest.cs new file mode 100644 index 00000000000..07a8a316bbd --- /dev/null +++ b/test/EFCore.SqlServer.FunctionalTests/JsonTypesCustomMappingSqlServerTest.cs @@ -0,0 +1,139 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable + +using Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal; + +namespace Microsoft.EntityFrameworkCore; + +public class JsonTypesCustomMappingSqlServerTest : JsonTypesSqlServerTestBase +{ + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + => base.OnConfiguring(optionsBuilder.ReplaceService()); + + private class TestSqlServerTypeMappingSource( + TypeMappingSourceDependencies dependencies, + RelationalTypeMappingSourceDependencies relationalDependencies) + : SqlServerTypeMappingSource(dependencies, relationalDependencies) + { + protected override RelationalTypeMapping? FindMapping(in RelationalTypeMappingInfo mappingInfo) + { + var mapping = base.FindMapping(in mappingInfo); + + if ((mapping == null + || (mappingInfo.CoreTypeMappingInfo.ElementTypeMapping != null + && mapping.ElementTypeMapping == null)) + && mappingInfo.ClrType != null + && mappingInfo.ClrType != typeof(string)) + { + var elementClrType = TryGetElementType(mappingInfo.ClrType, typeof(IEnumerable<>))!; + + mapping = CustomFindCollectionMapping( + mappingInfo, mappingInfo.ClrType, + null, + mappingInfo.CoreTypeMappingInfo.ElementTypeMapping ?? FindMapping(elementClrType)); + } + + return mapping; + } + + protected override RelationalTypeMapping? FindCollectionMapping( + RelationalTypeMappingInfo info, + Type modelType, + Type? providerType, + CoreTypeMapping? elementMapping) + => null; + + private RelationalTypeMapping? CustomFindCollectionMapping( + RelationalTypeMappingInfo info, + Type modelType, + Type? providerType, + CoreTypeMapping? elementMapping) + => TryFindJsonCollectionMapping( + info.CoreTypeMappingInfo, modelType, providerType, ref elementMapping, out var comparer, out var collectionReaderWriter) + ? (RelationalTypeMapping)FindMapping( + info.WithConverter( + // Note that the converter info is only used temporarily here and never creates an instance. + new ValueConverterInfo(modelType, typeof(string), _ => null!)))! + .WithComposedConverter( + (ValueConverter)Activator.CreateInstance( + typeof(CollectionToJsonStringConverter<>).MakeGenericType(TryGetElementType(modelType, typeof(IEnumerable<>))!), + collectionReaderWriter!)!, + comparer, + comparer, + elementMapping, + collectionReaderWriter) + : null; + + private static Type? TryGetElementType(Type type, Type interfaceOrBaseType) + { + if (type.IsGenericTypeDefinition) + { + return null; + } + + var types = GetGenericTypeImplementations(type, interfaceOrBaseType); + + Type? singleImplementation = null; + foreach (var implementation in types) + { + if (singleImplementation == null) + { + singleImplementation = implementation; + } + else + { + singleImplementation = null; + break; + } + } + + return singleImplementation?.GenericTypeArguments.FirstOrDefault(); + } + + private static IEnumerable GetGenericTypeImplementations(Type type, Type interfaceOrBaseType) + { + var typeInfo = type.GetTypeInfo(); + if (!typeInfo.IsGenericTypeDefinition) + { + var baseTypes = interfaceOrBaseType.GetTypeInfo().IsInterface + ? typeInfo.ImplementedInterfaces + : GetBaseTypes(type); + foreach (var baseType in baseTypes) + { + if (baseType.IsGenericType + && baseType.GetGenericTypeDefinition() == interfaceOrBaseType) + { + yield return baseType; + } + } + + if (type.IsGenericType + && type.GetGenericTypeDefinition() == interfaceOrBaseType) + { + yield return type; + } + } + } + + private static IEnumerable GetBaseTypes(Type type) + { + var currentType = type.BaseType; + + while (currentType != null) + { + yield return currentType; + + currentType = currentType.BaseType; + } + } + + private static Type UnwrapNullableType(Type type) + => Nullable.GetUnderlyingType(type) ?? type; + + private static bool IsNullableValueType(Type type) + => type.IsConstructedGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); + + } +} diff --git a/test/EFCore.SqlServer.FunctionalTests/JsonTypesSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/JsonTypesSqlServerTest.cs index 6966ab238d1..70f66a1c679 100644 --- a/test/EFCore.SqlServer.FunctionalTests/JsonTypesSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/JsonTypesSqlServerTest.cs @@ -5,61 +5,6 @@ namespace Microsoft.EntityFrameworkCore; -public class JsonTypesSqlServerTest : JsonTypesRelationalTestBase +public class JsonTypesSqlServerTest : JsonTypesSqlServerTestBase { - public override void Can_read_write_ulong_enum_JSON_values(EnumU64 value, string json) - { - if (value == EnumU64.Max) - { - json = """{"Prop":-1}"""; // Because ulong is converted to long on SQL Server - } - - base.Can_read_write_ulong_enum_JSON_values(value, json); - } - - public override void Can_read_write_nullable_ulong_enum_JSON_values(object? value, string json) - { - if (Equals(value, ulong.MaxValue)) - { - json = """{"Prop":-1}"""; // Because ulong is converted to long on SQL Server - } - - base.Can_read_write_nullable_ulong_enum_JSON_values(value, json); - } - - public override void Can_read_write_collection_of_ulong_enum_JSON_values() - => Can_read_and_write_JSON_value>(nameof(EnumU64CollectionType.EnumU64), - new List - { - EnumU64.Min, - EnumU64.Max, - EnumU64.Default, - EnumU64.One, - (EnumU64)8 - }, - """{"Prop":[0,-1,0,1,8]}""", // Because ulong is converted to long on SQL Server - mappedCollection: true); - - public override void Can_read_write_collection_of_nullable_ulong_enum_JSON_values() - => Can_read_and_write_JSON_value>(nameof(NullableEnumU64CollectionType.EnumU64), - new List - { - EnumU64.Min, - null, - EnumU64.Max, - EnumU64.Default, - EnumU64.One, - (EnumU64?)8 - }, - """{"Prop":[0,null,-1,0,1,8]}""", // Because ulong is converted to long on SQL Server - mappedCollection: true); - - public override void Can_read_write_collection_of_fixed_length_string_JSON_values(object? storeType) - => base.Can_read_write_collection_of_fixed_length_string_JSON_values("nchar(32)"); - - public override void Can_read_write_collection_of_ASCII_string_JSON_values(object? storeType) - => base.Can_read_write_collection_of_ASCII_string_JSON_values("varchar(max)"); - - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - => base.OnConfiguring(optionsBuilder.UseSqlServer(b => b.UseNetTopologySuite())); } diff --git a/test/EFCore.SqlServer.FunctionalTests/JsonTypesSqlServerTestBase.cs b/test/EFCore.SqlServer.FunctionalTests/JsonTypesSqlServerTestBase.cs new file mode 100644 index 00000000000..737c7520e27 --- /dev/null +++ b/test/EFCore.SqlServer.FunctionalTests/JsonTypesSqlServerTestBase.cs @@ -0,0 +1,65 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable + +namespace Microsoft.EntityFrameworkCore; + +public abstract class JsonTypesSqlServerTestBase : JsonTypesRelationalTestBase +{ + public override void Can_read_write_ulong_enum_JSON_values(EnumU64 value, string json) + { + if (value == EnumU64.Max) + { + json = """{"Prop":-1}"""; // Because ulong is converted to long on SQL Server + } + + base.Can_read_write_ulong_enum_JSON_values(value, json); + } + + public override void Can_read_write_nullable_ulong_enum_JSON_values(object? value, string json) + { + if (Equals(value, ulong.MaxValue)) + { + json = """{"Prop":-1}"""; // Because ulong is converted to long on SQL Server + } + + base.Can_read_write_nullable_ulong_enum_JSON_values(value, json); + } + + public override void Can_read_write_collection_of_ulong_enum_JSON_values() + => Can_read_and_write_JSON_value>(nameof(EnumU64CollectionType.EnumU64), + new List + { + EnumU64.Min, + EnumU64.Max, + EnumU64.Default, + EnumU64.One, + (EnumU64)8 + }, + """{"Prop":[0,-1,0,1,8]}""", // Because ulong is converted to long on SQL Server + mappedCollection: true); + + public override void Can_read_write_collection_of_nullable_ulong_enum_JSON_values() + => Can_read_and_write_JSON_value>(nameof(NullableEnumU64CollectionType.EnumU64), + new List + { + EnumU64.Min, + null, + EnumU64.Max, + EnumU64.Default, + EnumU64.One, + (EnumU64?)8 + }, + """{"Prop":[0,null,-1,0,1,8]}""", // Because ulong is converted to long on SQL Server + mappedCollection: true); + + public override void Can_read_write_collection_of_fixed_length_string_JSON_values(object? storeType) + => base.Can_read_write_collection_of_fixed_length_string_JSON_values("nchar(32)"); + + public override void Can_read_write_collection_of_ASCII_string_JSON_values(object? storeType) + => base.Can_read_write_collection_of_ASCII_string_JSON_values("varchar(max)"); + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + => base.OnConfiguring(optionsBuilder.UseSqlServer(b => b.UseNetTopologySuite())); +} diff --git a/test/EFCore.SqlServer.FunctionalTests/Migrations/MigrationsSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Migrations/MigrationsSqlServerTest.cs index fed07e2f342..e785ed26f61 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Migrations/MigrationsSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Migrations/MigrationsSqlServerTest.cs @@ -2225,6 +2225,105 @@ FROM [sys].[default_constraints] [d] """); } + [ConditionalFact] + public virtual async Task Create_index_unique_with_include_fillfactor_and_sortintempdb() + { + await Test( + builder => builder.Entity( + "People", e => + { + e.Property("Id"); + e.Property("FirstName"); + e.Property("LastName"); + e.Property("Name").IsRequired(); + }), + builder => { }, + builder => builder.Entity("People").HasIndex("Name") + .IsUnique() + .IncludeProperties("FirstName", "LastName") + .HasFillFactor(75) + .SortInTempDb(), + model => + { + var table = Assert.Single(model.Tables); + var index = Assert.Single(table.Indexes); + Assert.True(index.IsUnique); + Assert.Null(index.Filter); + Assert.Equal(1, index.Columns.Count); + Assert.Contains(table.Columns.Single(c => c.Name == "Name"), index.Columns); + var includedColumns = (IReadOnlyList?)index[SqlServerAnnotationNames.Include]; + Assert.Null(includedColumns); + Assert.Equal(75, index[SqlServerAnnotationNames.FillFactor]); + Assert.Null(index[SqlServerAnnotationNames.SortInTempDb]); + }); + + AssertSql( +""" +DECLARE @var0 sysname; +SELECT @var0 = [d].[name] +FROM [sys].[default_constraints] [d] +INNER JOIN [sys].[columns] [c] ON [d].[parent_column_id] = [c].[column_id] AND [d].[parent_object_id] = [c].[object_id] +WHERE ([d].[parent_object_id] = OBJECT_ID(N'[People]') AND [c].[name] = N'Name'); +IF @var0 IS NOT NULL EXEC(N'ALTER TABLE [People] DROP CONSTRAINT [' + @var0 + '];'); +ALTER TABLE [People] ALTER COLUMN [Name] nvarchar(450) NOT NULL; +""", +// +""" +CREATE UNIQUE INDEX [IX_People_Name] ON [People] ([Name]) INCLUDE ([FirstName], [LastName]) WITH (FILLFACTOR = 75, SORT_IN_TEMPDB = ON); +"""); + } + + [ConditionalTheory] + [InlineData(DataCompressionType.None, "NONE")] + [InlineData(DataCompressionType.Row, "ROW")] + [InlineData(DataCompressionType.Page, "PAGE")] + public virtual async Task Create_index_unique_with_include_sortintempdb_and_datacompression(DataCompressionType dataCompression, string dataCompressionSql) + { + await Test( + builder => builder.Entity( + "People", e => + { + e.Property("Id"); + e.Property("FirstName"); + e.Property("LastName"); + e.Property("Name").IsRequired(); + }), + builder => { }, + builder => builder.Entity("People").HasIndex("Name") + .IsUnique() + .IncludeProperties("FirstName", "LastName") + .SortInTempDb() + .UseDataCompression(dataCompression), + model => + { + var table = Assert.Single(model.Tables); + var index = Assert.Single(table.Indexes); + Assert.True(index.IsUnique); + Assert.Null(index.Filter); + Assert.Equal(1, index.Columns.Count); + Assert.Contains(table.Columns.Single(c => c.Name == "Name"), index.Columns); + var includedColumns = (IReadOnlyList?)index[SqlServerAnnotationNames.Include]; + Assert.Null(includedColumns); + Assert.Null(index[SqlServerAnnotationNames.SortInTempDb]); + Assert.Null(index[SqlServerAnnotationNames.DataCompression]); + }); + + AssertSql( +""" +DECLARE @var0 sysname; +SELECT @var0 = [d].[name] +FROM [sys].[default_constraints] [d] +INNER JOIN [sys].[columns] [c] ON [d].[parent_column_id] = [c].[column_id] AND [d].[parent_object_id] = [c].[object_id] +WHERE ([d].[parent_object_id] = OBJECT_ID(N'[People]') AND [c].[name] = N'Name'); +IF @var0 IS NOT NULL EXEC(N'ALTER TABLE [People] DROP CONSTRAINT [' + @var0 + '];'); +ALTER TABLE [People] ALTER COLUMN [Name] nvarchar(450) NOT NULL; +""", +// +$""" +CREATE UNIQUE INDEX [IX_People_Name] ON [People] ([Name]) INCLUDE ([FirstName], [LastName]) WITH (SORT_IN_TEMPDB = ON, DATA_COMPRESSION = {dataCompressionSql}); +"""); + } + [ConditionalFact] [SqlServerCondition(SqlServerCondition.SupportsMemoryOptimized)] public virtual async Task Create_index_memoryOptimized_unique_nullable() diff --git a/test/EFCore.SqlServer.FunctionalTests/Migrations/SqlServerMigrationsSqlGeneratorTest.cs b/test/EFCore.SqlServer.FunctionalTests/Migrations/SqlServerMigrationsSqlGeneratorTest.cs index e53c068bb06..0b400214e9b 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Migrations/SqlServerMigrationsSqlGeneratorTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Migrations/SqlServerMigrationsSqlGeneratorTest.cs @@ -29,6 +29,49 @@ public void CreateIndexOperation_unique_online() """); } + [ConditionalFact] + public void CreateIndexOperation_unique_sortintempdb() + { + Generate( + new CreateIndexOperation + { + Name = "IX_People_Name", + Table = "People", + Schema = "dbo", + Columns = new[] { "FirstName", "LastName" }, + IsUnique = true, + [SqlServerAnnotationNames.SortInTempDb] = true + }); + + AssertSql( +""" +CREATE UNIQUE INDEX [IX_People_Name] ON [dbo].[People] ([FirstName], [LastName]) WHERE [FirstName] IS NOT NULL AND [LastName] IS NOT NULL WITH (SORT_IN_TEMPDB = ON); +"""); + } + + [ConditionalTheory] + [InlineData(DataCompressionType.None, "NONE")] + [InlineData(DataCompressionType.Row, "ROW")] + [InlineData(DataCompressionType.Page, "PAGE")] + public void CreateIndexOperation_unique_datacompression(DataCompressionType dataCompression, string dataCompressionSql) + { + Generate( + new CreateIndexOperation + { + Name = "IX_People_Name", + Table = "People", + Schema = "dbo", + Columns = new[] { "FirstName", "LastName" }, + IsUnique = true, + [SqlServerAnnotationNames.DataCompression] = dataCompression + }); + + AssertSql( +$""" +CREATE UNIQUE INDEX [IX_People_Name] ON [dbo].[People] ([FirstName], [LastName]) WHERE [FirstName] IS NOT NULL AND [LastName] IS NOT NULL WITH (DATA_COMPRESSION = {dataCompressionSql}); +"""); + } + [ConditionalFact] public virtual void AddColumnOperation_identity_legacy() { diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexTypeQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexTypeQuerySqlServerTest.cs index 97dd6eea843..88ea463436e 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexTypeQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexTypeQuerySqlServerTest.cs @@ -240,6 +240,13 @@ FROM [Customer] AS [c] """); } + public override async Task Complex_type_equals_null(bool async) + { + await base.Complex_type_equals_null(async); + + AssertSql(); + } + public override async Task Subquery_over_complex_type(bool async) { await base.Subquery_over_complex_type(async); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/JsonQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/JsonQuerySqlServerTest.cs index 68164d0fe11..788944b8645 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/JsonQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/JsonQuerySqlServerTest.cs @@ -1012,10 +1012,10 @@ SELECT 1 FROM OPENJSON([j].[OwnedReferenceRoot], '$.OwnedCollectionBranch') WITH ( [Date] datetime2 '$.Date', [Enum] int '$.Enum', - [Enums] nvarchar(max) '$.Enums', + [Enums] nvarchar(max) '$.Enums' AS JSON, [Fraction] decimal(18,2) '$.Fraction', [NullableEnum] int '$.NullableEnum', - [NullableEnums] nvarchar(max) '$.NullableEnums', + [NullableEnums] nvarchar(max) '$.NullableEnums' AS JSON, [OwnedCollectionLeaf] nvarchar(max) '$.OwnedCollectionLeaf' AS JSON, [OwnedReferenceLeaf] nvarchar(max) '$.OwnedReferenceLeaf' AS JSON ) AS [o] @@ -1097,10 +1097,10 @@ SELECT COUNT(*) FROM OPENJSON([j].[OwnedReferenceRoot], '$.OwnedCollectionBranch') WITH ( [Date] datetime2 '$.Date', [Enum] int '$.Enum', - [Enums] nvarchar(max) '$.Enums', + [Enums] nvarchar(max) '$.Enums' AS JSON, [Fraction] decimal(18,2) '$.Fraction', [NullableEnum] int '$.NullableEnum', - [NullableEnums] nvarchar(max) '$.NullableEnums', + [NullableEnums] nvarchar(max) '$.NullableEnums' AS JSON, [OwnedCollectionLeaf] nvarchar(max) '$.OwnedCollectionLeaf' AS JSON, [OwnedReferenceLeaf] nvarchar(max) '$.OwnedReferenceLeaf' AS JSON ) AS [o] @@ -1121,9 +1121,9 @@ WHERE EXISTS ( SELECT 1 FROM OPENJSON([j].[OwnedCollectionRoot], '$') WITH ( [Name] nvarchar(max) '$.Name', - [Names] nvarchar(max) '$.Names', + [Names] nvarchar(max) '$.Names' AS JSON, [Number] int '$.Number', - [Numbers] nvarchar(max) '$.Numbers', + [Numbers] nvarchar(max) '$.Numbers' AS JSON, [OwnedCollectionBranch] nvarchar(max) '$.OwnedCollectionBranch' AS JSON, [OwnedReferenceBranch] nvarchar(max) '$.OwnedReferenceBranch' AS JSON ) AS [o] @@ -1132,10 +1132,10 @@ SELECT COUNT(*) FROM OPENJSON([o].[OwnedCollectionBranch], '$') WITH ( [Date] datetime2 '$.Date', [Enum] int '$.Enum', - [Enums] nvarchar(max) '$.Enums', + [Enums] nvarchar(max) '$.Enums' AS JSON, [Fraction] decimal(18,2) '$.Fraction', [NullableEnum] int '$.NullableEnum', - [NullableEnums] nvarchar(max) '$.NullableEnums', + [NullableEnums] nvarchar(max) '$.NullableEnums' AS JSON, [OwnedCollectionLeaf] nvarchar(max) '$.OwnedCollectionLeaf' AS JSON, [OwnedReferenceLeaf] nvarchar(max) '$.OwnedReferenceLeaf' AS JSON ) AS [o0]) = 2) @@ -1152,9 +1152,9 @@ public override async Task Json_collection_in_projection_with_composition_count( SELECT COUNT(*) FROM OPENJSON([j].[OwnedCollectionRoot], '$') WITH ( [Name] nvarchar(max) '$.Name', - [Names] nvarchar(max) '$.Names', + [Names] nvarchar(max) '$.Names' AS JSON, [Number] int '$.Number', - [Numbers] nvarchar(max) '$.Numbers', + [Numbers] nvarchar(max) '$.Numbers' AS JSON, [OwnedCollectionBranch] nvarchar(max) '$.OwnedCollectionBranch' AS JSON, [OwnedReferenceBranch] nvarchar(max) '$.OwnedReferenceBranch' AS JSON ) AS [o]) @@ -1214,14 +1214,73 @@ public override async Task Json_collection_filter_in_projection(bool async) { await base.Json_collection_filter_in_projection(async); - AssertSql(""); + AssertSql( +""" +SELECT [j].[Id], [t].[Id], [t].[Name], [t].[Names], [t].[Number], [t].[Numbers], [t].[c], [t].[c0], [t].[key] +FROM [JsonEntitiesBasic] AS [j] +OUTER APPLY ( + SELECT [j].[Id], JSON_VALUE([o].[value], '$.Name') AS [Name], JSON_QUERY([o].[value], '$.Names') AS [Names], CAST(JSON_VALUE([o].[value], '$.Number') AS int) AS [Number], JSON_QUERY([o].[value], '$.Numbers') AS [Numbers], JSON_QUERY([o].[value], '$.OwnedCollectionBranch') AS [c], JSON_QUERY([o].[value], '$.OwnedReferenceBranch') AS [c0], [o].[key], CAST([o].[key] AS int) AS [c1] + FROM OPENJSON([j].[OwnedCollectionRoot], '$') AS [o] + WHERE JSON_VALUE([o].[value], '$.Name') <> N'Foo' OR JSON_VALUE([o].[value], '$.Name') IS NULL +) AS [t] +ORDER BY [j].[Id], [t].[c1] +"""); + } + + public override async Task Json_nested_collection_filter_in_projection(bool async) + { + await base.Json_nested_collection_filter_in_projection(async); + + AssertSql( +""" +SELECT [j].[Id], [t0].[key], [t0].[Id], [t0].[Date], [t0].[Enum], [t0].[Enums], [t0].[Fraction], [t0].[NullableEnum], [t0].[NullableEnums], [t0].[c], [t0].[c0], [t0].[key0] +FROM [JsonEntitiesBasic] AS [j] +OUTER APPLY ( + SELECT [o].[key], [t].[Id], [t].[Date], [t].[Enum], [t].[Enums], [t].[Fraction], [t].[NullableEnum], [t].[NullableEnums], [t].[c], [t].[c0], [t].[key] AS [key0], CAST([o].[key] AS int) AS [c1], [t].[c1] AS [c10] + FROM OPENJSON([j].[OwnedCollectionRoot], '$') AS [o] + OUTER APPLY ( + SELECT [j].[Id], CAST(JSON_VALUE([o0].[value], '$.Date') AS datetime2) AS [Date], CAST(JSON_VALUE([o0].[value], '$.Enum') AS int) AS [Enum], JSON_QUERY([o0].[value], '$.Enums') AS [Enums], CAST(JSON_VALUE([o0].[value], '$.Fraction') AS decimal(18,2)) AS [Fraction], CAST(JSON_VALUE([o0].[value], '$.NullableEnum') AS int) AS [NullableEnum], JSON_QUERY([o0].[value], '$.NullableEnums') AS [NullableEnums], JSON_QUERY([o0].[value], '$.OwnedCollectionLeaf') AS [c], JSON_QUERY([o0].[value], '$.OwnedReferenceLeaf') AS [c0], [o0].[key], CAST([o0].[key] AS int) AS [c1] + FROM OPENJSON(JSON_QUERY([o].[value], '$.OwnedCollectionBranch'), '$') AS [o0] + WHERE CAST(JSON_VALUE([o0].[value], '$.Date') AS datetime2) <> '2000-01-01T00:00:00.0000000' OR CAST(JSON_VALUE([o0].[value], '$.Date') AS datetime2) IS NULL + ) AS [t] +) AS [t0] +ORDER BY [j].[Id], [t0].[c1], [t0].[key], [t0].[c10] +"""); + } + + public override async Task Json_nested_collection_anonymous_projection_in_projection(bool async) + { + await base.Json_nested_collection_anonymous_projection_in_projection(async); + + AssertSql( +""" +SELECT [j].[Id], [t].[key], [t].[c], [t].[c0], [t].[c1], [t].[c2], [t].[c3], [t].[Id], [t].[c4], [t].[key0] +FROM [JsonEntitiesBasic] AS [j] +OUTER APPLY ( + SELECT [o].[key], CAST(JSON_VALUE([o0].[value], '$.Date') AS datetime2) AS [c], CAST(JSON_VALUE([o0].[value], '$.Enum') AS int) AS [c0], JSON_QUERY([o0].[value], '$.Enums') AS [c1], CAST(JSON_VALUE([o0].[value], '$.Fraction') AS decimal(18,2)) AS [c2], JSON_QUERY([o0].[value], '$.OwnedReferenceLeaf') AS [c3], [j].[Id], JSON_QUERY([o0].[value], '$.OwnedCollectionLeaf') AS [c4], [o0].[key] AS [key0], CAST([o].[key] AS int) AS [c5], CAST([o0].[key] AS int) AS [c6] + FROM OPENJSON([j].[OwnedCollectionRoot], '$') AS [o] + OUTER APPLY OPENJSON(JSON_QUERY([o].[value], '$.OwnedCollectionBranch'), '$') AS [o0] +) AS [t] +ORDER BY [j].[Id], [t].[c5], [t].[key], [t].[c6] +"""); } public override async Task Json_collection_skip_take_in_projection(bool async) { await base.Json_collection_skip_take_in_projection(async); - AssertSql(""); + AssertSql( +""" +SELECT [j].[Id], [t].[Id], [t].[Name], [t].[Names], [t].[Number], [t].[Numbers], [t].[c], [t].[c0], [t].[key] +FROM [JsonEntitiesBasic] AS [j] +OUTER APPLY ( + SELECT [j].[Id], JSON_VALUE([o].[value], '$.Name') AS [Name], JSON_QUERY([o].[value], '$.Names') AS [Names], CAST(JSON_VALUE([o].[value], '$.Number') AS int) AS [Number], JSON_QUERY([o].[value], '$.Numbers') AS [Numbers], JSON_QUERY([o].[value], '$.OwnedCollectionBranch') AS [c], JSON_QUERY([o].[value], '$.OwnedReferenceBranch') AS [c0], [o].[key], JSON_VALUE([o].[value], '$.Name') AS [c1] + FROM OPENJSON([j].[OwnedCollectionRoot], '$') AS [o] + ORDER BY JSON_VALUE([o].[value], '$.Name') + OFFSET 1 ROWS FETCH NEXT 5 ROWS ONLY +) AS [t] +ORDER BY [j].[Id], [t].[c1] +"""); } public override async Task Json_collection_skip_take_in_projection_project_into_anonymous_type(bool async) @@ -1264,6 +1323,29 @@ public override async Task Json_collection_distinct_in_projection(bool async) { await base.Json_collection_distinct_in_projection(async); + AssertSql( +""" +SELECT [j].[Id], [t].[Id], [t].[Name], [t].[Names], [t].[Number], [t].[Numbers], [t].[c], [t].[c0] +FROM [JsonEntitiesBasic] AS [j] +OUTER APPLY ( + SELECT DISTINCT [j].[Id], [o].[Name], [o].[Names], [o].[Number], [o].[Numbers], [o].[OwnedCollectionBranch] AS [c], [o].[OwnedReferenceBranch] AS [c0] + FROM OPENJSON([j].[OwnedCollectionRoot], '$') WITH ( + [Name] nvarchar(max) '$.Name', + [Names] nvarchar(max) '$.Names' AS JSON, + [Number] int '$.Number', + [Numbers] nvarchar(max) '$.Numbers' AS JSON, + [OwnedCollectionBranch] nvarchar(max) '$.OwnedCollectionBranch' AS JSON, + [OwnedReferenceBranch] nvarchar(max) '$.OwnedReferenceBranch' AS JSON + ) AS [o] +) AS [t] +ORDER BY [j].[Id], [t].[Name], [t].[Names], [t].[Number] +"""); + } + + public override async Task Json_collection_anonymous_projection_distinct_in_projection(bool async) + { + await base.Json_collection_anonymous_projection_distinct_in_projection(async); + AssertSql(""); } @@ -1271,21 +1353,138 @@ public override async Task Json_collection_leaf_filter_in_projection(bool async) { await base.Json_collection_leaf_filter_in_projection(async); - AssertSql(""); + AssertSql( +""" +SELECT [j].[Id], [t].[Id], [t].[SomethingSomething], [t].[key] +FROM [JsonEntitiesBasic] AS [j] +OUTER APPLY ( + SELECT [j].[Id], JSON_VALUE([o].[value], '$.SomethingSomething') AS [SomethingSomething], [o].[key], CAST([o].[key] AS int) AS [c] + FROM OPENJSON([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch.OwnedCollectionLeaf') AS [o] + WHERE JSON_VALUE([o].[value], '$.SomethingSomething') <> N'Baz' OR JSON_VALUE([o].[value], '$.SomethingSomething') IS NULL +) AS [t] +ORDER BY [j].[Id], [t].[c] +"""); + } + + public override async Task Json_multiple_collection_projections(bool async) + { + await base.Json_multiple_collection_projections(async); + + AssertSql( +""" +SELECT [j].[Id], [t].[Id], [t].[SomethingSomething], [t].[key], [t0].[Id], [t0].[Name], [t0].[Names], [t0].[Number], [t0].[Numbers], [t0].[c], [t0].[c0], [t1].[key], [t1].[Id], [t1].[Date], [t1].[Enum], [t1].[Enums], [t1].[Fraction], [t1].[NullableEnum], [t1].[NullableEnums], [t1].[c], [t1].[c0], [t1].[key0], [j0].[Id], [j0].[Name], [j0].[ParentId] +FROM [JsonEntitiesBasic] AS [j] +OUTER APPLY ( + SELECT [j].[Id], JSON_VALUE([o].[value], '$.SomethingSomething') AS [SomethingSomething], [o].[key], CAST([o].[key] AS int) AS [c] + FROM OPENJSON([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch.OwnedCollectionLeaf') AS [o] + WHERE JSON_VALUE([o].[value], '$.SomethingSomething') <> N'Baz' OR JSON_VALUE([o].[value], '$.SomethingSomething') IS NULL +) AS [t] +OUTER APPLY ( + SELECT DISTINCT [j].[Id], [o0].[Name], [o0].[Names], [o0].[Number], [o0].[Numbers], [o0].[OwnedCollectionBranch] AS [c], [o0].[OwnedReferenceBranch] AS [c0] + FROM OPENJSON([j].[OwnedCollectionRoot], '$') WITH ( + [Name] nvarchar(max) '$.Name', + [Names] nvarchar(max) '$.Names' AS JSON, + [Number] int '$.Number', + [Numbers] nvarchar(max) '$.Numbers' AS JSON, + [OwnedCollectionBranch] nvarchar(max) '$.OwnedCollectionBranch' AS JSON, + [OwnedReferenceBranch] nvarchar(max) '$.OwnedReferenceBranch' AS JSON + ) AS [o0] +) AS [t0] +OUTER APPLY ( + SELECT [o1].[key], [t2].[Id], [t2].[Date], [t2].[Enum], [t2].[Enums], [t2].[Fraction], [t2].[NullableEnum], [t2].[NullableEnums], [t2].[c], [t2].[c0], [t2].[key] AS [key0], CAST([o1].[key] AS int) AS [c1], [t2].[c1] AS [c10] + FROM OPENJSON([j].[OwnedCollectionRoot], '$') AS [o1] + OUTER APPLY ( + SELECT [j].[Id], CAST(JSON_VALUE([o2].[value], '$.Date') AS datetime2) AS [Date], CAST(JSON_VALUE([o2].[value], '$.Enum') AS int) AS [Enum], JSON_QUERY([o2].[value], '$.Enums') AS [Enums], CAST(JSON_VALUE([o2].[value], '$.Fraction') AS decimal(18,2)) AS [Fraction], CAST(JSON_VALUE([o2].[value], '$.NullableEnum') AS int) AS [NullableEnum], JSON_QUERY([o2].[value], '$.NullableEnums') AS [NullableEnums], JSON_QUERY([o2].[value], '$.OwnedCollectionLeaf') AS [c], JSON_QUERY([o2].[value], '$.OwnedReferenceLeaf') AS [c0], [o2].[key], CAST([o2].[key] AS int) AS [c1] + FROM OPENJSON(JSON_QUERY([o1].[value], '$.OwnedCollectionBranch'), '$') AS [o2] + WHERE CAST(JSON_VALUE([o2].[value], '$.Date') AS datetime2) <> '2000-01-01T00:00:00.0000000' OR CAST(JSON_VALUE([o2].[value], '$.Date') AS datetime2) IS NULL + ) AS [t2] +) AS [t1] +LEFT JOIN [JsonEntitiesBasicForCollection] AS [j0] ON [j].[Id] = [j0].[ParentId] +ORDER BY [j].[Id], [t].[c], [t].[key], [t0].[Name], [t0].[Names], [t0].[Number], [t0].[Numbers], [t1].[c1], [t1].[key], [t1].[c10], [t1].[key0] +"""); + } + + public override async Task Json_branch_collection_distinct_and_other_collection(bool async) + { + await base.Json_branch_collection_distinct_and_other_collection(async); + + AssertSql( +""" +SELECT [j].[Id], [t].[Id], [t].[Date], [t].[Enum], [t].[Enums], [t].[Fraction], [t].[NullableEnum], [t].[NullableEnums], [t].[c], [t].[c0], [j0].[Id], [j0].[Name], [j0].[ParentId] +FROM [JsonEntitiesBasic] AS [j] +OUTER APPLY ( + SELECT DISTINCT [j].[Id], [o].[Date], [o].[Enum], [o].[Enums], [o].[Fraction], [o].[NullableEnum], [o].[NullableEnums], [o].[OwnedCollectionLeaf] AS [c], [o].[OwnedReferenceLeaf] AS [c0] + FROM OPENJSON([j].[OwnedReferenceRoot], '$.OwnedCollectionBranch') WITH ( + [Date] datetime2 '$.Date', + [Enum] int '$.Enum', + [Enums] nvarchar(max) '$.Enums' AS JSON, + [Fraction] decimal(18,2) '$.Fraction', + [NullableEnum] int '$.NullableEnum', + [NullableEnums] nvarchar(max) '$.NullableEnums' AS JSON, + [OwnedCollectionLeaf] nvarchar(max) '$.OwnedCollectionLeaf' AS JSON, + [OwnedReferenceLeaf] nvarchar(max) '$.OwnedReferenceLeaf' AS JSON + ) AS [o] +) AS [t] +LEFT JOIN [JsonEntitiesBasicForCollection] AS [j0] ON [j].[Id] = [j0].[ParentId] +ORDER BY [j].[Id], [t].[Date], [t].[Enum], [t].[Enums], [t].[Fraction], [t].[NullableEnum], [t].[NullableEnums] +"""); + } + + public override async Task Json_leaf_collection_distinct_and_other_collection(bool async) + { + await base.Json_leaf_collection_distinct_and_other_collection(async); + + AssertSql( +""" +SELECT [j].[Id], [t].[Id], [t].[SomethingSomething], [j0].[Id], [j0].[Name], [j0].[ParentId] +FROM [JsonEntitiesBasic] AS [j] +OUTER APPLY ( + SELECT DISTINCT [j].[Id], [o].[SomethingSomething] + FROM OPENJSON([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch.OwnedCollectionLeaf') WITH ([SomethingSomething] nvarchar(max) '$.SomethingSomething') AS [o] +) AS [t] +LEFT JOIN [JsonEntitiesBasicForCollection] AS [j0] ON [j].[Id] = [j0].[ParentId] +ORDER BY [j].[Id], [t].[SomethingSomething] +"""); } public override async Task Json_collection_SelectMany(bool async) { await base.Json_collection_SelectMany(async); - AssertSql(""); + AssertSql( +""" +SELECT [j].[Id], [o].[Name], [o].[Names], [o].[Number], [o].[Numbers], [o].[OwnedCollectionBranch], [o].[OwnedReferenceBranch] +FROM [JsonEntitiesBasic] AS [j] +CROSS APPLY OPENJSON([j].[OwnedCollectionRoot], '$') WITH ( + [Name] nvarchar(max) '$.Name', + [Names] nvarchar(max) '$.Names' AS JSON, + [Number] int '$.Number', + [Numbers] nvarchar(max) '$.Numbers' AS JSON, + [OwnedCollectionBranch] nvarchar(max) '$.OwnedCollectionBranch' AS JSON, + [OwnedReferenceBranch] nvarchar(max) '$.OwnedReferenceBranch' AS JSON +) AS [o] +"""); } public override async Task Json_nested_collection_SelectMany(bool async) { await base.Json_nested_collection_SelectMany(async); - AssertSql(""); + AssertSql( +""" +SELECT [j].[Id], [o].[Date], [o].[Enum], [o].[Enums], [o].[Fraction], [o].[NullableEnum], [o].[NullableEnums], [o].[OwnedCollectionLeaf], [o].[OwnedReferenceLeaf] +FROM [JsonEntitiesBasic] AS [j] +CROSS APPLY OPENJSON([j].[OwnedReferenceRoot], '$.OwnedCollectionBranch') WITH ( + [Date] datetime2 '$.Date', + [Enum] int '$.Enum', + [Enums] nvarchar(max) '$.Enums' AS JSON, + [Fraction] decimal(18,2) '$.Fraction', + [NullableEnum] int '$.NullableEnum', + [NullableEnums] nvarchar(max) '$.NullableEnums' AS JSON, + [OwnedCollectionLeaf] nvarchar(max) '$.OwnedCollectionLeaf' AS JSON, + [OwnedReferenceLeaf] nvarchar(max) '$.OwnedReferenceLeaf' AS JSON +) AS [o] +"""); } public override async Task Json_collection_of_primitives_SelectMany(bool async) @@ -2004,7 +2203,7 @@ public override async Task Json_all_types_projection_from_owned_entity_reference await base.Json_all_types_projection_from_owned_entity_reference(async); AssertSql( - """ +""" SELECT [j].[Reference], [j].[Id] FROM [JsonEntitiesAllTypes] AS [j] """); @@ -2016,7 +2215,7 @@ public override async Task Json_all_types_projection_individual_properties(bool AssertSql( """ -SELECT JSON_VALUE([j].[Reference], '$.TestDefaultString') AS [TestDefaultString], JSON_VALUE([j].[Reference], '$.TestMaxLengthString') AS [TestMaxLengthString], CAST(JSON_VALUE([j].[Reference], '$.TestBoolean') AS bit) AS [TestBoolean], CAST(JSON_VALUE([j].[Reference], '$.TestByte') AS tinyint) AS [TestByte], JSON_VALUE([j].[Reference], '$.TestCharacter') AS [TestCharacter], CAST(JSON_VALUE([j].[Reference], '$.TestDateTime') AS datetime2) AS [TestDateTime], CAST(JSON_VALUE([j].[Reference], '$.TestDateTimeOffset') AS datetimeoffset) AS [TestDateTimeOffset], CAST(JSON_VALUE([j].[Reference], '$.TestDecimal') AS decimal(18,3)) AS [TestDecimal], CAST(JSON_VALUE([j].[Reference], '$.TestDouble') AS float) AS [TestDouble], CAST(JSON_VALUE([j].[Reference], '$.TestGuid') AS uniqueidentifier) AS [TestGuid], CAST(JSON_VALUE([j].[Reference], '$.TestInt16') AS smallint) AS [TestInt16], CAST(JSON_VALUE([j].[Reference], '$.TestInt32') AS int) AS [TestInt32], CAST(JSON_VALUE([j].[Reference], '$.TestInt64') AS bigint) AS [TestInt64], CAST(JSON_VALUE([j].[Reference], '$.TestSignedByte') AS smallint) AS [TestSignedByte], CAST(JSON_VALUE([j].[Reference], '$.TestSingle') AS real) AS [TestSingle], CAST(JSON_VALUE([j].[Reference], '$.TestTimeSpan') AS time) AS [TestTimeSpan], CAST(JSON_VALUE([j].[Reference], '$.TestUnsignedInt16') AS int) AS [TestUnsignedInt16], CAST(JSON_VALUE([j].[Reference], '$.TestUnsignedInt32') AS bigint) AS [TestUnsignedInt32], CAST(JSON_VALUE([j].[Reference], '$.TestUnsignedInt64') AS decimal(20,0)) AS [TestUnsignedInt64], CAST(JSON_VALUE([j].[Reference], '$.TestEnum') AS int) AS [TestEnum], CAST(JSON_VALUE([j].[Reference], '$.TestEnumWithIntConverter') AS int) AS [TestEnumWithIntConverter], CAST(JSON_VALUE([j].[Reference], '$.TestNullableEnum') AS int) AS [TestNullableEnum], CAST(JSON_VALUE([j].[Reference], '$.TestNullableEnumWithIntConverter') AS int) AS [TestNullableEnumWithIntConverter], JSON_VALUE([j].[Reference], '$.TestNullableEnumWithConverterThatHandlesNulls') AS [TestNullableEnumWithConverterThatHandlesNulls] +SELECT JSON_VALUE([j].[Reference], '$.TestDefaultString') AS [TestDefaultString], JSON_VALUE([j].[Reference], '$.TestMaxLengthString') AS [TestMaxLengthString], CAST(JSON_VALUE([j].[Reference], '$.TestBoolean') AS bit) AS [TestBoolean], CAST(JSON_VALUE([j].[Reference], '$.TestByte') AS tinyint) AS [TestByte], JSON_VALUE([j].[Reference], '$.TestCharacter') AS [TestCharacter], CAST(JSON_VALUE([j].[Reference], '$.TestDateTime') AS datetime2) AS [TestDateTime], CAST(JSON_VALUE([j].[Reference], '$.TestDateTimeOffset') AS datetimeoffset) AS [TestDateTimeOffset], CAST(JSON_VALUE([j].[Reference], '$.TestDecimal') AS decimal(18,3)) AS [TestDecimal], CAST(JSON_VALUE([j].[Reference], '$.TestDouble') AS float) AS [TestDouble], CAST(JSON_VALUE([j].[Reference], '$.TestGuid') AS uniqueidentifier) AS [TestGuid], CAST(JSON_VALUE([j].[Reference], '$.TestInt16') AS smallint) AS [TestInt16], CAST(JSON_VALUE([j].[Reference], '$.TestInt32') AS int) AS [TestInt32], CAST(JSON_VALUE([j].[Reference], '$.TestInt64') AS bigint) AS [TestInt64], CAST(JSON_VALUE([j].[Reference], '$.TestSignedByte') AS smallint) AS [TestSignedByte], CAST(JSON_VALUE([j].[Reference], '$.TestSingle') AS real) AS [TestSingle], CAST(JSON_VALUE([j].[Reference], '$.TestTimeSpan') AS time) AS [TestTimeSpan], CAST(JSON_VALUE([j].[Reference], '$.TestDateOnly') AS date) AS [TestDateOnly], CAST(JSON_VALUE([j].[Reference], '$.TestTimeOnly') AS time) AS [TestTimeOnly], CAST(JSON_VALUE([j].[Reference], '$.TestUnsignedInt16') AS int) AS [TestUnsignedInt16], CAST(JSON_VALUE([j].[Reference], '$.TestUnsignedInt32') AS bigint) AS [TestUnsignedInt32], CAST(JSON_VALUE([j].[Reference], '$.TestUnsignedInt64') AS decimal(20,0)) AS [TestUnsignedInt64], CAST(JSON_VALUE([j].[Reference], '$.TestEnum') AS int) AS [TestEnum], CAST(JSON_VALUE([j].[Reference], '$.TestEnumWithIntConverter') AS int) AS [TestEnumWithIntConverter], CAST(JSON_VALUE([j].[Reference], '$.TestNullableEnum') AS int) AS [TestNullableEnum], CAST(JSON_VALUE([j].[Reference], '$.TestNullableEnumWithIntConverter') AS int) AS [TestNullableEnumWithIntConverter], JSON_VALUE([j].[Reference], '$.TestNullableEnumWithConverterThatHandlesNulls') AS [TestNullableEnumWithConverterThatHandlesNulls] FROM [JsonEntitiesAllTypes] AS [j] """); } @@ -2383,6 +2582,30 @@ WHERE CAST(JSON_VALUE([j].[Reference], '$.TestTimeSpan') AS time) <> '03:02:00' """); } + public override async Task Json_predicate_on_dateonly(bool async) + { + await base.Json_predicate_on_dateonly(async); + + AssertSql( +""" +SELECT [j].[Id], [j].[TestBooleanCollection], [j].[TestByteCollection], [j].[TestCharacterCollection], [j].[TestDateTimeCollection], [j].[TestDateTimeOffsetCollection], [j].[TestDecimalCollection], [j].[TestDefaultStringCollection], [j].[TestDoubleCollection], [j].[TestEnumCollection], [j].[TestEnumWithIntConverterCollection], [j].[TestGuidCollection], [j].[TestInt16Collection], [j].[TestInt32Collection], [j].[TestInt64Collection], [j].[TestMaxLengthStringCollection], [j].[TestNullableEnumCollection], [j].[TestNullableEnumWithConverterThatHandlesNullsCollection], [j].[TestNullableEnumWithIntConverterCollection], [j].[TestNullableInt32Collection], [j].[TestSignedByteCollection], [j].[TestSingleCollection], [j].[TestTimeSpanCollection], [j].[TestUnsignedInt16Collection], [j].[TestUnsignedInt32Collection], [j].[TestUnsignedInt64Collection], [j].[Collection], [j].[Reference] +FROM [JsonEntitiesAllTypes] AS [j] +WHERE CAST(JSON_VALUE([j].[Reference], '$.TestDateOnly') AS date) <> '0003-02-01' OR CAST(JSON_VALUE([j].[Reference], '$.TestDateOnly') AS date) IS NULL +"""); + } + + public override async Task Json_predicate_on_timeonly(bool async) + { + await base.Json_predicate_on_timeonly(async); + + AssertSql( +""" +SELECT [j].[Id], [j].[TestBooleanCollection], [j].[TestByteCollection], [j].[TestCharacterCollection], [j].[TestDateTimeCollection], [j].[TestDateTimeOffsetCollection], [j].[TestDecimalCollection], [j].[TestDefaultStringCollection], [j].[TestDoubleCollection], [j].[TestEnumCollection], [j].[TestEnumWithIntConverterCollection], [j].[TestGuidCollection], [j].[TestInt16Collection], [j].[TestInt32Collection], [j].[TestInt64Collection], [j].[TestMaxLengthStringCollection], [j].[TestNullableEnumCollection], [j].[TestNullableEnumWithConverterThatHandlesNullsCollection], [j].[TestNullableEnumWithIntConverterCollection], [j].[TestNullableInt32Collection], [j].[TestSignedByteCollection], [j].[TestSingleCollection], [j].[TestTimeSpanCollection], [j].[TestUnsignedInt16Collection], [j].[TestUnsignedInt32Collection], [j].[TestUnsignedInt64Collection], [j].[Collection], [j].[Reference] +FROM [JsonEntitiesAllTypes] AS [j] +WHERE CAST(JSON_VALUE([j].[Reference], '$.TestTimeOnly') AS time) <> '03:02:00' OR CAST(JSON_VALUE([j].[Reference], '$.TestTimeOnly') AS time) IS NULL +"""); + } + public override async Task Json_predicate_on_unisgnedint16(bool async) { await base.Json_predicate_on_unisgnedint16(async); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/OwnedEntityQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/OwnedEntityQuerySqlServerTest.cs index 804c9ea3db0..12c52e09b30 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/OwnedEntityQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/OwnedEntityQuerySqlServerTest.cs @@ -166,7 +166,37 @@ public override async Task Owned_entity_with_all_null_properties_entity_equality """ SELECT [r].[Id], [r].[Rot_ApartmentNo], [r].[Rot_ServiceType] FROM [RotRutCases] AS [r] -WHERE [r].[Rot_ApartmentNo] IS NOT NULL AND [r].[Rot_ServiceType] IS NOT NULL +WHERE [r].[Rot_ApartmentNo] IS NOT NULL OR [r].[Rot_ServiceType] IS NOT NULL +"""); + } + + public override async Task Owned_entity_with_all_null_properties_in_compared_to_null_in_conditional_projection(bool async) + { + await base.Owned_entity_with_all_null_properties_in_compared_to_null_in_conditional_projection(async); + + AssertSql( +""" +SELECT CASE + WHEN [r].[Rot_ApartmentNo] IS NULL AND [r].[Rot_ServiceType] IS NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END, [r].[Rot_ApartmentNo], [r].[Rot_ServiceType] +FROM [RotRutCases] AS [r] +ORDER BY [r].[Id] +"""); + } + + public override async Task Owned_entity_with_all_null_properties_in_compared_to_non_null_in_conditional_projection(bool async) + { + await base.Owned_entity_with_all_null_properties_in_compared_to_non_null_in_conditional_projection(async); + + AssertSql( +""" +SELECT CASE + WHEN [r].[Rot_ApartmentNo] IS NOT NULL OR [r].[Rot_ServiceType] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END, [r].[Rot_ApartmentNo], [r].[Rot_ServiceType] +FROM [RotRutCases] AS [r] +ORDER BY [r].[Id] """); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQueryOldSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQueryOldSqlServerTest.cs index f3dce36adad..1f2c600627b 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQueryOldSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQueryOldSqlServerTest.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.EntityFrameworkCore.SqlServer.Internal; +using Xunit.Sdk; namespace Microsoft.EntityFrameworkCore.Query; @@ -10,7 +11,7 @@ namespace Microsoft.EntityFrameworkCore.Query; /// This exercises the older translation paths for e.g. Contains, to make sure things work for providers with no queryable constant/ /// parameter support. /// -public class PrimitiveCollectionsQueryOldSqlServerTest : PrimitiveCollectionsQueryTestBase< +public class PrimitiveCollectionsQueryOldSqlServerTest : PrimitiveCollectionsQueryRelationalTestBase< PrimitiveCollectionsQueryOldSqlServerTest.PrimitiveCollectionsQueryOldSqlServerFixture> { public PrimitiveCollectionsQueryOldSqlServerTest(PrimitiveCollectionsQueryOldSqlServerFixture fixture, ITestOutputHelper testOutputHelper) @@ -464,9 +465,9 @@ FROM [PrimitiveCollectionsEntity] AS [p] """); } - public override async Task Column_collection_Concat_parameter_collection_equality_inline_collection_not_supported(bool async) + public override async Task Column_collection_Concat_parameter_collection_equality_inline_collection(bool async) { - await base.Column_collection_Concat_parameter_collection_equality_inline_collection_not_supported(async); + await base.Column_collection_Concat_parameter_collection_equality_inline_collection(async); AssertSql(); } @@ -510,8 +511,9 @@ public override Task Parameter_collection_in_subquery_Count_as_compiled_query(bo public override Task Column_collection_in_subquery_Union_parameter_collection(bool async) => AssertCompatibilityLevelTooLow(() => base.Column_collection_in_subquery_Union_parameter_collection(async)); + // Base implementation asserts that a different exception is thrown public override Task Parameter_collection_in_subquery_Union_another_parameter_collection_as_compiled_query(bool async) - => AssertCompatibilityLevelTooLow(() => base.Parameter_collection_in_subquery_Union_another_parameter_collection_as_compiled_query(async)); + => Assert.ThrowsAsync(() => base.Parameter_collection_in_subquery_Union_another_parameter_collection_as_compiled_query(async)); public override async Task Project_collection_of_ints_simple(bool async) { @@ -533,9 +535,9 @@ public override Task Project_collection_of_datetimes_filtered(bool async) // we don't propagate error details from projection => AssertTranslationFailed(() => base.Project_collection_of_datetimes_filtered(async)); - public override async Task Project_collection_of_ints_with_paging(bool async) + public override async Task Project_collection_of_nullable_ints_with_paging(bool async) { - await base.Project_collection_of_ints_with_paging(async); + await base.Project_collection_of_nullable_ints_with_paging(async); // client eval AssertSql( @@ -546,13 +548,13 @@ ORDER BY [p].[Id] """); } - public override Task Project_collection_of_ints_with_paging2(bool async) + public override Task Project_collection_of_nullable_ints_with_paging2(bool async) // we don't propagate error details from projection - => AssertTranslationFailed(() => base.Project_collection_of_ints_with_paging2(async)); + => AssertTranslationFailed(() => base.Project_collection_of_nullable_ints_with_paging2(async)); - public override async Task Project_collection_of_ints_with_paging3(bool async) + public override async Task Project_collection_of_nullable_ints_with_paging3(bool async) { - await base.Project_collection_of_ints_with_paging3(async); + await base.Project_collection_of_nullable_ints_with_paging3(async); // client eval AssertSql( diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerTest.cs index e2ddaa9a82f..84e59a15cbc 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerTest.cs @@ -5,7 +5,7 @@ namespace Microsoft.EntityFrameworkCore.Query; #nullable enable -public class PrimitiveCollectionsQuerySqlServerTest : PrimitiveCollectionsQueryTestBase< +public class PrimitiveCollectionsQuerySqlServerTest : PrimitiveCollectionsQueryRelationalTestBase< PrimitiveCollectionsQuerySqlServerTest.PrimitiveCollectionsQuerySqlServerFixture> { public PrimitiveCollectionsQuerySqlServerTest(PrimitiveCollectionsQuerySqlServerFixture fixture, ITestOutputHelper testOutputHelper) @@ -877,9 +877,9 @@ FROM [PrimitiveCollectionsEntity] AS [p] """); } - public override async Task Column_collection_Concat_parameter_collection_equality_inline_collection_not_supported(bool async) + public override async Task Column_collection_Concat_parameter_collection_equality_inline_collection(bool async) { - await base.Column_collection_Concat_parameter_collection_equality_inline_collection_not_supported(async); + await base.Column_collection_Concat_parameter_collection_equality_inline_collection(async); AssertSql(); } @@ -1018,10 +1018,9 @@ OFFSET 1 ROWS public override async Task Parameter_collection_in_subquery_Union_another_parameter_collection_as_compiled_query(bool async) { - var message = (await Assert.ThrowsAsync( - () => base.Parameter_collection_in_subquery_Union_another_parameter_collection_as_compiled_query(async))).Message; + await base.Parameter_collection_in_subquery_Union_another_parameter_collection_as_compiled_query(async); - Assert.Equal(RelationalStrings.SetOperationsRequireAtLeastOneSideWithValidTypeMapping("Union"), message); + AssertSql(); } public override async Task Column_collection_in_subquery_Union_parameter_collection(bool async) @@ -1093,9 +1092,9 @@ WHERE DATEPART(day, CAST([d].[value] AS datetime2)) <> 1 """); } - public override async Task Project_collection_of_ints_with_paging(bool async) + public override async Task Project_collection_of_nullable_ints_with_paging(bool async) { - await base.Project_collection_of_ints_with_paging(async); + await base.Project_collection_of_nullable_ints_with_paging(async); AssertSql( """ @@ -1110,9 +1109,9 @@ ORDER BY CAST([n].[key] AS int) """); } - public override async Task Project_collection_of_ints_with_paging2(bool async) + public override async Task Project_collection_of_nullable_ints_with_paging2(bool async) { - await base.Project_collection_of_ints_with_paging2(async); + await base.Project_collection_of_nullable_ints_with_paging2(async); AssertSql( """ @@ -1128,9 +1127,9 @@ OFFSET 1 ROWS """); } - public override async Task Project_collection_of_ints_with_paging3(bool async) + public override async Task Project_collection_of_nullable_ints_with_paging3(bool async) { - await base.Project_collection_of_ints_with_paging3(async); + await base.Project_collection_of_nullable_ints_with_paging3(async); AssertSql( """ diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/SpatialQuerySqlServerGeographyFixture.cs b/test/EFCore.SqlServer.FunctionalTests/Query/SpatialQuerySqlServerGeographyFixture.cs index 2a108b2e6f6..9d7223bdf72 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/SpatialQuerySqlServerGeographyFixture.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/SpatialQuerySqlServerGeographyFixture.cs @@ -40,17 +40,16 @@ protected class ReplacementTypeMappingSource : SqlServerTypeMappingSource { public ReplacementTypeMappingSource( TypeMappingSourceDependencies dependencies, - RelationalTypeMappingSourceDependencies relationalDependencies, - ISqlServerSingletonOptions sqlServerSingletonOptions) - : base(dependencies, relationalDependencies, sqlServerSingletonOptions) + RelationalTypeMappingSourceDependencies relationalDependencies) + : base(dependencies, relationalDependencies) { } protected override RelationalTypeMapping FindMapping(in RelationalTypeMappingInfo mappingInfo) => mappingInfo.ClrType == typeof(GeoPoint) ? ((RelationalTypeMapping)base.FindMapping(typeof(Point)) - .Clone(new GeoPointConverter(CreateGeometryServices().CreateGeometryFactory()))) - .Clone("geography", null) + .WithComposedConverter(new GeoPointConverter(CreateGeometryServices().CreateGeometryFactory()))) + .WithStoreTypeAndSize("geography", null) : base.FindMapping(mappingInfo); } } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/SpatialQuerySqlServerGeometryFixture.cs b/test/EFCore.SqlServer.FunctionalTests/Query/SpatialQuerySqlServerGeometryFixture.cs index 8084b613b7d..f386e5b2b81 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/SpatialQuerySqlServerGeometryFixture.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/SpatialQuerySqlServerGeometryFixture.cs @@ -37,16 +37,15 @@ protected class ReplacementTypeMappingSource : SqlServerTypeMappingSource { public ReplacementTypeMappingSource( TypeMappingSourceDependencies dependencies, - RelationalTypeMappingSourceDependencies relationalDependencies, - ISqlServerSingletonOptions sqlServerSingletonOptions) - : base(dependencies, relationalDependencies, sqlServerSingletonOptions) + RelationalTypeMappingSourceDependencies relationalDependencies) + : base(dependencies, relationalDependencies) { } protected override RelationalTypeMapping FindMapping(in RelationalTypeMappingInfo mappingInfo) => mappingInfo.ClrType == typeof(GeoPoint) ? ((RelationalTypeMapping)base.FindMapping(typeof(Point)) - .Clone(new GeoPointConverter())).Clone("geometry", null) + .WithComposedConverter(new GeoPointConverter())).WithStoreTypeAndSize("geometry", null) : base.FindMapping(mappingInfo); } } diff --git a/test/EFCore.SqlServer.FunctionalTests/SqlServerConfigPatternsTest.cs b/test/EFCore.SqlServer.FunctionalTests/SqlServerConfigPatternsTest.cs index 6e6d5853617..72e4845f6a6 100644 --- a/test/EFCore.SqlServer.FunctionalTests/SqlServerConfigPatternsTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/SqlServerConfigPatternsTest.cs @@ -5,6 +5,8 @@ // ReSharper disable UnusedAutoPropertyAccessor.Local #pragma warning disable RCS1102 // Make class static. +using Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal; + namespace Microsoft.EntityFrameworkCore; public class SqlServerConfigPatternsTest @@ -455,6 +457,102 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) } } + public class AzureSqlDatabase + { + [InlineData(true)] + [InlineData(false)] + [ConditionalTheory] + public void Retry_on_failure_enabled_by_default_on_Azure_SQL(bool azure) + { + using var context = new NorthwindContext(azure); + if (azure) + { + Assert.IsType(context.Database.CreateExecutionStrategy()); + } + else + { + Assert.IsType(context.Database.CreateExecutionStrategy()); + } + } + + private class NorthwindContext : DbContext + { + private readonly bool _isAzure; + + public NorthwindContext(bool azure) + : base() + { + _isAzure = azure; + } + + public DbSet Customers { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + => optionsBuilder + .EnableServiceProviderCaching(false) + .UseSqlServer( + @"Server=test.database.windows.net:4040;Database=Test;ConnectRetryCount=0", + a => + { + if (!_isAzure) + { + a.UseAzureSql(false); + } + }); + + protected override void OnModelCreating(ModelBuilder modelBuilder) + => ConfigureModel(modelBuilder); + } + } + + public class NonDefaultAzureSqlDatabase + { + [InlineData(true)] + [InlineData(false)] + [ConditionalTheory] + public void Retry_on_failure_enabled_if_Azure_SQL_configured(bool azure) + { + using var context = new NorthwindContext(azure); + if (azure) + { + Assert.IsType(context.Database.CreateExecutionStrategy()); + } + else + { + Assert.IsType(context.Database.CreateExecutionStrategy()); + } + } + + private class NorthwindContext : DbContext + { + private readonly bool _isAzure; + + public NorthwindContext(bool azure) + : base() + { + _isAzure = azure; + } + + public DbSet Customers { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + => optionsBuilder + .EnableServiceProviderCaching(false) + .UseSqlServer( + SqlServerNorthwindTestStoreFactory.NorthwindConnectionString, + a => + { + if (_isAzure) + { + a.UseAzureSql(true); + } + }); + + protected override void OnModelCreating(ModelBuilder modelBuilder) + => ConfigureModel(modelBuilder); + } + } + // ReSharper disable once ClassNeverInstantiated.Local private class Customer { diff --git a/test/EFCore.SqlServer.FunctionalTests/TestUtilities/TestSqlServerConnection.cs b/test/EFCore.SqlServer.FunctionalTests/TestUtilities/TestSqlServerConnection.cs index cc650496a63..758b7a49c5a 100644 --- a/test/EFCore.SqlServer.FunctionalTests/TestUtilities/TestSqlServerConnection.cs +++ b/test/EFCore.SqlServer.FunctionalTests/TestUtilities/TestSqlServerConnection.cs @@ -13,7 +13,7 @@ public TestSqlServerConnection(RelationalConnectionDependencies dependencies) { } - public int ErrorNumber { get; set; } = -2; + public int ErrorNumber { get; set; } = 64; public Queue OpenFailures { get; } = new(); public int OpenCount { get; set; } public Queue CommitFailures { get; } = new(); diff --git a/test/EFCore.SqlServer.FunctionalTests/Update/JsonUpdateSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Update/JsonUpdateSqlServerTest.cs index f571806a602..c0d5bc749e2 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Update/JsonUpdateSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Update/JsonUpdateSqlServerTest.cs @@ -1197,8 +1197,8 @@ public override async Task Edit_two_properties_on_same_entity_updates_the_entire AssertSql( """ -@p0='{"TestBoolean":false,"TestBooleanCollection":[true,false],"TestByte":25,"TestByteCollection":null,"TestCharacter":"h","TestCharacterCollection":["A","B","\u0022"],"TestDateTime":"2100-11-11T12:34:56","TestDateTimeCollection":["2000-01-01T12:34:56","3000-01-01T12:34:56"],"TestDateTimeOffset":"2200-11-11T12:34:56-05:00","TestDateTimeOffsetCollection":["2000-01-01T12:34:56-08:00"],"TestDecimal":-123450.01,"TestDecimalCollection":[-1234567890.01],"TestDefaultString":"MyDefaultStringInCollection1","TestDefaultStringCollection":["S1","\u0022S2\u0022","S3"],"TestDouble":-1.2345,"TestDoubleCollection":[-1.23456789,1.23456789,0],"TestEnum":0,"TestEnumCollection":[0,2,-7],"TestEnumWithIntConverter":1,"TestEnumWithIntConverterCollection":[0,2,-7],"TestGuid":"00000000-0000-0000-0000-000000000000","TestGuidCollection":["12345678-1234-4321-7777-987654321000"],"TestInt16":-12,"TestInt16Collection":[-32768,0,32767],"TestInt32":32,"TestInt32Collection":[-2147483648,0,2147483647],"TestInt64":64,"TestInt64Collection":[-9223372036854775808,0,9223372036854775807],"TestMaxLengthString":"Baz","TestMaxLengthStringCollection":["S1","S2","S3"],"TestNullableEnum":0,"TestNullableEnumCollection":[0,null,2,-7],"TestNullableEnumWithConverterThatHandlesNulls":"Two","TestNullableEnumWithConverterThatHandlesNullsCollection":[0,null,-7],"TestNullableEnumWithIntConverter":2,"TestNullableEnumWithIntConverterCollection":[0,null,2,-7],"TestNullableInt32":90,"TestNullableInt32Collection":[null,-2147483648,0,null,2147483647,null],"TestSignedByte":-18,"TestSignedByteCollection":[-128,0,127],"TestSingle":-1.4,"TestSingleCollection":[-1.234,0,-1.234],"TestTimeSpan":"6:05:04.003","TestTimeSpanCollection":["10:09:08.007","-9:50:51.993"],"TestUnsignedInt16":12,"TestUnsignedInt16Collection":[0,0,65535],"TestUnsignedInt32":12345,"TestUnsignedInt32Collection":[0,0,4294967295],"TestUnsignedInt64":1234567867,"TestUnsignedInt64Collection":[0,0,18446744073709551615]}' (Nullable = false) (Size = 1947) -@p1='{"TestBoolean":true,"TestBooleanCollection":[true,false],"TestByte":255,"TestByteCollection":null,"TestCharacter":"a","TestCharacterCollection":["A","B","\u0022"],"TestDateTime":"2000-01-01T12:34:56","TestDateTimeCollection":["2000-01-01T12:34:56","3000-01-01T12:34:56"],"TestDateTimeOffset":"2000-01-01T12:34:56-08:00","TestDateTimeOffsetCollection":["2000-01-01T12:34:56-08:00"],"TestDecimal":-1234567890.01,"TestDecimalCollection":[-1234567890.01],"TestDefaultString":"MyDefaultStringInReference1","TestDefaultStringCollection":["S1","\u0022S2\u0022","S3"],"TestDouble":-1.23456789,"TestDoubleCollection":[-1.23456789,1.23456789,0],"TestEnum":0,"TestEnumCollection":[0,2,-7],"TestEnumWithIntConverter":1,"TestEnumWithIntConverterCollection":[0,2,-7],"TestGuid":"12345678-1234-4321-7777-987654321000","TestGuidCollection":["12345678-1234-4321-7777-987654321000"],"TestInt16":-1234,"TestInt16Collection":[-32768,0,32767],"TestInt32":32,"TestInt32Collection":[-2147483648,0,2147483647],"TestInt64":64,"TestInt64Collection":[-9223372036854775808,0,9223372036854775807],"TestMaxLengthString":"Foo","TestMaxLengthStringCollection":["S1","S2","S3"],"TestNullableEnum":0,"TestNullableEnumCollection":[0,null,2,-7],"TestNullableEnumWithConverterThatHandlesNulls":"Three","TestNullableEnumWithConverterThatHandlesNullsCollection":[0,null,-7],"TestNullableEnumWithIntConverter":1,"TestNullableEnumWithIntConverterCollection":[0,null,2,-7],"TestNullableInt32":78,"TestNullableInt32Collection":[null,-2147483648,0,null,2147483647,null],"TestSignedByte":-128,"TestSignedByteCollection":[-128,0,127],"TestSingle":-1.234,"TestSingleCollection":[-1.234,0,-1.234],"TestTimeSpan":"10:09:08.007","TestTimeSpanCollection":["10:09:08.007","-9:50:51.993"],"TestUnsignedInt16":1234,"TestUnsignedInt16Collection":[0,0,65535],"TestUnsignedInt32":1234565789,"TestUnsignedInt32Collection":[0,0,4294967295],"TestUnsignedInt64":1234567890123456789,"TestUnsignedInt64Collection":[0,0,18446744073709551615]}' (Nullable = false) (Size = 1978) +@p0='{"TestBoolean":false,"TestBooleanCollection":[true,false],"TestByte":25,"TestByteCollection":null,"TestCharacter":"h","TestCharacterCollection":["A","B","\u0022"],"TestDateOnly":"2323-04-03","TestDateOnlyCollection":["3234-01-23","4331-01-21"],"TestDateTime":"2100-11-11T12:34:56","TestDateTimeCollection":["2000-01-01T12:34:56","3000-01-01T12:34:56"],"TestDateTimeOffset":"2200-11-11T12:34:56-05:00","TestDateTimeOffsetCollection":["2000-01-01T12:34:56-08:00"],"TestDecimal":-123450.01,"TestDecimalCollection":[-1234567890.01],"TestDefaultString":"MyDefaultStringInCollection1","TestDefaultStringCollection":["S1","\u0022S2\u0022","S3"],"TestDouble":-1.2345,"TestDoubleCollection":[-1.23456789,1.23456789,0],"TestEnum":0,"TestEnumCollection":[0,2,-7],"TestEnumWithIntConverter":1,"TestEnumWithIntConverterCollection":[0,2,-7],"TestGuid":"00000000-0000-0000-0000-000000000000","TestGuidCollection":["12345678-1234-4321-7777-987654321000"],"TestInt16":-12,"TestInt16Collection":[-32768,0,32767],"TestInt32":32,"TestInt32Collection":[-2147483648,0,2147483647],"TestInt64":64,"TestInt64Collection":[-9223372036854775808,0,9223372036854775807],"TestMaxLengthString":"Baz","TestMaxLengthStringCollection":["S1","S2","S3"],"TestNullableEnum":0,"TestNullableEnumCollection":[0,null,2,-7],"TestNullableEnumWithConverterThatHandlesNulls":"Two","TestNullableEnumWithConverterThatHandlesNullsCollection":[0,null,-7],"TestNullableEnumWithIntConverter":2,"TestNullableEnumWithIntConverterCollection":[0,null,2,-7],"TestNullableInt32":90,"TestNullableInt32Collection":[null,-2147483648,0,null,2147483647,null],"TestSignedByte":-18,"TestSignedByteCollection":[-128,0,127],"TestSingle":-1.4,"TestSingleCollection":[-1.234,0,-1.234],"TestTimeOnly":"05:07:08.0000000","TestTimeOnlyCollection":["13:42:23.0000000","07:17:25.0000000"],"TestTimeSpan":"6:05:04.003","TestTimeSpanCollection":["10:09:08.007","-9:50:51.993"],"TestUnsignedInt16":12,"TestUnsignedInt16Collection":[0,0,65535],"TestUnsignedInt32":12345,"TestUnsignedInt32Collection":[0,0,4294967295],"TestUnsignedInt64":1234567867,"TestUnsignedInt64Collection":[0,0,18446744073709551615]}' (Nullable = false) (Size = 2127) +@p1='{"TestBoolean":true,"TestBooleanCollection":[true,false],"TestByte":255,"TestByteCollection":null,"TestCharacter":"a","TestCharacterCollection":["A","B","\u0022"],"TestDateOnly":"2023-10-10","TestDateOnlyCollection":["1234-01-23","4321-01-21"],"TestDateTime":"2000-01-01T12:34:56","TestDateTimeCollection":["2000-01-01T12:34:56","3000-01-01T12:34:56"],"TestDateTimeOffset":"2000-01-01T12:34:56-08:00","TestDateTimeOffsetCollection":["2000-01-01T12:34:56-08:00"],"TestDecimal":-1234567890.01,"TestDecimalCollection":[-1234567890.01],"TestDefaultString":"MyDefaultStringInReference1","TestDefaultStringCollection":["S1","\u0022S2\u0022","S3"],"TestDouble":-1.23456789,"TestDoubleCollection":[-1.23456789,1.23456789,0],"TestEnum":0,"TestEnumCollection":[0,2,-7],"TestEnumWithIntConverter":1,"TestEnumWithIntConverterCollection":[0,2,-7],"TestGuid":"12345678-1234-4321-7777-987654321000","TestGuidCollection":["12345678-1234-4321-7777-987654321000"],"TestInt16":-1234,"TestInt16Collection":[-32768,0,32767],"TestInt32":32,"TestInt32Collection":[-2147483648,0,2147483647],"TestInt64":64,"TestInt64Collection":[-9223372036854775808,0,9223372036854775807],"TestMaxLengthString":"Foo","TestMaxLengthStringCollection":["S1","S2","S3"],"TestNullableEnum":0,"TestNullableEnumCollection":[0,null,2,-7],"TestNullableEnumWithConverterThatHandlesNulls":"Three","TestNullableEnumWithConverterThatHandlesNullsCollection":[0,null,-7],"TestNullableEnumWithIntConverter":1,"TestNullableEnumWithIntConverterCollection":[0,null,2,-7],"TestNullableInt32":78,"TestNullableInt32Collection":[null,-2147483648,0,null,2147483647,null],"TestSignedByte":-128,"TestSignedByteCollection":[-128,0,127],"TestSingle":-1.234,"TestSingleCollection":[-1.234,0,-1.234],"TestTimeOnly":"11:12:13.0000000","TestTimeOnlyCollection":["11:42:23.0000000","07:17:27.0000000"],"TestTimeSpan":"10:09:08.007","TestTimeSpanCollection":["10:09:08.007","-9:50:51.993"],"TestUnsignedInt16":1234,"TestUnsignedInt16Collection":[0,0,65535],"TestUnsignedInt32":1234565789,"TestUnsignedInt32Collection":[0,0,4294967295],"TestUnsignedInt64":1234567890123456789,"TestUnsignedInt64Collection":[0,0,18446744073709551615]}' (Nullable = false) (Size = 2158) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; @@ -1780,6 +1780,54 @@ FROM [JsonEntitiesAllTypes] AS [j] """); } + public override async Task Edit_single_property_collection_of_dateonly() + { + await base.Edit_single_property_collection_of_dateonly(); + + AssertSql( +""" +@p0='["3234-01-23","0001-01-07"]' (Nullable = false) (Size = 4000) +@p1='["0001-01-07","4321-01-21"]' (Nullable = false) (Size = 4000) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestDateOnlyCollection', JSON_QUERY(@p0)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestDateOnlyCollection', JSON_QUERY(@p1)) +OUTPUT 1 +WHERE [Id] = @p2; +""", + // + """ +SELECT TOP(2) [j].[Id], [j].[TestBooleanCollection], [j].[TestByteCollection], [j].[TestCharacterCollection], [j].[TestDateTimeCollection], [j].[TestDateTimeOffsetCollection], [j].[TestDecimalCollection], [j].[TestDefaultStringCollection], [j].[TestDoubleCollection], [j].[TestEnumCollection], [j].[TestEnumWithIntConverterCollection], [j].[TestGuidCollection], [j].[TestInt16Collection], [j].[TestInt32Collection], [j].[TestInt64Collection], [j].[TestMaxLengthStringCollection], [j].[TestNullableEnumCollection], [j].[TestNullableEnumWithConverterThatHandlesNullsCollection], [j].[TestNullableEnumWithIntConverterCollection], [j].[TestNullableInt32Collection], [j].[TestSignedByteCollection], [j].[TestSingleCollection], [j].[TestTimeSpanCollection], [j].[TestUnsignedInt16Collection], [j].[TestUnsignedInt32Collection], [j].[TestUnsignedInt64Collection], [j].[Collection], [j].[Reference] +FROM [JsonEntitiesAllTypes] AS [j] +WHERE [j].[Id] = 1 +"""); + } + + public override async Task Edit_single_property_collection_of_timeonly() + { + await base.Edit_single_property_collection_of_timeonly(); + + AssertSql( +""" +@p0='["13:42:23.0000000","01:01:07.0000000"]' (Nullable = false) (Size = 4000) +@p1='["01:01:07.0000000","07:17:27.0000000"]' (Nullable = false) (Size = 4000) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestTimeOnlyCollection', JSON_QUERY(@p0)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestTimeOnlyCollection', JSON_QUERY(@p1)) +OUTPUT 1 +WHERE [Id] = @p2; +""", + // + """ +SELECT TOP(2) [j].[Id], [j].[TestBooleanCollection], [j].[TestByteCollection], [j].[TestCharacterCollection], [j].[TestDateTimeCollection], [j].[TestDateTimeOffsetCollection], [j].[TestDecimalCollection], [j].[TestDefaultStringCollection], [j].[TestDoubleCollection], [j].[TestEnumCollection], [j].[TestEnumWithIntConverterCollection], [j].[TestGuidCollection], [j].[TestInt16Collection], [j].[TestInt32Collection], [j].[TestInt64Collection], [j].[TestMaxLengthStringCollection], [j].[TestNullableEnumCollection], [j].[TestNullableEnumWithConverterThatHandlesNullsCollection], [j].[TestNullableEnumWithIntConverterCollection], [j].[TestNullableInt32Collection], [j].[TestSignedByteCollection], [j].[TestSingleCollection], [j].[TestTimeSpanCollection], [j].[TestUnsignedInt16Collection], [j].[TestUnsignedInt32Collection], [j].[TestUnsignedInt64Collection], [j].[Collection], [j].[Reference] +FROM [JsonEntitiesAllTypes] AS [j] +WHERE [j].[Id] = 1 +"""); + } + public override async Task Edit_single_property_collection_of_uint16() { await base.Edit_single_property_collection_of_uint16(); diff --git a/test/EFCore.SqlServer.FunctionalTests/Update/SqlServerUpdateSqlGeneratorTest.cs b/test/EFCore.SqlServer.FunctionalTests/Update/SqlServerUpdateSqlGeneratorTest.cs index 7de26bdcb11..f361aa68484 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Update/SqlServerUpdateSqlGeneratorTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Update/SqlServerUpdateSqlGeneratorTest.cs @@ -21,8 +21,7 @@ protected override IUpdateSqlGenerator CreateSqlGenerator() new RelationalSqlGenerationHelperDependencies()), new SqlServerTypeMappingSource( TestServiceFactory.Instance.Create(), - TestServiceFactory.Instance.Create(), - new SqlServerSingletonOptions()))); + TestServiceFactory.Instance.Create()))); } protected override TestHelpers TestHelpers diff --git a/test/EFCore.SqlServer.Tests/Design/Internal/SqlServerAnnotationCodeGeneratorTest.cs b/test/EFCore.SqlServer.Tests/Design/Internal/SqlServerAnnotationCodeGeneratorTest.cs index 6ec6b2c0487..0151c3cae4f 100644 --- a/test/EFCore.SqlServer.Tests/Design/Internal/SqlServerAnnotationCodeGeneratorTest.cs +++ b/test/EFCore.SqlServer.Tests/Design/Internal/SqlServerAnnotationCodeGeneratorTest.cs @@ -394,6 +394,5 @@ private SqlServerAnnotationCodeGenerator CreateGenerator() new JsonValueReaderWriterSource(new JsonValueReaderWriterSourceDependencies()), Array.Empty()), new RelationalTypeMappingSourceDependencies( - Array.Empty()), - new SqlServerSingletonOptions()))); + Array.Empty())))); } diff --git a/test/EFCore.SqlServer.Tests/Infrastructure/SqlServerModelValidatorTest.cs b/test/EFCore.SqlServer.Tests/Infrastructure/SqlServerModelValidatorTest.cs index 0e2c71e8591..7019f8ada98 100644 --- a/test/EFCore.SqlServer.Tests/Infrastructure/SqlServerModelValidatorTest.cs +++ b/test/EFCore.SqlServer.Tests/Infrastructure/SqlServerModelValidatorTest.cs @@ -402,6 +402,38 @@ public virtual void Detects_duplicate_index_names_within_hierarchy_differently_o modelBuilder); } + [ConditionalFact] + public virtual void Detects_duplicate_index_names_within_hierarchy_different_sort_in_tempdb() + { + var modelBuilder = CreateConventionModelBuilder(); + modelBuilder.Entity(); + modelBuilder.Entity().HasIndex(c => c.Name).HasDatabaseName("IX_Animal_Name"); + modelBuilder.Entity().HasIndex(d => d.Name).HasDatabaseName("IX_Animal_Name").SortInTempDb(); + + VerifyError( + SqlServerStrings.DuplicateIndexSortInTempDbMismatch( + "{'" + nameof(Dog.Name) + "'}", nameof(Dog), + "{'" + nameof(Cat.Name) + "'}", nameof(Cat), + nameof(Animal), "IX_Animal_Name"), + modelBuilder); + } + + [ConditionalFact] + public virtual void Detects_duplicate_index_names_within_hierarchy_different_data_compression() + { + var modelBuilder = CreateConventionModelBuilder(); + modelBuilder.Entity(); + modelBuilder.Entity().HasIndex(c => c.Name).HasDatabaseName("IX_Animal_Name"); + modelBuilder.Entity().HasIndex(d => d.Name).HasDatabaseName("IX_Animal_Name").UseDataCompression(DataCompressionType.Page); + + VerifyError( + SqlServerStrings.DuplicateIndexDataCompressionMismatch( + "{'" + nameof(Dog.Name) + "'}", nameof(Dog), + "{'" + nameof(Cat.Name) + "'}", nameof(Cat), + nameof(Animal), "IX_Animal_Name"), + modelBuilder); + } + [ConditionalFact] public virtual void Detects_duplicate_index_names_within_hierarchy_with_different_different_include() { diff --git a/test/EFCore.SqlServer.Tests/Metadata/SqlServerBuilderExtensionsTest.cs b/test/EFCore.SqlServer.Tests/Metadata/SqlServerBuilderExtensionsTest.cs index 96614ca705f..5dc89729e06 100644 --- a/test/EFCore.SqlServer.Tests/Metadata/SqlServerBuilderExtensionsTest.cs +++ b/test/EFCore.SqlServer.Tests/Metadata/SqlServerBuilderExtensionsTest.cs @@ -1078,6 +1078,72 @@ public void Throws_if_attempt_to_set_fillfactor_with_argument_out_of_range(int f }); } + [ConditionalFact] + public void Can_set_index_with_sortintempdb() + { + var modelBuilder = CreateConventionModelBuilder(); + + modelBuilder + .Entity() + .HasIndex(e => e.Name) + .SortInTempDb(); + + var index = modelBuilder.Model.FindEntityType(typeof(Customer)).GetIndexes().Single(); + + Assert.True(index.GetSortInTempDb()); + } + + [ConditionalFact] + public void Can_set_index_with_sortintempdb_non_generic() + { + var modelBuilder = CreateConventionModelBuilder(); + + modelBuilder + .Entity(typeof(Customer)) + .HasIndex("Name") + .SortInTempDb(); + + var index = modelBuilder.Model.FindEntityType(typeof(Customer)).GetIndexes().Single(); + + Assert.True(index.GetSortInTempDb()); + } + + [ConditionalTheory] + [InlineData(DataCompressionType.None)] + [InlineData(DataCompressionType.Row)] + [InlineData(DataCompressionType.Page)] + public void Can_set_index_with_datacompression(DataCompressionType dataCompression) + { + var modelBuilder = CreateConventionModelBuilder(); + + modelBuilder + .Entity() + .HasIndex(e => e.Name) + .UseDataCompression(dataCompression); + + var index = modelBuilder.Model.FindEntityType(typeof(Customer)).GetIndexes().Single(); + + Assert.Equal(dataCompression, index.GetDataCompression()); + } + + [ConditionalTheory] + [InlineData(DataCompressionType.None)] + [InlineData(DataCompressionType.Row)] + [InlineData(DataCompressionType.Page)] + public void Can_set_index_with_datacompression_non_generic(DataCompressionType dataCompression) + { + var modelBuilder = CreateConventionModelBuilder(); + + modelBuilder + .Entity(typeof(Customer)) + .HasIndex("Name") + .UseDataCompression(dataCompression); + + var index = modelBuilder.Model.FindEntityType(typeof(Customer)).GetIndexes().Single(); + + Assert.Equal(dataCompression, index.GetDataCompression()); + } + #region UseSqlOutputClause [ConditionalFact] diff --git a/test/EFCore.SqlServer.Tests/Migrations/SqlServerModelDifferTest.cs b/test/EFCore.SqlServer.Tests/Migrations/SqlServerModelDifferTest.cs index b99f98fdc9c..23b49967592 100644 --- a/test/EFCore.SqlServer.Tests/Migrations/SqlServerModelDifferTest.cs +++ b/test/EFCore.SqlServer.Tests/Migrations/SqlServerModelDifferTest.cs @@ -1390,4 +1390,241 @@ public void Rebuild_index_with_different_fillfactor_value() Assert.Equal(90, annotationValue); }); + + [ConditionalFact] + public void Dont_rebuild_index_with_unchanged_sortintempdb_option() + => Execute( + source => source + .Entity( + "Address", + x => + { + x.Property("Id"); + x.Property("Zip"); + x.Property("City"); + x.HasIndex("Zip") + .SortInTempDb(); + }), + target => target + .Entity( + "Address", + x => + { + x.Property("Id"); + x.Property("Zip"); + x.Property("City"); + x.HasIndex("Zip") + .SortInTempDb(); + }), + operations => Assert.Equal(0, operations.Count)); + + [ConditionalFact] + public void Rebuild_index_when_changing_sortintempdb_option() + => Execute( + _ => { }, + source => source + .Entity( + "Address", + x => + { + x.Property("Id"); + x.Property("Zip"); + x.Property("City"); + x.Property("Street"); + x.HasIndex("Zip"); + }), + target => target + .Entity( + "Address", + x => + { + x.Property("Id"); + x.Property("Zip"); + x.Property("City"); + x.Property("Street"); + x.HasIndex("Zip") + .SortInTempDb(); + }), + upOps => + { + Assert.Equal(2, upOps.Count); + + var operation1 = Assert.IsType(upOps[0]); + Assert.Equal("Address", operation1.Table); + Assert.Equal("IX_Address_Zip", operation1.Name); + + Assert.Empty(operation1.GetAnnotations()); + + var operation2 = Assert.IsType(upOps[1]); + Assert.Equal("Address", operation1.Table); + Assert.Equal("IX_Address_Zip", operation1.Name); + + var annotation = operation2.GetAnnotation(SqlServerAnnotationNames.SortInTempDb); + Assert.NotNull(annotation); + + var annotationValue = Assert.IsType(annotation.Value); + Assert.True(annotationValue); + }, + downOps => + { + Assert.Equal(2, downOps.Count); + + var operation1 = Assert.IsType(downOps[0]); + Assert.Equal("Address", operation1.Table); + Assert.Equal("IX_Address_Zip", operation1.Name); + + Assert.Empty(operation1.GetAnnotations()); + + var operation2 = Assert.IsType(downOps[1]); + Assert.Equal("Address", operation1.Table); + Assert.Equal("IX_Address_Zip", operation1.Name); + + Assert.Empty(operation2.GetAnnotations()); + }); + + [ConditionalTheory] + [InlineData(DataCompressionType.None)] + [InlineData(DataCompressionType.Row)] + [InlineData(DataCompressionType.Page)] + public void Dont_rebuild_index_with_unchanged_datacompression_option(DataCompressionType dataCompression) + => Execute( + source => source + .Entity( + "Address", + x => + { + x.Property("Id"); + x.Property("Zip"); + x.Property("City"); + x.HasIndex("Zip") + .UseDataCompression(dataCompression); + }), + target => target + .Entity( + "Address", + x => + { + x.Property("Id"); + x.Property("Zip"); + x.Property("City"); + x.HasIndex("Zip") + .UseDataCompression(dataCompression); + }), + operations => Assert.Equal(0, operations.Count)); + + [ConditionalTheory] + [InlineData(DataCompressionType.None)] + [InlineData(DataCompressionType.Row)] + [InlineData(DataCompressionType.Page)] + public void Rebuild_index_when_adding_datacompression_option(DataCompressionType dataCompression) + => Execute( + _ => { }, + source => source + .Entity( + "Address", + x => + { + x.Property("Id"); + x.Property("Zip"); + x.Property("City"); + x.Property("Street"); + x.HasIndex("Zip"); + }), + target => target + .Entity( + "Address", + x => + { + x.Property("Id"); + x.Property("Zip"); + x.Property("City"); + x.Property("Street"); + x.HasIndex("Zip") + .UseDataCompression(dataCompression); + }), + upOps => + { + Assert.Equal(2, upOps.Count); + + var operation1 = Assert.IsType(upOps[0]); + Assert.Equal("Address", operation1.Table); + Assert.Equal("IX_Address_Zip", operation1.Name); + + Assert.Empty(operation1.GetAnnotations()); + + var operation2 = Assert.IsType(upOps[1]); + Assert.Equal("Address", operation1.Table); + Assert.Equal("IX_Address_Zip", operation1.Name); + + var annotation = operation2.GetAnnotation(SqlServerAnnotationNames.DataCompression); + Assert.NotNull(annotation); + + var annotationValue = Assert.IsType(annotation.Value); + Assert.Equal(dataCompression, annotationValue); + }, + downOps => + { + Assert.Equal(2, downOps.Count); + + var operation1 = Assert.IsType(downOps[0]); + Assert.Equal("Address", operation1.Table); + Assert.Equal("IX_Address_Zip", operation1.Name); + + Assert.Empty(operation1.GetAnnotations()); + + var operation2 = Assert.IsType(downOps[1]); + Assert.Equal("Address", operation1.Table); + Assert.Equal("IX_Address_Zip", operation1.Name); + + Assert.Empty(operation2.GetAnnotations()); + }); + + [ConditionalFact] + public void Rebuild_index_with_different_datacompression_value() + => Execute( + source => source + .Entity( + "Address", + x => + { + x.Property("Id"); + x.Property("Zip"); + x.Property("City"); + x.Property("Street"); + x.HasIndex("Zip") + .UseDataCompression(DataCompressionType.Row); + }), + target => target + .Entity( + "Address", + x => + { + x.Property("Id"); + x.Property("Zip"); + x.Property("City"); + x.Property("Street"); + x.HasIndex("Zip") + .UseDataCompression(DataCompressionType.Page); + }), + operations => + { + Assert.Equal(2, operations.Count); + + var operation1 = Assert.IsType(operations[0]); + Assert.Equal("Address", operation1.Table); + Assert.Equal("IX_Address_Zip", operation1.Name); + + Assert.Empty(operation1.GetAnnotations()); + + var operation2 = Assert.IsType(operations[1]); + Assert.Equal("Address", operation1.Table); + Assert.Equal("IX_Address_Zip", operation1.Name); + + var annotation = operation2.GetAnnotation(SqlServerAnnotationNames.DataCompression); + Assert.NotNull(annotation); + + var annotationValue = Assert.IsType(annotation.Value); + + Assert.Equal(DataCompressionType.Page, annotationValue); + }); } diff --git a/test/EFCore.SqlServer.Tests/SqlServerSequenceValueGeneratorTest.cs b/test/EFCore.SqlServer.Tests/SqlServerSequenceValueGeneratorTest.cs index 13d875b5927..045c5fb35a7 100644 --- a/test/EFCore.SqlServer.Tests/SqlServerSequenceValueGeneratorTest.cs +++ b/test/EFCore.SqlServer.Tests/SqlServerSequenceValueGeneratorTest.cs @@ -77,8 +77,7 @@ private async Task Generates_sequential_values(bool async) new RelationalSqlGenerationHelperDependencies()), new SqlServerTypeMappingSource( TestServiceFactory.Instance.Create(), - TestServiceFactory.Instance.Create(), - new SqlServerSingletonOptions()))), + TestServiceFactory.Instance.Create()))), state, CreateConnection(), new FakeRelationalCommandDiagnosticsLogger()); @@ -132,8 +131,7 @@ private async Task>> GenerateValuesInMultipleThreads(int new RelationalSqlGenerationHelperDependencies()), new SqlServerTypeMappingSource( TestServiceFactory.Instance.Create(), - TestServiceFactory.Instance.Create(), - new SqlServerSingletonOptions()))); + TestServiceFactory.Instance.Create()))); var logger = new FakeRelationalCommandDiagnosticsLogger(); @@ -184,8 +182,7 @@ public void Does_not_generate_temp_values() new RelationalSqlGenerationHelperDependencies()), new SqlServerTypeMappingSource( TestServiceFactory.Instance.Create(), - TestServiceFactory.Instance.Create(), - new SqlServerSingletonOptions()))), + TestServiceFactory.Instance.Create()))), state, CreateConnection(), new FakeRelationalCommandDiagnosticsLogger()); diff --git a/test/EFCore.SqlServer.Tests/Storage/SqlServerTypeMappingSourceTest.cs b/test/EFCore.SqlServer.Tests/Storage/SqlServerTypeMappingSourceTest.cs index 844936668cb..6d235e9ea46 100644 --- a/test/EFCore.SqlServer.Tests/Storage/SqlServerTypeMappingSourceTest.cs +++ b/test/EFCore.SqlServer.Tests/Storage/SqlServerTypeMappingSourceTest.cs @@ -313,7 +313,10 @@ public void Does_key_SQL_Server_string_mapping(Type type, bool? unicode, bool? f property.SetIsFixedLength(fixedLength); ((IMutableEntityType)property.DeclaringType).SetPrimaryKey(property); - var typeMapping = CreateRelationalTypeMappingSource().GetMapping((IProperty)property); + var model = property.DeclaringType.Model.FinalizeModel(); + var typeMappingSource = CreateRelationalTypeMappingSource(model); + var runtimeProperty = model.FindEntityType(typeof(MyType))!.FindProperty("MyProp")!; + var typeMapping = typeMappingSource.GetMapping(runtimeProperty); Assert.Equal(DbType.String, typeMapping.DbType); Assert.Equal("nvarchar(450)", typeMapping.StoreType); @@ -351,7 +354,10 @@ public void Does_foreign_key_SQL_Server_string_mapping(Type type, bool? unicode, var pk = ((IMutableEntityType)property.DeclaringType).SetPrimaryKey(property); ((IMutableEntityType)property.DeclaringType).AddForeignKey(fkProperty, pk, ((IMutableEntityType)property.DeclaringType)); - var typeMapping = CreateRelationalTypeMappingSource().GetMapping((IProperty)fkProperty); + var model = property.DeclaringType.Model.FinalizeModel(); + var typeMappingSource = CreateRelationalTypeMappingSource(model); + var runtimeProperty = model.FindEntityType(typeof(MyType))!.FindProperty("MyProp")!; + var typeMapping = typeMappingSource.GetMapping(runtimeProperty); Assert.Equal(DbType.String, typeMapping.DbType); Assert.Equal("nvarchar(450)", typeMapping.StoreType); @@ -390,7 +396,10 @@ public void Does_required_foreign_key_SQL_Server_string_mapping(Type type, bool? ((IMutableEntityType)property.DeclaringType).AddForeignKey(fkProperty, pk, ((IMutableEntityType)property.DeclaringType)); fkProperty.IsNullable = false; - var typeMapping = CreateRelationalTypeMappingSource().GetMapping((IProperty)fkProperty); + var model = property.DeclaringType.Model.FinalizeModel(); + var typeMappingSource = CreateRelationalTypeMappingSource(model); + var runtimeProperty = model.FindEntityType(typeof(MyType))!.FindProperty("MyProp")!; + var typeMapping = typeMappingSource.GetMapping(runtimeProperty); Assert.Equal(DbType.String, typeMapping.DbType); Assert.Equal("nvarchar(450)", typeMapping.StoreType); @@ -426,7 +435,10 @@ public void Does_indexed_column_SQL_Server_string_mapping(Type type, bool? unico property.SetIsFixedLength(fixedLength); entityType.AddIndex(property); - var typeMapping = CreateRelationalTypeMappingSource().GetMapping((IProperty)property); + var model = property.DeclaringType.Model.FinalizeModel(); + var typeMappingSource = CreateRelationalTypeMappingSource(model); + var runtimeProperty = model.FindEntityType(typeof(MyType))!.FindProperty("MyProp")!; + var typeMapping = typeMappingSource.GetMapping(runtimeProperty); Assert.Equal(DbType.String, typeMapping.DbType); Assert.Equal("nvarchar(450)", typeMapping.StoreType); @@ -456,9 +468,11 @@ public void Does_IndexAttribute_column_SQL_Server_string_mapping(bool? unicode, var property = entityType.FindProperty("Name"); property.SetIsUnicode(unicode); property.SetIsFixedLength(fixedLength); - entityType.Model.FinalizeModel(); - var typeMapping = CreateRelationalTypeMappingSource().GetMapping((IProperty)property); + var model = property.DeclaringType.Model.FinalizeModel(); + var typeMappingSource = CreateRelationalTypeMappingSource(model); + var runtimeProperty = model.FindEntityType(typeof(MyTypeWithIndexAttribute))!.FindProperty("Name")!; + var typeMapping = typeMappingSource.GetMapping(runtimeProperty); Assert.Equal(DbType.String, typeMapping.DbType); Assert.Equal("nvarchar(450)", typeMapping.StoreType); @@ -476,12 +490,14 @@ public void Does_IndexAttribute_column_SQL_Server_string_mapping(bool? unicode, public void Does_IndexAttribute_column_SQL_Server_primitive_collection_mapping(bool? unicode, bool? fixedLength) { var entityType = CreateEntityType(); - var property = entityType.FindProperty("Ints"); + var property = entityType.FindProperty("Ints")!; property.SetIsUnicode(unicode); property.SetIsFixedLength(fixedLength); - entityType.Model.FinalizeModel(); - var typeMapping = CreateRelationalTypeMappingSource().GetMapping((IProperty)property); + var model = entityType.Model.FinalizeModel(); + var typeMappingSource = CreateRelationalTypeMappingSource(model); + var runtimeProperty = model.FindEntityType(typeof(MyTypeWithIndexAttributeOnCollection))!.FindProperty("Ints")!; + var typeMapping = typeMappingSource.GetMapping(runtimeProperty); Assert.Equal(DbType.String, typeMapping.DbType); Assert.Equal("nvarchar(450)", typeMapping.StoreType); @@ -714,7 +730,10 @@ public void Does_key_SQL_Server_string_mapping_ansi(Type type, bool? fixedLength property.SetIsFixedLength(fixedLength); ((IMutableEntityType)property.DeclaringType).SetPrimaryKey(property); - var typeMapping = CreateRelationalTypeMappingSource().GetMapping((IProperty)property); + var model = property.DeclaringType.Model.FinalizeModel(); + var typeMappingSource = CreateRelationalTypeMappingSource(model); + var runtimeProperty = model.FindEntityType(typeof(MyType))!.FindProperty("MyProp")!; + var typeMapping = typeMappingSource.GetMapping(runtimeProperty); Assert.Equal(DbType.AnsiString, typeMapping.DbType); Assert.Equal("varchar(900)", typeMapping.StoreType); @@ -748,7 +767,10 @@ public void Does_foreign_key_SQL_Server_string_mapping_ansi(Type type, bool? fix var pk = ((IMutableEntityType)property.DeclaringType).SetPrimaryKey(property); ((IMutableEntityType)property.DeclaringType).AddForeignKey(fkProperty, pk, ((IMutableEntityType)property.DeclaringType)); - var typeMapping = CreateRelationalTypeMappingSource().GetMapping((IProperty)fkProperty); + var model = property.DeclaringType.Model.FinalizeModel(); + var typeMappingSource = CreateRelationalTypeMappingSource(model); + var runtimeProperty = model.FindEntityType(typeof(MyType))!.FindProperty("MyProp")!; + var typeMapping = typeMappingSource.GetMapping(runtimeProperty); Assert.Equal(DbType.AnsiString, typeMapping.DbType); Assert.Equal("varchar(900)", typeMapping.StoreType); @@ -783,7 +805,10 @@ public void Does_required_foreign_key_SQL_Server_string_mapping_ansi(Type type, ((IMutableEntityType)property.DeclaringType).AddForeignKey(fkProperty, pk, ((IMutableEntityType)property.DeclaringType)); fkProperty.IsNullable = false; - var typeMapping = CreateRelationalTypeMappingSource().GetMapping((IProperty)fkProperty); + var model = property.DeclaringType.Model.FinalizeModel(); + var typeMappingSource = CreateRelationalTypeMappingSource(model); + var runtimeProperty = model.FindEntityType(typeof(MyType))!.FindProperty("MyProp")!; + var typeMapping = typeMappingSource.GetMapping(runtimeProperty); Assert.Equal(DbType.AnsiString, typeMapping.DbType); Assert.Equal("varchar(900)", typeMapping.StoreType); @@ -815,7 +840,10 @@ public void Does_indexed_column_SQL_Server_string_mapping_ansi(Type type, bool? property.SetIsFixedLength(fixedLength); entityType.AddIndex(property); - var typeMapping = CreateRelationalTypeMappingSource().GetMapping((IProperty)property); + var model = property.DeclaringType.Model.FinalizeModel(); + var typeMappingSource = CreateRelationalTypeMappingSource(model); + var runtimeProperty = model.FindEntityType(typeof(MyType))!.FindProperty("MyProp")!; + var typeMapping = typeMappingSource.GetMapping(runtimeProperty); Assert.Equal(DbType.AnsiString, typeMapping.DbType); Assert.Equal("varchar(900)", typeMapping.StoreType); @@ -843,9 +871,11 @@ public void Does_IndexAttribute_column_SQL_Server_string_mapping_ansi(bool? fixe var property = entityType.FindProperty("Name"); property.SetIsUnicode(false); property.SetIsFixedLength(fixedLength); - entityType.Model.FinalizeModel(); - var typeMapping = CreateRelationalTypeMappingSource().GetMapping((IProperty)property); + var model = property.DeclaringType.Model.FinalizeModel(); + var typeMappingSource = CreateRelationalTypeMappingSource(model); + var runtimeProperty = model.FindEntityType(typeof(MyTypeWithIndexAttribute))!.FindProperty("Name")!; + var typeMapping = typeMappingSource.GetMapping(runtimeProperty); Assert.Equal(DbType.AnsiString, typeMapping.DbType); Assert.Equal("varchar(900)", typeMapping.StoreType); @@ -861,12 +891,14 @@ public void Does_IndexAttribute_column_SQL_Server_string_mapping_ansi(bool? fixe public void Does_IndexAttribute_column_SQL_Server_primitive_collection_mapping_ansi(bool? fixedLength) { var entityType = CreateEntityType(); - var property = entityType.FindProperty("Ints"); + var property = entityType.FindProperty("Ints")!; property.SetIsUnicode(false); property.SetIsFixedLength(fixedLength); - entityType.Model.FinalizeModel(); - var typeMapping = CreateRelationalTypeMappingSource().GetMapping((IProperty)property); + var model = entityType.Model.FinalizeModel(); + var typeMappingSource = CreateRelationalTypeMappingSource(model); + var runtimeProperty = model.FindEntityType(typeof(MyTypeWithIndexAttributeOnCollection))!.FindProperty("Ints")!; + var typeMapping = typeMappingSource.GetMapping(runtimeProperty); Assert.Equal(DbType.AnsiString, typeMapping.DbType); Assert.Equal("varchar(900)", typeMapping.StoreType); @@ -1044,7 +1076,10 @@ private RelationalTypeMapping CreateBinaryMapping(string typeName, int? maxLengt property.SetMaxLength(maxLength); } - return CreateRelationalTypeMappingSource().GetMapping((IProperty)property); + var model = property.DeclaringType.Model.FinalizeModel(); + var typeMappingSource = CreateRelationalTypeMappingSource(model); + var runtimeProperty = model.FindEntityType(typeof(MyType))!.FindProperty("MyBinaryProp")!; + return typeMappingSource.GetMapping(runtimeProperty); } [ConditionalTheory] @@ -1057,7 +1092,10 @@ public void Does_key_SQL_Server_binary_mapping(bool? fixedLength) property.SetIsFixedLength(fixedLength); ((IMutableEntityType)property.DeclaringType).SetPrimaryKey(property); - var typeMapping = CreateRelationalTypeMappingSource().GetMapping((IProperty)property); + var model = property.DeclaringType.Model.FinalizeModel(); + var typeMappingSource = CreateRelationalTypeMappingSource(model); + var runtimeProperty = model.FindEntityType(typeof(MyType))!.FindProperty("MyProp")!; + var typeMapping = typeMappingSource.GetMapping(runtimeProperty); Assert.Equal(DbType.Binary, typeMapping.DbType); Assert.Equal("varbinary(900)", typeMapping.StoreType); @@ -1077,7 +1115,10 @@ public void Does_foreign_key_SQL_Server_binary_mapping(bool? fixedLength) var pk = ((IMutableEntityType)property.DeclaringType).SetPrimaryKey(property); ((IMutableEntityType)property.DeclaringType).AddForeignKey(fkProperty, pk, ((IMutableEntityType)property.DeclaringType)); - var typeMapping = CreateRelationalTypeMappingSource().GetMapping((IProperty)fkProperty); + var model = property.DeclaringType.Model.FinalizeModel(); + var typeMappingSource = CreateRelationalTypeMappingSource(model); + var runtimeProperty = model.FindEntityType(typeof(MyType))!.FindProperty("MyProp")!; + var typeMapping = typeMappingSource.GetMapping(runtimeProperty); Assert.False(typeMapping.IsFixedLength); Assert.Equal(DbType.Binary, typeMapping.DbType); @@ -1098,7 +1139,10 @@ public void Does_required_foreign_key_SQL_Server_binary_mapping(bool? fixedLengt ((IMutableEntityType)property.DeclaringType).AddForeignKey(fkProperty, pk, ((IMutableEntityType)property.DeclaringType)); fkProperty.IsNullable = false; - var typeMapping = CreateRelationalTypeMappingSource().GetMapping((IProperty)fkProperty); + var model = property.DeclaringType.Model.FinalizeModel(); + var typeMappingSource = CreateRelationalTypeMappingSource(model); + var runtimeProperty = model.FindEntityType(typeof(MyType))!.FindProperty("MyProp")!; + var typeMapping = typeMappingSource.GetMapping(runtimeProperty); Assert.False(typeMapping.IsFixedLength); Assert.Equal(DbType.Binary, typeMapping.DbType); @@ -1116,7 +1160,10 @@ public void Does_indexed_column_SQL_Server_binary_mapping(bool? fixedLength) property.SetIsFixedLength(fixedLength); entityType.AddIndex(property); - var typeMapping = CreateRelationalTypeMappingSource().GetMapping((IProperty)property); + var model = property.DeclaringType.Model.FinalizeModel(); + var typeMappingSource = CreateRelationalTypeMappingSource(model); + var runtimeProperty = model.FindEntityType(typeof(MyType))!.FindProperty("MyProp")!; + var typeMapping = typeMappingSource.GetMapping(runtimeProperty); Assert.False(typeMapping.IsFixedLength); Assert.Equal(DbType.Binary, typeMapping.DbType); @@ -1131,7 +1178,10 @@ public void Does_non_key_SQL_Server_rowversion_mapping() property.IsConcurrencyToken = true; property.ValueGenerated = ValueGenerated.OnAddOrUpdate; - var typeMapping = CreateRelationalTypeMappingSource().GetMapping((IProperty)property); + var model = property.DeclaringType.Model.FinalizeModel(); + var typeMappingSource = CreateRelationalTypeMappingSource(model); + var runtimeProperty = model.FindEntityType(typeof(MyType))!.FindProperty("MyProp")!; + var typeMapping = typeMappingSource.GetMapping(runtimeProperty); Assert.Equal(DbType.Binary, typeMapping.DbType); Assert.Equal("rowversion", typeMapping.StoreType); @@ -1148,7 +1198,10 @@ public void Does_non_key_SQL_Server_required_rowversion_mapping() property.ValueGenerated = ValueGenerated.OnAddOrUpdate; property.IsNullable = false; - var typeMapping = CreateRelationalTypeMappingSource().GetMapping((IProperty)property); + var model = property.DeclaringType.Model.FinalizeModel(); + var typeMappingSource = CreateRelationalTypeMappingSource(model); + var runtimeProperty = model.FindEntityType(typeof(MyType))!.FindProperty("MyProp")!; + var typeMapping = typeMappingSource.GetMapping(runtimeProperty); Assert.Equal(DbType.Binary, typeMapping.DbType); Assert.Equal("rowversion", typeMapping.StoreType); @@ -1163,7 +1216,10 @@ public void Does_not_do_rowversion_mapping_for_non_computed_concurrency_tokens() var property = CreateEntityType().AddProperty("MyProp", typeof(byte[])); property.IsConcurrencyToken = true; - var typeMapping = CreateRelationalTypeMappingSource().GetMapping((IProperty)property); + var model = property.DeclaringType.Model.FinalizeModel(); + var typeMappingSource = CreateRelationalTypeMappingSource(model); + var runtimeProperty = model.FindEntityType(typeof(MyType))!.FindProperty("MyProp")!; + var typeMapping = typeMappingSource.GetMapping(runtimeProperty); Assert.Equal(DbType.Binary, typeMapping.DbType); Assert.False(typeMapping.IsFixedLength); @@ -1173,32 +1229,36 @@ public void Does_not_do_rowversion_mapping_for_non_computed_concurrency_tokens() [ConditionalFact] public void Does_default_mappings_for_sequence_types() { - Assert.Equal("int", CreateRelationalTypeMappingSource().GetMapping(typeof(int)).StoreType); - Assert.Equal("smallint", CreateRelationalTypeMappingSource().GetMapping(typeof(short)).StoreType); - Assert.Equal("bigint", CreateRelationalTypeMappingSource().GetMapping(typeof(long)).StoreType); - Assert.Equal("tinyint", CreateRelationalTypeMappingSource().GetMapping(typeof(byte)).StoreType); + var model = CreateModel(); + Assert.Equal("int", CreateRelationalTypeMappingSource(model).GetMapping(typeof(int)).StoreType); + Assert.Equal("smallint", CreateRelationalTypeMappingSource(model).GetMapping(typeof(short)).StoreType); + Assert.Equal("bigint", CreateRelationalTypeMappingSource(model).GetMapping(typeof(long)).StoreType); + Assert.Equal("tinyint", CreateRelationalTypeMappingSource(model).GetMapping(typeof(byte)).StoreType); } [ConditionalFact] public void Does_default_mappings_for_strings_and_byte_arrays() { - Assert.Equal("nvarchar(max)", CreateRelationalTypeMappingSource().GetMapping(typeof(string)).StoreType); - Assert.Equal("varbinary(max)", CreateRelationalTypeMappingSource().GetMapping(typeof(byte[])).StoreType); + var model = CreateModel(); + Assert.Equal("nvarchar(max)", CreateRelationalTypeMappingSource(model).GetMapping(typeof(string)).StoreType); + Assert.Equal("varbinary(max)", CreateRelationalTypeMappingSource(model).GetMapping(typeof(byte[])).StoreType); } [ConditionalFact] public void Does_default_mappings_for_values() { - Assert.Equal("nvarchar(max)", CreateRelationalTypeMappingSource().GetMappingForValue("Cheese").StoreType); - Assert.Equal("varbinary(max)", CreateRelationalTypeMappingSource().GetMappingForValue(new byte[1]).StoreType); - Assert.Equal("datetime2", CreateRelationalTypeMappingSource().GetMappingForValue(new DateTime()).StoreType); + var model = CreateModel(); + Assert.Equal("nvarchar(max)", CreateRelationalTypeMappingSource(model).GetMappingForValue("Cheese").StoreType); + Assert.Equal("varbinary(max)", CreateRelationalTypeMappingSource(model).GetMappingForValue(new byte[1]).StoreType); + Assert.Equal("datetime2", CreateRelationalTypeMappingSource(model).GetMappingForValue(new DateTime()).StoreType); } [ConditionalFact] public void Does_default_mappings_for_null_values() { - Assert.Equal("NULL", CreateRelationalTypeMappingSource().GetMappingForValue(null).StoreType); - Assert.Equal("NULL", CreateRelationalTypeMappingSource().GetMappingForValue(DBNull.Value).StoreType); + var model = CreateModel(); + Assert.Equal("NULL", CreateRelationalTypeMappingSource(model).GetMappingForValue(null).StoreType); + Assert.Equal("NULL", CreateRelationalTypeMappingSource(model).GetMappingForValue(DBNull.Value).StoreType); } [ConditionalFact] @@ -1206,17 +1266,19 @@ public void Throws_for_unrecognized_property_types() { var property = ((IMutableModel)new Model()).AddEntityType("Entity1") .AddProperty("Strange", typeof(object)); - var ex = Assert.Throws(() => CreateRelationalTypeMappingSource().GetMapping((IProperty)property)); + var model = CreateModel(); + + var ex = Assert.Throws(() => CreateRelationalTypeMappingSource(model).GetMapping((IProperty)property)); Assert.Equal( RelationalStrings.UnsupportedPropertyType("Entity1 (Dictionary)", "Strange", "object"), ex.Message); Assert.Equal( RelationalStrings.UnsupportedType("object"), - Assert.Throws(() => CreateRelationalTypeMappingSource().GetMapping(typeof(object))).Message); + Assert.Throws(() => CreateRelationalTypeMappingSource(model).GetMapping(typeof(object))).Message); Assert.Equal( RelationalStrings.UnsupportedStoreType("object"), - Assert.Throws(() => CreateRelationalTypeMappingSource().GetMapping("object")).Message); + Assert.Throws(() => CreateRelationalTypeMappingSource(model).GetMapping("object")).Message); } [ConditionalTheory] @@ -1270,7 +1332,7 @@ public void Throws_for_unrecognized_property_types() [InlineData("VARCHAR(max)", typeof(string), -1, false, false, "VARCHAR(max)")] public void Can_map_by_store_type(string storeType, Type type, int? size, bool unicode, bool fixedLength, string expectedType = null) { - var mapping = CreateRelationalTypeMappingSource().FindMapping(storeType); + var mapping = CreateRelationalTypeMappingSource(CreateModel()).FindMapping(storeType); Assert.Same(type, mapping.ClrType); Assert.Equal(size, mapping.Size); @@ -1287,7 +1349,7 @@ public void Can_map_by_store_type(string storeType, Type type, int? size, bool u [InlineData(typeof(TimeSpan), "time")] public void Can_map_by_clr_and_store_types(Type clrType, string storeType) { - var mapping = CreateRelationalTypeMappingSource().FindMapping(clrType, storeType); + var mapping = CreateRelationalTypeMappingSource(CreateModel()).FindMapping(clrType, storeType); Assert.Equal(storeType, mapping.StoreType); Assert.Same(clrType, mapping.ClrType); @@ -1316,7 +1378,10 @@ public void Can_map_string_base_type_name_and_size(string typeName) .HasMaxLength(2018) .Metadata; - var mapping = CreateRelationalTypeMappingSource().FindMapping((IProperty)property); + var model = property.DeclaringType.Model.FinalizeModel(); + var typeMappingSource = CreateRelationalTypeMappingSource(model); + var runtimeProperty = model.FindEntityType(typeof(StringCheese))!.FindProperty("StringWithSize")!; + var mapping = typeMappingSource.GetMapping(runtimeProperty); Assert.Same(typeof(string), mapping.ClrType); Assert.Equal(2018, mapping.Size); @@ -1348,7 +1413,10 @@ public void Can_map_collection_base_type_name_and_size(string typeName) .HasMaxLength(2018) .Metadata; - var mapping = CreateRelationalTypeMappingSource().FindMapping((IProperty)property); + var model = property.DeclaringType.Model.FinalizeModel(); + var typeMappingSource = CreateRelationalTypeMappingSource(model); + var runtimeProperty = model.FindEntityType(typeof(StringCheese))!.FindProperty("CollectionWithSize")!; + var mapping = typeMappingSource.GetMapping(runtimeProperty); Assert.Same(typeof(IEnumerable), mapping.ClrType); Assert.Equal(2018, mapping.Size); @@ -1376,7 +1444,10 @@ public void Can_map_datetime_base_type_columnType_with_precision(string typeName .HasColumnType(typeName) .Metadata; - var mapping = CreateRelationalTypeMappingSource().FindMapping((IProperty)property); + var model = property.DeclaringType.Model.FinalizeModel(); + var typeMappingSource = CreateRelationalTypeMappingSource(model); + var runtimeProperty = model.FindEntityType(typeof(VarTimeEntity))!.FindProperty("DateTimeWithPrecision")!; + var mapping = typeMappingSource.GetMapping(runtimeProperty); Assert.Same(typeof(DateTime), mapping.ClrType); Assert.Equal(precision, mapping.Precision); @@ -1402,7 +1473,10 @@ public void Can_map_datetime_base_type_precision(string typeName, int precision) .HasPrecision(precision) .Metadata; - var mapping = CreateRelationalTypeMappingSource().FindMapping((IProperty)property); + var model = property.DeclaringType.Model.FinalizeModel(); + var typeMappingSource = CreateRelationalTypeMappingSource(model); + var runtimeProperty = model.FindEntityType(typeof(VarTimeEntity))!.FindProperty("DateTimeWithPrecision")!; + var mapping = typeMappingSource.GetMapping(runtimeProperty); Assert.Same(typeof(DateTime), mapping.ClrType); Assert.Equal(precision, mapping.Precision); @@ -1429,7 +1503,10 @@ public void Can_map_datetimeoffset_base_type_columnType_with_precision(string ty .HasColumnType(typeName) .Metadata; - var mapping = CreateRelationalTypeMappingSource().FindMapping((IProperty)property); + var model = property.DeclaringType.Model.FinalizeModel(); + var typeMappingSource = CreateRelationalTypeMappingSource(model); + var runtimeProperty = model.FindEntityType(typeof(VarTimeEntity))!.FindProperty("DateTimeOffsetWithPrecision")!; + var mapping = typeMappingSource.GetMapping(runtimeProperty); Assert.Same(typeof(DateTimeOffset), mapping.ClrType); Assert.Equal(precision, mapping.Precision); @@ -1455,7 +1532,10 @@ public void Can_map_datetimeoffset_base_type_precision(string typeName, int prec .HasPrecision(precision) .Metadata; - var mapping = CreateRelationalTypeMappingSource().FindMapping((IProperty)property); + var model = property.DeclaringType.Model.FinalizeModel(); + var typeMappingSource = CreateRelationalTypeMappingSource(model); + var runtimeProperty = model.FindEntityType(typeof(VarTimeEntity))!.FindProperty("DateTimeOffsetWithPrecision")!; + var mapping = typeMappingSource.GetMapping(runtimeProperty); Assert.Same(typeof(DateTimeOffset), mapping.ClrType); Assert.Equal(precision, mapping.Precision); @@ -1482,7 +1562,10 @@ public void Can_map_time_base_type_columnType_with_precision(string typeName, in .HasColumnType(typeName) .Metadata; - var mapping = CreateRelationalTypeMappingSource().FindMapping((IProperty)property); + var model = property.DeclaringType.Model.FinalizeModel(); + var typeMappingSource = CreateRelationalTypeMappingSource(model); + var runtimeProperty = model.FindEntityType(typeof(VarTimeEntity))!.FindProperty("TimeSpanWithPrecision")!; + var mapping = typeMappingSource.GetMapping(runtimeProperty); Assert.Same(typeof(TimeSpan), mapping.ClrType); Assert.Equal(precision, mapping.Precision); @@ -1508,7 +1591,10 @@ public void Can_map_time_base_type_precision(string typeName, int precision) .HasPrecision(precision) .Metadata; - var mapping = CreateRelationalTypeMappingSource().FindMapping((IProperty)property); + var model = property.DeclaringType.Model.FinalizeModel(); + var typeMappingSource = CreateRelationalTypeMappingSource(model); + var runtimeProperty = model.FindEntityType(typeof(VarTimeEntity))!.FindProperty("TimeSpanWithPrecision")!; + var mapping = typeMappingSource.GetMapping(runtimeProperty); Assert.Same(typeof(TimeSpan), mapping.ClrType); Assert.Equal(precision, mapping.Precision); @@ -1538,7 +1624,10 @@ public void Can_map_binary_base_type_name_and_size(string typeName) .HasMaxLength(2018) .Metadata; - var mapping = CreateRelationalTypeMappingSource().FindMapping((IProperty)property); + var model = property.DeclaringType.Model.FinalizeModel(); + var typeMappingSource = CreateRelationalTypeMappingSource(model); + var runtimeProperty = model.FindEntityType(typeof(StringCheese))!.FindProperty("BinaryWithSize")!; + var mapping = typeMappingSource.GetMapping(runtimeProperty); Assert.Same(typeof(byte[]), mapping.ClrType); Assert.Equal(2018, mapping.Size); @@ -1558,7 +1647,7 @@ private class StringCheese public void Key_with_store_type_is_picked_up_by_FK() { var model = CreateModel(); - var mapper = CreateRelationalTypeMappingSource(); + var mapper = CreateRelationalTypeMappingSource(model); Assert.Equal( "money", @@ -1573,7 +1662,7 @@ public void Key_with_store_type_is_picked_up_by_FK() public void String_key_with_max_fixed_length_is_picked_up_by_FK() { var model = CreateModel(); - var mapper = CreateRelationalTypeMappingSource(); + var mapper = CreateRelationalTypeMappingSource(model); Assert.Equal( "nchar(200)", @@ -1588,7 +1677,7 @@ public void String_key_with_max_fixed_length_is_picked_up_by_FK() public void Binary_key_with_max_fixed_length_is_picked_up_by_FK() { var model = CreateModel(); - var mapper = CreateRelationalTypeMappingSource(); + var mapper = CreateRelationalTypeMappingSource(model); Assert.Equal( "binary(100)", @@ -1603,7 +1692,7 @@ public void Binary_key_with_max_fixed_length_is_picked_up_by_FK() public void String_key_with_unicode_is_picked_up_by_FK() { var model = CreateModel(); - var mapper = CreateRelationalTypeMappingSource(); + var mapper = CreateRelationalTypeMappingSource(model); Assert.Equal( "varchar(900)", @@ -1618,7 +1707,7 @@ public void String_key_with_unicode_is_picked_up_by_FK() public void Key_store_type_if_preferred_if_specified() { var model = CreateModel(); - var mapper = CreateRelationalTypeMappingSource(); + var mapper = CreateRelationalTypeMappingSource(model); Assert.Equal( "money", @@ -1633,7 +1722,7 @@ public void Key_store_type_if_preferred_if_specified() public void String_FK_max_length_is_preferred_if_specified() { var model = CreateModel(); - var mapper = CreateRelationalTypeMappingSource(); + var mapper = CreateRelationalTypeMappingSource(model); Assert.Equal( "nchar(200)", @@ -1648,7 +1737,7 @@ public void String_FK_max_length_is_preferred_if_specified() public void Binary_FK_max_length_is_preferred_if_specified() { var model = CreateModel(); - var mapper = CreateRelationalTypeMappingSource(); + var mapper = CreateRelationalTypeMappingSource(model); Assert.Equal( "binary(100)", @@ -1663,7 +1752,7 @@ public void Binary_FK_max_length_is_preferred_if_specified() public void String_FK_unicode_is_preferred_if_specified() { var model = CreateModel(); - var mapper = CreateRelationalTypeMappingSource(); + var mapper = CreateRelationalTypeMappingSource(model); Assert.Equal( "varchar(900)", @@ -1682,8 +1771,7 @@ public void Plugins_can_override_builtin_mappings() TestServiceFactory.Instance.Create() with { Plugins = new[] { new FakeTypeMappingSourcePlugin() } - }, - new SqlServerSingletonOptions()); + }); Assert.Equal("String", typeMappingSource.GetMapping("datetime2").ClrType.Name); } @@ -1694,11 +1782,16 @@ public RelationalTypeMapping FindMapping(in RelationalTypeMappingInfo mappingInf => new StringTypeMapping("datetime2", null); } - protected override IRelationalTypeMappingSource CreateRelationalTypeMappingSource() - => new SqlServerTypeMappingSource( + protected override IRelationalTypeMappingSource CreateRelationalTypeMappingSource(IModel model) + { + var typeMappingSource = new SqlServerTypeMappingSource( TestServiceFactory.Instance.Create(), - TestServiceFactory.Instance.Create(), - new SqlServerSingletonOptions()); + TestServiceFactory.Instance.Create()); + + model.ModelDependencies = new RuntimeModelDependencies(typeMappingSource, null!, null!); + + return typeMappingSource; + } private enum LongEnum : long { diff --git a/test/EFCore.SqlServer.Tests/Storage/SqlServerTypeMappingTest.cs b/test/EFCore.SqlServer.Tests/Storage/SqlServerTypeMappingTest.cs index 578b66bf203..c24eeac1338 100644 --- a/test/EFCore.SqlServer.Tests/Storage/SqlServerTypeMappingTest.cs +++ b/test/EFCore.SqlServer.Tests/Storage/SqlServerTypeMappingTest.cs @@ -135,7 +135,7 @@ public virtual void Create_and_clone_UDT_mapping_with_converter() 33, true); - var clone = (SqlServerUdtTypeMapping)mapping.Clone("", 66); + var clone = (SqlServerUdtTypeMapping)mapping.WithStoreTypeAndSize("", 66); Assert.NotSame(mapping, clone); Assert.Same(mapping.GetType(), clone.GetType()); @@ -158,7 +158,7 @@ public virtual void Create_and_clone_UDT_mapping_with_converter() Assert.Same(literalGenerator, clone.LiteralGenerator); var newConverter = CreateConverter(typeof(object)); - clone = (SqlServerUdtTypeMapping)mapping.Clone(newConverter); + clone = (SqlServerUdtTypeMapping)mapping.WithComposedConverter(newConverter); Assert.NotSame(mapping, clone); Assert.Same(mapping.GetType(), clone.GetType()); @@ -183,8 +183,7 @@ public virtual void Create_and_clone_UDT_mapping_with_converter() public static RelationalTypeMapping GetMapping(Type type) => new SqlServerTypeMappingSource( TestServiceFactory.Instance.Create(), - TestServiceFactory.Instance.Create(), - new SqlServerSingletonOptions()) + TestServiceFactory.Instance.Create()) .FindMapping(type); public override void ByteArray_literal_generated_correctly() @@ -418,8 +417,7 @@ public static RelationalTypeMapping GetMapping(string type) public static SqlServerTypeMappingSource GetTypeMappingSource() => new( TestServiceFactory.Instance.Create(), - TestServiceFactory.Instance.Create(), - new SqlServerSingletonOptions()); + TestServiceFactory.Instance.Create()); protected virtual void Test_GenerateCodeLiteral_helper( RelationalTypeMapping typeMapping, @@ -428,8 +426,7 @@ protected virtual void Test_GenerateCodeLiteral_helper( { var typeMappingSource = new SqlServerTypeMappingSource( TestServiceFactory.Instance.Create(), - TestServiceFactory.Instance.Create(), - new SqlServerSingletonOptions()); + TestServiceFactory.Instance.Create()); var csharpHelper = new CSharpHelper(typeMappingSource); diff --git a/test/EFCore.SqlServer.Tests/Update/SqlServerModificationCommandBatchFactoryTest.cs b/test/EFCore.SqlServer.Tests/Update/SqlServerModificationCommandBatchFactoryTest.cs index 32d2740ce45..63f478c8ab7 100644 --- a/test/EFCore.SqlServer.Tests/Update/SqlServerModificationCommandBatchFactoryTest.cs +++ b/test/EFCore.SqlServer.Tests/Update/SqlServerModificationCommandBatchFactoryTest.cs @@ -20,8 +20,7 @@ public void Uses_MaxBatchSize_specified_in_SqlServerOptionsExtension() var typeMapper = new SqlServerTypeMappingSource( TestServiceFactory.Instance.Create(), - TestServiceFactory.Instance.Create(), - new SqlServerSingletonOptions()); + TestServiceFactory.Instance.Create()); var logger = new FakeRelationalCommandDiagnosticsLogger(); @@ -57,8 +56,7 @@ public void MaxBatchSize_is_optional() var typeMapper = new SqlServerTypeMappingSource( TestServiceFactory.Instance.Create(), - TestServiceFactory.Instance.Create(), - new SqlServerSingletonOptions()); + TestServiceFactory.Instance.Create()); var logger = new FakeRelationalCommandDiagnosticsLogger(); diff --git a/test/EFCore.SqlServer.Tests/Update/SqlServerModificationCommandBatchTest.cs b/test/EFCore.SqlServer.Tests/Update/SqlServerModificationCommandBatchTest.cs index bf5ecd31201..6ec20c0f8fe 100644 --- a/test/EFCore.SqlServer.Tests/Update/SqlServerModificationCommandBatchTest.cs +++ b/test/EFCore.SqlServer.Tests/Update/SqlServerModificationCommandBatchTest.cs @@ -156,8 +156,7 @@ private static TestSqlServerModificationCommandBatch CreateBatch(int maxBatchSiz private static SqlServerTypeMappingSource CreateTypeMappingSource() => new( TestServiceFactory.Instance.Create(), - TestServiceFactory.Instance.Create(), - new SqlServerSingletonOptions()); + TestServiceFactory.Instance.Create()); private static INonTrackedModificationCommand CreateModificationCommand( string name, diff --git a/test/EFCore.Sqlite.FunctionalTests/BuiltInDataTypesSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/BuiltInDataTypesSqliteTest.cs index 08fc4c6e23a..a170ced2635 100644 --- a/test/EFCore.Sqlite.FunctionalTests/BuiltInDataTypesSqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/BuiltInDataTypesSqliteTest.cs @@ -1546,6 +1546,46 @@ SELECT hex("o"."Bytes") Assert.Equal(expectedResults, results); } + [ConditionalFact] + public virtual void Can_query_using_unhex_function() + { + using var context = CreateContext(); + + var results = context.Set() + .Select(e => EF.Functions.Unhex(EF.Functions.Hex(e.Bytes))).ToList(); + + AssertSql( +""" +SELECT unhex(hex("o"."Bytes")) +FROM "ObjectBackedDataTypes" AS "o" +"""); + + var expectedResults = context.Set().AsEnumerable() + .Select(e => e.Bytes).ToList(); + + Assert.Equal(expectedResults, results); + } + + [ConditionalFact] + public virtual void Can_query_using_unhex_function_with_ignore_chars() + { + using var context = CreateContext(); + + var results = context.Set() + .Select(e => EF.Functions.Unhex(EF.Functions.Hex(e.Bytes) + "!?", "!?")).ToList(); + + AssertSql( +""" +SELECT unhex(COALESCE(hex("o"."Bytes"), '') || '!?', '!?') +FROM "ObjectBackedDataTypes" AS "o" +"""); + + var expectedResults = context.Set().AsEnumerable() + .Select(e => e.Bytes).ToList(); + + Assert.Equal(expectedResults, results); + } + [ConditionalFact] public virtual void Can_query_using_substr_function() { @@ -1599,11 +1639,153 @@ SELECT CAST("b"."TestSignedByte" AS TEXT), CAST("b"."TestByte" AS TEXT), CAST("b } [ConditionalFact] - public virtual void Projecting_aritmetic_operations_on_decimals() + public virtual void Character_to_integer_conversion() + { + using var context = CreateContext(); + + context.Add( + new BuiltInDataTypes + { + Id = 290, + TestCharacter = '0' + }); + + context.SaveChanges(); + + var expected = context.Set() + .Where(e => e.Id == 290) + .AsEnumerable() + .Select( + b => new + { + Sbyte = (sbyte)b.TestCharacter, + Byte = (byte)b.TestCharacter, + Short = (short)b.TestCharacter, + Ushort = (ushort)b.TestCharacter, + Int = (int)b.TestCharacter, + Uint = (uint)b.TestCharacter, + Long = (long)b.TestCharacter, + Ulong = (ulong)b.TestCharacter + }) + .First(); + + Fixture.ListLoggerFactory.Clear(); + + var query = context.Set() + .Where(e => e.Id == 290) + .Select( + b => new + { + Sbyte = (sbyte)b.TestCharacter, + Byte = (byte)b.TestCharacter, + Short = (short)b.TestCharacter, + Ushort = (ushort)b.TestCharacter, + Int = (int)b.TestCharacter, + Uint = (uint)b.TestCharacter, + Long = (long)b.TestCharacter, + Ulong = (ulong)b.TestCharacter + }) + .ToList(); + + var actual = Assert.Single(query); + Assert.Equal(expected.Sbyte, actual.Sbyte); + Assert.Equal(expected.Byte, actual.Byte); + Assert.Equal(expected.Short, actual.Short); + Assert.Equal(expected.Ushort, actual.Ushort); + Assert.Equal(expected.Int, actual.Int); + Assert.Equal(expected.Uint, actual.Uint); + Assert.Equal(expected.Long, actual.Long); + Assert.Equal(expected.Ulong, actual.Ulong); + + AssertSql( +""" +SELECT unicode("b"."TestCharacter") AS "Sbyte", unicode("b"."TestCharacter") AS "Byte", unicode("b"."TestCharacter") AS "Short", unicode("b"."TestCharacter") AS "Ushort", unicode("b"."TestCharacter") AS "Int", unicode("b"."TestCharacter") AS "Uint", unicode("b"."TestCharacter") AS "Long", unicode("b"."TestCharacter") AS "Ulong" +FROM "BuiltInDataTypes" AS "b" +WHERE "b"."Id" = 290 +"""); + } + + [ConditionalFact] + public virtual void Integer_to_character_conversion() + { + using var context = CreateContext(); + + context.Add( + new BuiltInDataTypes + { + Id = 291, + TestSignedByte = 0, + TestByte = 0, + TestInt16 = 0, + TestUnsignedInt16 = 0, + TestInt32 = 0, + TestUnsignedInt32 = 0, + TestInt64 = 0, + TestUnsignedInt64 = 0 + }); + + context.SaveChanges(); + + var expected = context.Set() + .Where(e => e.Id == 291) + .AsEnumerable() + .Select( + b => new + { + Sbyte = (char)b.TestSignedByte, + Byte = (char)b.TestByte, + Short = (char)b.TestInt16, + Ushort = (char)b.TestUnsignedInt16, + Int = (char)b.TestInt32, + Uint = (char)b.TestUnsignedInt32, + Long = (char)b.TestInt64, + Ulong = (char)b.TestUnsignedInt64 + }) + .First(); + + Fixture.ListLoggerFactory.Clear(); + + var query = context.Set() + .Where(e => e.Id == 291) + .Select( + b => new + { + Sbyte = (char)b.TestSignedByte, + Byte = (char)b.TestByte, + Short = (char)b.TestInt16, + Ushort = (char)b.TestUnsignedInt16, + Int = (char)b.TestInt32, + Uint = (char)b.TestUnsignedInt32, + Long = (char)b.TestInt64, + Ulong = (char)b.TestUnsignedInt64 + }) + .ToList(); + + var actual = Assert.Single(query); + Assert.Equal(expected.Sbyte, actual.Sbyte); + Assert.Equal(expected.Byte, actual.Byte); + Assert.Equal(expected.Short, actual.Short); + Assert.Equal(expected.Ushort, actual.Ushort); + Assert.Equal(expected.Int, actual.Int); + Assert.Equal(expected.Uint, actual.Uint); + Assert.Equal(expected.Long, actual.Long); + Assert.Equal(expected.Ulong, actual.Ulong); + + AssertSql( +""" +SELECT char("b"."TestSignedByte") AS "Sbyte", char("b"."TestByte") AS "Byte", char("b"."TestInt16") AS "Short", char("b"."TestUnsignedInt16") AS "Ushort", char("b"."TestInt32") AS "Int", char("b"."TestUnsignedInt32") AS "Uint", char("b"."TestInt64") AS "Long", char("b"."TestUnsignedInt64") AS "Ulong" +FROM "BuiltInDataTypes" AS "b" +WHERE "b"."Id" = 291 +"""); + } + + [ConditionalFact] + public virtual void Projecting_arithmetic_operations_on_decimals() { using var context = CreateContext(); var expected = (from dt1 in context.Set().ToList() from dt2 in context.Set().ToList() + where dt2.TestDecimal != 0m orderby dt1.Id, dt2.Id select new { @@ -1618,6 +1800,7 @@ from dt2 in context.Set().ToList() var actual = (from dt1 in context.Set() from dt2 in context.Set() + where dt2.TestDecimal != 0m orderby dt1.Id, dt2.Id select new { @@ -1643,6 +1826,7 @@ from dt2 in context.Set() SELECT ef_add("b"."TestDecimal", "b0"."TestDecimal") AS "add", ef_add("b"."TestDecimal", ef_negate("b0"."TestDecimal")) AS "subtract", ef_multiply("b"."TestDecimal", "b0"."TestDecimal") AS "multiply", ef_divide("b"."TestDecimal", "b0"."TestDecimal") AS "divide", ef_negate("b"."TestDecimal") AS "negate" FROM "BuiltInDataTypes" AS "b" CROSS JOIN "BuiltInDataTypes" AS "b0" +WHERE "b0"."TestDecimal" <> '0.0' ORDER BY "b"."Id", "b0"."Id" """); } diff --git a/test/EFCore.Sqlite.FunctionalTests/EFCore.Sqlite.FunctionalTests.csproj b/test/EFCore.Sqlite.FunctionalTests/EFCore.Sqlite.FunctionalTests.csproj index d19dee23c1b..97a7c68c875 100644 --- a/test/EFCore.Sqlite.FunctionalTests/EFCore.Sqlite.FunctionalTests.csproj +++ b/test/EFCore.Sqlite.FunctionalTests/EFCore.Sqlite.FunctionalTests.csproj @@ -53,7 +53,7 @@ - + diff --git a/test/EFCore.Sqlite.FunctionalTests/JsonTypesSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/JsonTypesSqliteTest.cs index 59186dd2a28..a5b749768c7 100644 --- a/test/EFCore.Sqlite.FunctionalTests/JsonTypesSqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/JsonTypesSqliteTest.cs @@ -15,20 +15,14 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) => base.OnConfiguring(optionsBuilder.UseSqlite(b => b.UseNetTopologySuite())); public override void Can_read_write_binary_JSON_values(string value, string json) - { - // Cannot override since the base test contains [InlineData] attributes which still apply, and which contain data we need - // to override. See Can_read_write_binary_JSON_values_sqlite instead. - } - - [ConditionalTheory] - [InlineData("0,0,0,1", """{"Prop":"00000001"}""")] - [InlineData("255,255,255,255", """{"Prop":"FFFFFFFF"}""")] - [InlineData("", """{"Prop":""}""")] - [InlineData("1,2,3,4", """{"Prop":"01020304"}""")] - public virtual void Can_read_write_binary_JSON_values_sqlite(string value, string json) - => Can_read_and_write_JSON_value( - nameof(BytesType.Bytes), - value == "" ? Array.Empty() : value.Split(',').Select(e => byte.Parse(e)).ToArray(), json); + => base.Can_read_write_binary_JSON_values(value, value switch + { + "" => json, + "0,0,0,1" => """{"Prop":"00000001"}""", + "1,2,3,4" => """{"Prop":"01020304"}""", + "255,255,255,255" => """{"Prop":"FFFFFFFF"}""", + _ => throw new ArgumentOutOfRangeException(nameof(value), value, null) + }); [ConditionalFact] public override void Can_read_write_collection_of_decimal_JSON_values() diff --git a/test/EFCore.Sqlite.FunctionalTests/MaterializationInterceptionSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/MaterializationInterceptionSqliteTest.cs index 46dca829102..f2627186dd7 100644 --- a/test/EFCore.Sqlite.FunctionalTests/MaterializationInterceptionSqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/MaterializationInterceptionSqliteTest.cs @@ -3,6 +3,8 @@ #nullable enable +using Microsoft.EntityFrameworkCore.Sqlite.Internal; + namespace Microsoft.EntityFrameworkCore; public class MaterializationInterceptionSqliteTest : MaterializationInterceptionTestBase, @@ -13,6 +15,13 @@ public MaterializationInterceptionSqliteTest(MaterializationInterceptionSqliteFi { } + public override async Task Intercept_query_materialization_with_owned_types_projecting_collection(bool async) + => Assert.Equal( + SqliteStrings.ApplyNotSupported, + (await Assert.ThrowsAsync( + () => base.Intercept_query_materialization_with_owned_types_projecting_collection(async))) + .Message); + public class SqliteLibraryContext : LibraryContext { public SqliteLibraryContext(DbContextOptions options) diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/ComplexTypeQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/ComplexTypeQuerySqliteTest.cs index 314e58d2951..2cc7b167a66 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/ComplexTypeQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/ComplexTypeQuerySqliteTest.cs @@ -240,6 +240,13 @@ public override async Task Complex_type_equals_parameter(bool async) """); } + public override async Task Complex_type_equals_null(bool async) + { + await base.Complex_type_equals_null(async); + + AssertSql(); + } + public override async Task Subquery_over_complex_type(bool async) { await base.Subquery_over_complex_type(async); diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/JsonQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/JsonQuerySqliteTest.cs index 272558e3955..5f683ce48ef 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/JsonQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/JsonQuerySqliteTest.cs @@ -298,6 +298,83 @@ public override async Task Json_collection_skip_take_in_projection_with_json_ref () => base.Json_collection_skip_take_in_projection_with_json_reference_access_as_final_operation(async))) .Message); + public override async Task Json_collection_distinct_in_projection(bool async) + => Assert.Equal( + SqliteStrings.ApplyNotSupported, + (await Assert.ThrowsAsync( + () => base.Json_collection_distinct_in_projection(async))) + .Message); + + public override async Task Json_collection_filter_in_projection(bool async) + => Assert.Equal( + SqliteStrings.ApplyNotSupported, + (await Assert.ThrowsAsync( + () => base.Json_collection_filter_in_projection(async))) + .Message); + + public override async Task Json_collection_leaf_filter_in_projection(bool async) + => Assert.Equal( + SqliteStrings.ApplyNotSupported, + (await Assert.ThrowsAsync( + () => base.Json_collection_leaf_filter_in_projection(async))) + .Message); + + public override async Task Json_branch_collection_distinct_and_other_collection(bool async) + => Assert.Equal( + SqliteStrings.ApplyNotSupported, + (await Assert.ThrowsAsync( + () => base.Json_branch_collection_distinct_and_other_collection(async))) + .Message); + + public override async Task Json_leaf_collection_distinct_and_other_collection(bool async) + => Assert.Equal( + SqliteStrings.ApplyNotSupported, + (await Assert.ThrowsAsync( + () => base.Json_leaf_collection_distinct_and_other_collection(async))) + .Message); + + public override async Task Json_multiple_collection_projections(bool async) + => Assert.Equal( + SqliteStrings.ApplyNotSupported, + (await Assert.ThrowsAsync( + () => base.Json_multiple_collection_projections(async))) + .Message); + + public override async Task Json_collection_SelectMany(bool async) + => Assert.Equal( + SqliteStrings.ApplyNotSupported, + (await Assert.ThrowsAsync( + () => base.Json_collection_SelectMany(async))) + .Message); + + public override async Task Json_collection_skip_take_in_projection(bool async) + => Assert.Equal( + SqliteStrings.ApplyNotSupported, + (await Assert.ThrowsAsync( + () => base.Json_collection_skip_take_in_projection(async))) + .Message); + + public override async Task Json_nested_collection_anonymous_projection_in_projection(bool async) + => Assert.Equal( + SqliteStrings.ApplyNotSupported, + (await Assert.ThrowsAsync( + () => base.Json_nested_collection_anonymous_projection_in_projection(async))) + .Message); + + public override async Task Json_nested_collection_filter_in_projection(bool async) + => Assert.Equal( + SqliteStrings.ApplyNotSupported, + (await Assert.ThrowsAsync( + () => base.Json_nested_collection_filter_in_projection(async))) + .Message); + + public override async Task Json_nested_collection_SelectMany(bool async) + => Assert.Equal( + SqliteStrings.ApplyNotSupported, + (await Assert.ThrowsAsync( + () => base.Json_nested_collection_SelectMany(async))) + .Message); + public override async Task Json_collection_index_in_projection_using_untranslatable_client_method(bool async) { var message = (await Assert.ThrowsAsync( diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/PrimitiveCollectionsQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/PrimitiveCollectionsQuerySqliteTest.cs index 7c2009996f9..e1d0fcb1eee 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/PrimitiveCollectionsQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/PrimitiveCollectionsQuerySqliteTest.cs @@ -6,7 +6,7 @@ namespace Microsoft.EntityFrameworkCore.Query; -public class PrimitiveCollectionsQuerySqliteTest : PrimitiveCollectionsQueryTestBase< +public class PrimitiveCollectionsQuerySqliteTest : PrimitiveCollectionsQueryRelationalTestBase< PrimitiveCollectionsQuerySqliteTest.PrimitiveCollectionsQuerySqlServerFixture> { public PrimitiveCollectionsQuerySqliteTest(PrimitiveCollectionsQuerySqlServerFixture fixture, ITestOutputHelper testOutputHelper) @@ -858,9 +858,9 @@ public override async Task Column_collection_equality_parameter_collection(bool """); } - public override async Task Column_collection_Concat_parameter_collection_equality_inline_collection_not_supported(bool async) + public override async Task Column_collection_Concat_parameter_collection_equality_inline_collection(bool async) { - await base.Column_collection_Concat_parameter_collection_equality_inline_collection_not_supported(async); + await base.Column_collection_Concat_parameter_collection_equality_inline_collection(async); AssertSql(); } @@ -908,10 +908,9 @@ ORDER BY "i"."key" public override async Task Parameter_collection_in_subquery_Union_another_parameter_collection_as_compiled_query(bool async) { - var message = (await Assert.ThrowsAsync( - () => base.Parameter_collection_in_subquery_Union_another_parameter_collection_as_compiled_query(async))).Message; + await base.Parameter_collection_in_subquery_Union_another_parameter_collection_as_compiled_query(async); - Assert.Equal(RelationalStrings.SetOperationsRequireAtLeastOneSideWithValidTypeMapping("Union"), message); + AssertSql(); } public override async Task Parameter_collection_in_subquery_Union_column_collection_as_compiled_query(bool async) @@ -1056,23 +1055,23 @@ public override async Task Project_collection_of_datetimes_filtered(bool async) (await Assert.ThrowsAsync( () => base.Project_collection_of_datetimes_filtered(async))).Message); - public override async Task Project_collection_of_ints_with_paging(bool async) + public override async Task Project_collection_of_nullable_ints_with_paging(bool async) => Assert.Equal( SqliteStrings.ApplyNotSupported, (await Assert.ThrowsAsync( - () => base.Project_collection_of_ints_with_paging(async))).Message); + () => base.Project_collection_of_nullable_ints_with_paging(async))).Message); - public override async Task Project_collection_of_ints_with_paging2(bool async) + public override async Task Project_collection_of_nullable_ints_with_paging2(bool async) => Assert.Equal( SqliteStrings.ApplyNotSupported, (await Assert.ThrowsAsync( - () => base.Project_collection_of_ints_with_paging2(async))).Message); + () => base.Project_collection_of_nullable_ints_with_paging2(async))).Message); - public override async Task Project_collection_of_ints_with_paging3(bool async) + public override async Task Project_collection_of_nullable_ints_with_paging3(bool async) => Assert.Equal( SqliteStrings.ApplyNotSupported, (await Assert.ThrowsAsync( - () => base.Project_collection_of_ints_with_paging3(async))).Message); + () => base.Project_collection_of_nullable_ints_with_paging3(async))).Message); public override async Task Project_collection_of_ints_with_distinct(bool async) => Assert.Equal( diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/SpatialQuerySqliteFixture.cs b/test/EFCore.Sqlite.FunctionalTests/Query/SpatialQuerySqliteFixture.cs index 7febcaca4cf..3c72998a8aa 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/SpatialQuerySqliteFixture.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/SpatialQuerySqliteFixture.cs @@ -54,8 +54,8 @@ public ReplacementTypeMappingSource( protected override RelationalTypeMapping FindMapping(in RelationalTypeMappingInfo mappingInfo) => mappingInfo.ClrType == typeof(GeoPoint) ? ((RelationalTypeMapping)base.FindMapping(typeof(Point)) - .Clone(new GeoPointConverter())) - .Clone("geometry", null) + .WithComposedConverter(new GeoPointConverter())) + .WithStoreTypeAndSize("geometry", null) : base.FindMapping(mappingInfo); } } diff --git a/test/EFCore.Sqlite.FunctionalTests/SqliteApiConsistencyTest.cs b/test/EFCore.Sqlite.FunctionalTests/SqliteApiConsistencyTest.cs index 44f972be99a..73c68a7e986 100644 --- a/test/EFCore.Sqlite.FunctionalTests/SqliteApiConsistencyTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/SqliteApiConsistencyTest.cs @@ -26,7 +26,6 @@ public class SqliteApiConsistencyFixture : ApiConsistencyFixtureBase typeof(SqliteDbContextOptionsBuilderExtensions), typeof(SqliteDbContextOptionsBuilder), typeof(SqlitePropertyBuilderExtensions), - typeof(SqlitePrimitiveCollectionBuilderExtensions) }; public override @@ -54,8 +53,6 @@ public override protected override void Initialize() { MirrorTypes.Add(typeof(SqlitePropertyBuilderExtensions), typeof(SqliteComplexTypePropertyBuilderExtensions)); - MirrorTypes.Add(typeof(SqlitePrimitiveCollectionBuilderExtensions), typeof(SqlitePropertyBuilderExtensions)); - MirrorTypes.Add(typeof(SqliteComplexTypePrimitiveCollectionBuilderExtensions), typeof(SqliteComplexTypePropertyBuilderExtensions)); base.Initialize(); } diff --git a/test/EFCore.Sqlite.FunctionalTests/Update/JsonUpdateSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Update/JsonUpdateSqliteTest.cs index 62cc2c123c9..9ca8850efff 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Update/JsonUpdateSqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Update/JsonUpdateSqliteTest.cs @@ -1143,16 +1143,16 @@ public override async Task Edit_two_properties_on_same_entity_updates_the_entire AssertSql( """ -@p0='{"TestBoolean":false,"TestBooleanCollection":[true,false],"TestByte":25,"TestByteCollection":null,"TestCharacter":"h","TestCharacterCollection":["A","B","\u0022"],"TestDateTime":"2100-11-11 12:34:56","TestDateTimeCollection":["2000-01-01 12:34:56","3000-01-01 12:34:56"],"TestDateTimeOffset":"2200-11-11 12:34:56-05:00","TestDateTimeOffsetCollection":["2000-01-01 12:34:56-08:00"],"TestDecimal":"-123450.01","TestDecimalCollection":["-1234567890.01"],"TestDefaultString":"MyDefaultStringInCollection1","TestDefaultStringCollection":["S1","\u0022S2\u0022","S3"],"TestDouble":-1.2345,"TestDoubleCollection":[-1.23456789,1.23456789,0],"TestEnum":0,"TestEnumCollection":[0,2,-7],"TestEnumWithIntConverter":1,"TestEnumWithIntConverterCollection":[0,2,-7],"TestGuid":"00000000-0000-0000-0000-000000000000","TestGuidCollection":["12345678-1234-4321-7777-987654321000"],"TestInt16":-12,"TestInt16Collection":[-32768,0,32767],"TestInt32":32,"TestInt32Collection":[-2147483648,0,2147483647],"TestInt64":64,"TestInt64Collection":[-9223372036854775808,0,9223372036854775807],"TestMaxLengthString":"Baz","TestMaxLengthStringCollection":["S1","S2","S3"],"TestNullableEnum":0,"TestNullableEnumCollection":[0,null,2,-7],"TestNullableEnumWithConverterThatHandlesNulls":"Two","TestNullableEnumWithConverterThatHandlesNullsCollection":[0,null,-7],"TestNullableEnumWithIntConverter":2,"TestNullableEnumWithIntConverterCollection":[0,null,2,-7],"TestNullableInt32":90,"TestNullableInt32Collection":[null,-2147483648,0,null,2147483647,null],"TestSignedByte":-18,"TestSignedByteCollection":[-128,0,127],"TestSingle":-1.4,"TestSingleCollection":[-1.234,0,-1.234],"TestTimeSpan":"6:05:04.003","TestTimeSpanCollection":["10:09:08.007","-9:50:51.993"],"TestUnsignedInt16":12,"TestUnsignedInt16Collection":[0,0,65535],"TestUnsignedInt32":12345,"TestUnsignedInt32Collection":[0,0,4294967295],"TestUnsignedInt64":1234567867,"TestUnsignedInt64Collection":[0,0,18446744073709551615]}' (Nullable = false) (Size = 1951) -@p1='{"TestBoolean":true,"TestBooleanCollection":[true,false],"TestByte":255,"TestByteCollection":null,"TestCharacter":"a","TestCharacterCollection":["A","B","\u0022"],"TestDateTime":"2000-01-01 12:34:56","TestDateTimeCollection":["2000-01-01 12:34:56","3000-01-01 12:34:56"],"TestDateTimeOffset":"2000-01-01 12:34:56-08:00","TestDateTimeOffsetCollection":["2000-01-01 12:34:56-08:00"],"TestDecimal":"-1234567890.01","TestDecimalCollection":["-1234567890.01"],"TestDefaultString":"MyDefaultStringInReference1","TestDefaultStringCollection":["S1","\u0022S2\u0022","S3"],"TestDouble":-1.23456789,"TestDoubleCollection":[-1.23456789,1.23456789,0],"TestEnum":0,"TestEnumCollection":[0,2,-7],"TestEnumWithIntConverter":1,"TestEnumWithIntConverterCollection":[0,2,-7],"TestGuid":"12345678-1234-4321-7777-987654321000","TestGuidCollection":["12345678-1234-4321-7777-987654321000"],"TestInt16":-1234,"TestInt16Collection":[-32768,0,32767],"TestInt32":32,"TestInt32Collection":[-2147483648,0,2147483647],"TestInt64":64,"TestInt64Collection":[-9223372036854775808,0,9223372036854775807],"TestMaxLengthString":"Foo","TestMaxLengthStringCollection":["S1","S2","S3"],"TestNullableEnum":0,"TestNullableEnumCollection":[0,null,2,-7],"TestNullableEnumWithConverterThatHandlesNulls":"Three","TestNullableEnumWithConverterThatHandlesNullsCollection":[0,null,-7],"TestNullableEnumWithIntConverter":1,"TestNullableEnumWithIntConverterCollection":[0,null,2,-7],"TestNullableInt32":78,"TestNullableInt32Collection":[null,-2147483648,0,null,2147483647,null],"TestSignedByte":-128,"TestSignedByteCollection":[-128,0,127],"TestSingle":-1.234,"TestSingleCollection":[-1.234,0,-1.234],"TestTimeSpan":"10:09:08.007","TestTimeSpanCollection":["10:09:08.007","-9:50:51.993"],"TestUnsignedInt16":1234,"TestUnsignedInt16Collection":[0,0,65535],"TestUnsignedInt32":1234565789,"TestUnsignedInt32Collection":[0,0,4294967295],"TestUnsignedInt64":1234567890123456789,"TestUnsignedInt64Collection":[0,0,18446744073709551615]}' (Nullable = false) (Size = 1982) +@p0='{"TestBoolean":false,"TestBooleanCollection":[true,false],"TestByte":25,"TestByteCollection":null,"TestCharacter":"h","TestCharacterCollection":["A","B","\u0022"],"TestDateOnly":"2323-04-03","TestDateOnlyCollection":["3234-01-23","4331-01-21"],"TestDateTime":"2100-11-11 12:34:56","TestDateTimeCollection":["2000-01-01 12:34:56","3000-01-01 12:34:56"],"TestDateTimeOffset":"2200-11-11 12:34:56-05:00","TestDateTimeOffsetCollection":["2000-01-01 12:34:56-08:00"],"TestDecimal":"-123450.01","TestDecimalCollection":["-1234567890.01"],"TestDefaultString":"MyDefaultStringInCollection1","TestDefaultStringCollection":["S1","\u0022S2\u0022","S3"],"TestDouble":-1.2345,"TestDoubleCollection":[-1.23456789,1.23456789,0],"TestEnum":0,"TestEnumCollection":[0,2,-7],"TestEnumWithIntConverter":1,"TestEnumWithIntConverterCollection":[0,2,-7],"TestGuid":"00000000-0000-0000-0000-000000000000","TestGuidCollection":["12345678-1234-4321-7777-987654321000"],"TestInt16":-12,"TestInt16Collection":[-32768,0,32767],"TestInt32":32,"TestInt32Collection":[-2147483648,0,2147483647],"TestInt64":64,"TestInt64Collection":[-9223372036854775808,0,9223372036854775807],"TestMaxLengthString":"Baz","TestMaxLengthStringCollection":["S1","S2","S3"],"TestNullableEnum":0,"TestNullableEnumCollection":[0,null,2,-7],"TestNullableEnumWithConverterThatHandlesNulls":"Two","TestNullableEnumWithConverterThatHandlesNullsCollection":[0,null,-7],"TestNullableEnumWithIntConverter":2,"TestNullableEnumWithIntConverterCollection":[0,null,2,-7],"TestNullableInt32":90,"TestNullableInt32Collection":[null,-2147483648,0,null,2147483647,null],"TestSignedByte":-18,"TestSignedByteCollection":[-128,0,127],"TestSingle":-1.4,"TestSingleCollection":[-1.234,0,-1.234],"TestTimeOnly":"05:07:08.0000000","TestTimeOnlyCollection":["13:42:23.0000000","07:17:25.0000000"],"TestTimeSpan":"6:05:04.003","TestTimeSpanCollection":["10:09:08.007","-9:50:51.993"],"TestUnsignedInt16":12,"TestUnsignedInt16Collection":[0,0,65535],"TestUnsignedInt32":12345,"TestUnsignedInt32Collection":[0,0,4294967295],"TestUnsignedInt64":1234567867,"TestUnsignedInt64Collection":[0,0,18446744073709551615]}' (Nullable = false) (Size = 2131) +@p1='{"TestBoolean":true,"TestBooleanCollection":[true,false],"TestByte":255,"TestByteCollection":null,"TestCharacter":"a","TestCharacterCollection":["A","B","\u0022"],"TestDateOnly":"2023-10-10","TestDateOnlyCollection":["1234-01-23","4321-01-21"],"TestDateTime":"2000-01-01 12:34:56","TestDateTimeCollection":["2000-01-01 12:34:56","3000-01-01 12:34:56"],"TestDateTimeOffset":"2000-01-01 12:34:56-08:00","TestDateTimeOffsetCollection":["2000-01-01 12:34:56-08:00"],"TestDecimal":"-1234567890.01","TestDecimalCollection":["-1234567890.01"],"TestDefaultString":"MyDefaultStringInReference1","TestDefaultStringCollection":["S1","\u0022S2\u0022","S3"],"TestDouble":-1.23456789,"TestDoubleCollection":[-1.23456789,1.23456789,0],"TestEnum":0,"TestEnumCollection":[0,2,-7],"TestEnumWithIntConverter":1,"TestEnumWithIntConverterCollection":[0,2,-7],"TestGuid":"12345678-1234-4321-7777-987654321000","TestGuidCollection":["12345678-1234-4321-7777-987654321000"],"TestInt16":-1234,"TestInt16Collection":[-32768,0,32767],"TestInt32":32,"TestInt32Collection":[-2147483648,0,2147483647],"TestInt64":64,"TestInt64Collection":[-9223372036854775808,0,9223372036854775807],"TestMaxLengthString":"Foo","TestMaxLengthStringCollection":["S1","S2","S3"],"TestNullableEnum":0,"TestNullableEnumCollection":[0,null,2,-7],"TestNullableEnumWithConverterThatHandlesNulls":"Three","TestNullableEnumWithConverterThatHandlesNullsCollection":[0,null,-7],"TestNullableEnumWithIntConverter":1,"TestNullableEnumWithIntConverterCollection":[0,null,2,-7],"TestNullableInt32":78,"TestNullableInt32Collection":[null,-2147483648,0,null,2147483647,null],"TestSignedByte":-128,"TestSignedByteCollection":[-128,0,127],"TestSingle":-1.234,"TestSingleCollection":[-1.234,0,-1.234],"TestTimeOnly":"11:12:13.0000000","TestTimeOnlyCollection":["11:42:23.0000000","07:17:27.0000000"],"TestTimeSpan":"10:09:08.007","TestTimeSpanCollection":["10:09:08.007","-9:50:51.993"],"TestUnsignedInt16":1234,"TestUnsignedInt16Collection":[0,0,65535],"TestUnsignedInt32":1234565789,"TestUnsignedInt32Collection":[0,0,4294967295],"TestUnsignedInt64":1234567890123456789,"TestUnsignedInt64Collection":[0,0,18446744073709551615]}' (Nullable = false) (Size = 2162) @p2='1' UPDATE "JsonEntitiesAllTypes" SET "Collection" = json_set("Collection", '$[0]', json(@p0)), "Reference" = @p1 WHERE "Id" = @p2 RETURNING 1; """, - // - """ + // + """ SELECT "j"."Id", "j"."TestBooleanCollection", "j"."TestByteCollection", "j"."TestCharacterCollection", "j"."TestDateTimeCollection", "j"."TestDateTimeOffsetCollection", "j"."TestDecimalCollection", "j"."TestDefaultStringCollection", "j"."TestDoubleCollection", "j"."TestEnumCollection", "j"."TestEnumWithIntConverterCollection", "j"."TestGuidCollection", "j"."TestInt16Collection", "j"."TestInt32Collection", "j"."TestInt64Collection", "j"."TestMaxLengthStringCollection", "j"."TestNullableEnumCollection", "j"."TestNullableEnumWithConverterThatHandlesNullsCollection", "j"."TestNullableEnumWithIntConverterCollection", "j"."TestNullableInt32Collection", "j"."TestSignedByteCollection", "j"."TestSingleCollection", "j"."TestTimeSpanCollection", "j"."TestUnsignedInt16Collection", "j"."TestUnsignedInt32Collection", "j"."TestUnsignedInt64Collection", "j"."Collection", "j"."Reference" FROM "JsonEntitiesAllTypes" AS "j" WHERE "j"."Id" = 1 @@ -1694,6 +1694,52 @@ LIMIT 2 """); } + public override async Task Edit_single_property_collection_of_dateonly() + { + await base.Edit_single_property_collection_of_dateonly(); + + AssertSql( +""" +@p0='["3234-01-23","0001-01-07"]' (Nullable = false) (Size = 27) +@p1='["0001-01-07","4321-01-21"]' (Nullable = false) (Size = 27) +@p2='1' + +UPDATE "JsonEntitiesAllTypes" SET "Collection" = json_set("Collection", '$[0].TestDateOnlyCollection', json(@p0)), "Reference" = json_set("Reference", '$.TestDateOnlyCollection', json(@p1)) +WHERE "Id" = @p2 +RETURNING 1; +""", + // + """ +SELECT "j"."Id", "j"."TestBooleanCollection", "j"."TestByteCollection", "j"."TestCharacterCollection", "j"."TestDateTimeCollection", "j"."TestDateTimeOffsetCollection", "j"."TestDecimalCollection", "j"."TestDefaultStringCollection", "j"."TestDoubleCollection", "j"."TestEnumCollection", "j"."TestEnumWithIntConverterCollection", "j"."TestGuidCollection", "j"."TestInt16Collection", "j"."TestInt32Collection", "j"."TestInt64Collection", "j"."TestMaxLengthStringCollection", "j"."TestNullableEnumCollection", "j"."TestNullableEnumWithConverterThatHandlesNullsCollection", "j"."TestNullableEnumWithIntConverterCollection", "j"."TestNullableInt32Collection", "j"."TestSignedByteCollection", "j"."TestSingleCollection", "j"."TestTimeSpanCollection", "j"."TestUnsignedInt16Collection", "j"."TestUnsignedInt32Collection", "j"."TestUnsignedInt64Collection", "j"."Collection", "j"."Reference" +FROM "JsonEntitiesAllTypes" AS "j" +WHERE "j"."Id" = 1 +LIMIT 2 +"""); + } + + public override async Task Edit_single_property_collection_of_timeonly() + { + await base.Edit_single_property_collection_of_timeonly(); + + AssertSql( +""" +@p0='["13:42:23.0000000","01:01:07.0000000"]' (Nullable = false) (Size = 39) +@p1='["01:01:07.0000000","07:17:27.0000000"]' (Nullable = false) (Size = 39) +@p2='1' + +UPDATE "JsonEntitiesAllTypes" SET "Collection" = json_set("Collection", '$[0].TestTimeOnlyCollection', json(@p0)), "Reference" = json_set("Reference", '$.TestTimeOnlyCollection', json(@p1)) +WHERE "Id" = @p2 +RETURNING 1; +""", + // + """ +SELECT "j"."Id", "j"."TestBooleanCollection", "j"."TestByteCollection", "j"."TestCharacterCollection", "j"."TestDateTimeCollection", "j"."TestDateTimeOffsetCollection", "j"."TestDecimalCollection", "j"."TestDefaultStringCollection", "j"."TestDoubleCollection", "j"."TestEnumCollection", "j"."TestEnumWithIntConverterCollection", "j"."TestGuidCollection", "j"."TestInt16Collection", "j"."TestInt32Collection", "j"."TestInt64Collection", "j"."TestMaxLengthStringCollection", "j"."TestNullableEnumCollection", "j"."TestNullableEnumWithConverterThatHandlesNullsCollection", "j"."TestNullableEnumWithIntConverterCollection", "j"."TestNullableInt32Collection", "j"."TestSignedByteCollection", "j"."TestSingleCollection", "j"."TestTimeSpanCollection", "j"."TestUnsignedInt16Collection", "j"."TestUnsignedInt32Collection", "j"."TestUnsignedInt64Collection", "j"."Collection", "j"."Reference" +FROM "JsonEntitiesAllTypes" AS "j" +WHERE "j"."Id" = 1 +LIMIT 2 +"""); + } + public override async Task Edit_single_property_collection_of_uint16() { await base.Edit_single_property_collection_of_uint16(); diff --git a/test/EFCore.Sqlite.Tests/Storage/SqliteTypeMappingSourceTest.cs b/test/EFCore.Sqlite.Tests/Storage/SqliteTypeMappingSourceTest.cs index 9ad8c022c16..c89e2e42b53 100644 --- a/test/EFCore.Sqlite.Tests/Storage/SqliteTypeMappingSourceTest.cs +++ b/test/EFCore.Sqlite.Tests/Storage/SqliteTypeMappingSourceTest.cs @@ -117,7 +117,7 @@ public void Does_mappings_for_store_type(string storeType, Type clrType, DbType? { foreach (var type in new[] { storeType, storeType.ToLower(), storeType.Substring(0, 1) + storeType.Substring(1).ToLower() }) { - var mapping = CreateRelationalTypeMappingSource().FindMapping(type)!; + var mapping = CreateRelationalTypeMappingSource(CreateModel()).FindMapping(type)!; Assert.Equal(storeType.ToLower(), mapping.StoreType.ToLower()); Assert.Equal(Nullable.GetUnderlyingType(clrType) ?? clrType, mapping.ClrType); Assert.Equal(dbType, mapping.DbType); @@ -289,27 +289,29 @@ public void Does_mappings_for_both_store_and_CLR_type(string storeType, Type clr [ConditionalFact] public void Does_default_mappings_for_values() { - Assert.Equal("TEXT", CreateRelationalTypeMappingSource().GetMappingForValue("Cheese").StoreType); - Assert.Equal("BLOB", CreateRelationalTypeMappingSource().GetMappingForValue(new byte[1]).StoreType); - Assert.Equal("TEXT", CreateRelationalTypeMappingSource().GetMappingForValue(new DateTime()).StoreType); - Assert.Equal("INTEGER", CreateRelationalTypeMappingSource().GetMappingForValue(1).StoreType); - Assert.Equal("INTEGER", CreateRelationalTypeMappingSource().GetMappingForValue(1L).StoreType); - Assert.Equal("INTEGER", CreateRelationalTypeMappingSource().GetMappingForValue((byte)1).StoreType); - Assert.Equal("INTEGER", CreateRelationalTypeMappingSource().GetMappingForValue((short)1).StoreType); - Assert.Equal("INTEGER", CreateRelationalTypeMappingSource().GetMappingForValue((uint)1).StoreType); - Assert.Equal("INTEGER", CreateRelationalTypeMappingSource().GetMappingForValue((ulong)1).StoreType); - Assert.Equal("INTEGER", CreateRelationalTypeMappingSource().GetMappingForValue((sbyte)1).StoreType); - Assert.Equal("INTEGER", CreateRelationalTypeMappingSource().GetMappingForValue((ushort)1).StoreType); - Assert.Equal("TEXT", CreateRelationalTypeMappingSource().GetMappingForValue(1.0m).StoreType); - Assert.Equal("REAL", CreateRelationalTypeMappingSource().GetMappingForValue(1.0).StoreType); - Assert.Equal("REAL", CreateRelationalTypeMappingSource().GetMappingForValue(1.0f).StoreType); + var model = CreateModel(); + Assert.Equal("TEXT", CreateRelationalTypeMappingSource(model).GetMappingForValue("Cheese").StoreType); + Assert.Equal("BLOB", CreateRelationalTypeMappingSource(model).GetMappingForValue(new byte[1]).StoreType); + Assert.Equal("TEXT", CreateRelationalTypeMappingSource(model).GetMappingForValue(new DateTime()).StoreType); + Assert.Equal("INTEGER", CreateRelationalTypeMappingSource(model).GetMappingForValue(1).StoreType); + Assert.Equal("INTEGER", CreateRelationalTypeMappingSource(model).GetMappingForValue(1L).StoreType); + Assert.Equal("INTEGER", CreateRelationalTypeMappingSource(model).GetMappingForValue((byte)1).StoreType); + Assert.Equal("INTEGER", CreateRelationalTypeMappingSource(model).GetMappingForValue((short)1).StoreType); + Assert.Equal("INTEGER", CreateRelationalTypeMappingSource(model).GetMappingForValue((uint)1).StoreType); + Assert.Equal("INTEGER", CreateRelationalTypeMappingSource(model).GetMappingForValue((ulong)1).StoreType); + Assert.Equal("INTEGER", CreateRelationalTypeMappingSource(model).GetMappingForValue((sbyte)1).StoreType); + Assert.Equal("INTEGER", CreateRelationalTypeMappingSource(model).GetMappingForValue((ushort)1).StoreType); + Assert.Equal("TEXT", CreateRelationalTypeMappingSource(model).GetMappingForValue(1.0m).StoreType); + Assert.Equal("REAL", CreateRelationalTypeMappingSource(model).GetMappingForValue(1.0).StoreType); + Assert.Equal("REAL", CreateRelationalTypeMappingSource(model).GetMappingForValue(1.0f).StoreType); } [ConditionalFact] public void Does_default_mappings_for_null_values() { - Assert.Equal("NULL", CreateRelationalTypeMappingSource().GetMappingForValue(null).StoreType); - Assert.Equal("NULL", CreateRelationalTypeMappingSource().GetMappingForValue(DBNull.Value).StoreType); + var model = CreateModel(); + Assert.Equal("NULL", CreateRelationalTypeMappingSource(model).GetMappingForValue(null).StoreType); + Assert.Equal("NULL", CreateRelationalTypeMappingSource(model).GetMappingForValue(DBNull.Value).StoreType); } [ConditionalFact] @@ -317,13 +319,13 @@ public void Throws_for_unrecognized_property_types() { var property = ((IMutableModel)new Model()).AddEntityType("Entity1") .AddProperty("Strange", typeof(object)); - var ex = Assert.Throws(() => CreateRelationalTypeMappingSource().GetMapping((IProperty)property)); + var ex = Assert.Throws(() => CreateRelationalTypeMappingSource(CreateModel()).GetMapping((IProperty)property)); Assert.Equal( RelationalStrings.UnsupportedPropertyType("Entity1 (Dictionary)", "Strange", "object"), ex.Message); Assert.Equal( RelationalStrings.UnsupportedType("object"), - Assert.Throws(() => CreateRelationalTypeMappingSource().GetMapping(typeof(object))).Message); + Assert.Throws(() => CreateRelationalTypeMappingSource(CreateModel()).GetMapping(typeof(object))).Message); } [ConditionalFact] @@ -345,11 +347,17 @@ public RelationalTypeMapping FindMapping(in RelationalTypeMappingInfo mappingInf => new StringTypeMapping("datetime2", null); } - protected override IRelationalTypeMappingSource CreateRelationalTypeMappingSource() - => new SqliteTypeMappingSource( + protected override IRelationalTypeMappingSource CreateRelationalTypeMappingSource(IModel model) + { + var typeMappingSource = new SqliteTypeMappingSource( TestServiceFactory.Instance.Create(), TestServiceFactory.Instance.Create()); + model.ModelDependencies = new RuntimeModelDependencies(typeMappingSource, null!, null!); + + return typeMappingSource; + } + private enum LongEnum : long { } diff --git a/test/EFCore.Tests/ChangeTracking/Internal/ChangeDetectorTest.cs b/test/EFCore.Tests/ChangeTracking/Internal/ChangeDetectorTest.cs index 7b9c501aee6..7e4ff1dc769 100644 --- a/test/EFCore.Tests/ChangeTracking/Internal/ChangeDetectorTest.cs +++ b/test/EFCore.Tests/ChangeTracking/Internal/ChangeDetectorTest.cs @@ -311,12 +311,14 @@ public ConcreteTypeMapping(Type clrType, ValueConverter converter, ValueComparer { } - public override CoreTypeMapping Clone( + public override CoreTypeMapping WithComposedConverter( ValueConverter converter, ValueComparer comparer = null, + ValueComparer keyComparer = null, CoreTypeMapping elementMapping = null, JsonValueReaderWriter jsonValueReaderWriter = null) - => new ConcreteTypeMapping(Parameters.WithComposedConverter(converter, comparer, elementMapping, jsonValueReaderWriter)); + => new ConcreteTypeMapping(Parameters.WithComposedConverter( + converter, comparer, keyComparer, elementMapping, jsonValueReaderWriter)); protected override CoreTypeMapping Clone(CoreTypeMappingParameters parameters) => new ConcreteTypeMapping(parameters); diff --git a/test/EFCore.Tests/CollectionComparerTest.cs b/test/EFCore.Tests/CollectionComparerTest.cs index 871110d10fa..9e83db642ca 100644 --- a/test/EFCore.Tests/CollectionComparerTest.cs +++ b/test/EFCore.Tests/CollectionComparerTest.cs @@ -504,33 +504,33 @@ public void List_comparer_throws_when_used_with_non_list() var comparer = new ListComparer(new ValueComparer(favorStructuralComparisons: false)); Assert.Equal( - CoreStrings.BadListType("HashSet", "ListComparer", "IList"), + CoreStrings.BadListType("HashSet", "IList"), Assert.Throws(() => comparer.Equals(new List(), new HashSet())).Message); Assert.Equal( - CoreStrings.BadListType("HashSet", "ListComparer", "IList"), + CoreStrings.BadListType("HashSet", "IList"), Assert.Throws(() => comparer.Equals(new HashSet(), new List())).Message); Assert.Equal( - CoreStrings.BadListType("HashSet", "ListComparer", "IList"), + CoreStrings.BadListType("HashSet", "IList"), Assert.Throws(() => comparer.Snapshot(new HashSet())).Message); } [ConditionalFact] public void Nullable_list_comparer_throws_when_used_with_non_list() { - var comparer = new NullableValueTypeListComparer(new ValueComparer(favorStructuralComparisons: false)); + var comparer = new NullableValueTypeListComparer(new ValueComparer(favorStructuralComparisons: false)); Assert.Equal( - CoreStrings.BadListType("HashSet", "NullableValueTypeListComparer", "IList"), + CoreStrings.BadListType("HashSet", "IList"), Assert.Throws(() => comparer.Equals(new List(), new HashSet())).Message); Assert.Equal( - CoreStrings.BadListType("HashSet", "NullableValueTypeListComparer", "IList"), + CoreStrings.BadListType("HashSet", "IList"), Assert.Throws(() => comparer.Equals(new HashSet(), new List())).Message); Assert.Equal( - CoreStrings.BadListType("HashSet", "NullableValueTypeListComparer", "IList"), + CoreStrings.BadListType("HashSet", "IList"), Assert.Throws(() => comparer.Snapshot(new HashSet())).Message); } diff --git a/test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs b/test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs index 2d214c2c1c6..6d31ba320db 100644 --- a/test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs +++ b/test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs @@ -183,6 +183,27 @@ protected class WithCollectionConversion public string[] SomeStrings { get; set; } } + [ConditionalFact] + public virtual void Throws_when_mapping_concrete_sealed_type_that_does_not_implement_IList() + { + var modelBuilder = CreateConventionModelBuilder(); + + modelBuilder.Entity( + eb => + { + eb.Property(e => e.Id); + eb.PrimitiveCollection(e => e.SomeString); + }); + + VerifyError(CoreStrings.BadListType("string", "IList"), modelBuilder, sensitiveDataLoggingEnabled: false); + } + + protected class WithStringCollection + { + public int Id { get; set; } + public string SomeString { get; set; } + } + [ConditionalFact] public virtual void Ignores_binary_keys_and_strings_without_custom_comparer() { @@ -482,9 +503,7 @@ public virtual void Detects_identifying_relationship_cycle() modelBuilder.Entity().HasOne().WithOne().HasForeignKey(a => a.Id).HasPrincipalKey(b => b.Id).IsRequired(); modelBuilder.Entity().HasOne().WithOne().HasForeignKey(a => a.Id).HasPrincipalKey(b => b.Id).IsRequired(); - VerifyError( - CoreStrings.IdentifyingRelationshipCycle("A -> B -> C"), - modelBuilder); + VerifyError(CoreStrings.RelationshipCycle("B", "AId", "ValueConverter"), modelBuilder); } [ConditionalFact] diff --git a/test/EFCore.Tests/Metadata/Conventions/ConventionDispatcherTest.cs b/test/EFCore.Tests/Metadata/Conventions/ConventionDispatcherTest.cs index 177d4c9bbe4..19a3ef87f04 100644 --- a/test/EFCore.Tests/Metadata/Conventions/ConventionDispatcherTest.cs +++ b/test/EFCore.Tests/Metadata/Conventions/ConventionDispatcherTest.cs @@ -3689,12 +3689,12 @@ public void OnPropertyElementTypeChanged_calls_conventions_in_order(bool useBuil if (useBuilder) { - Assert.NotNull(propertyBuilder.ElementType(true, ConfigurationSource.Convention)); - elementType = (ElementType)propertyBuilder.Metadata.GetElementType()!; + Assert.NotNull(propertyBuilder.SetElementType(typeof(int), ConfigurationSource.Convention)); + elementType = propertyBuilder.Metadata.GetElementType()!; } else { - elementType = (ElementType)propertyBuilder.Metadata.ElementType(true, ConfigurationSource.Convention); + elementType = propertyBuilder.Metadata.SetElementType(typeof(int), ConfigurationSource.Convention); } if (useScope) @@ -3710,12 +3710,12 @@ public void OnPropertyElementTypeChanged_calls_conventions_in_order(bool useBuil if (useBuilder) { - Assert.Null(propertyBuilder.ElementType(true, ConfigurationSource.Convention)); - elementType = (ElementType)propertyBuilder.Metadata.GetElementType()!; + Assert.Null(propertyBuilder.SetElementType(typeof(int), ConfigurationSource.Convention)); + elementType = propertyBuilder.Metadata.GetElementType()!; } else { - elementType = (ElementType)propertyBuilder.Metadata.ElementType(true, ConfigurationSource.Convention); + elementType = propertyBuilder.Metadata.SetElementType(typeof(int), ConfigurationSource.Convention); } Assert.Equal(new (object, object)[] { (null, elementType) }, convention1.Calls); @@ -3724,11 +3724,11 @@ public void OnPropertyElementTypeChanged_calls_conventions_in_order(bool useBuil if (useBuilder) { - Assert.NotNull(propertyBuilder.ElementType(false, ConfigurationSource.Convention)); + Assert.NotNull(propertyBuilder.SetElementType(null, ConfigurationSource.Convention)); } else { - Assert.Null(propertyBuilder.Metadata.ElementType(false, ConfigurationSource.Convention)); + Assert.Null(propertyBuilder.Metadata.SetElementType(null, ConfigurationSource.Convention)); } Assert.Equal(new (object, object)[] { (null, elementType), (elementType, null) }, convention1.Calls); @@ -5069,7 +5069,7 @@ public void OnElementTypeAnnotationChanged_calls_conventions_in_order(bool useBu var builder = new InternalModelBuilder(new Model(conventions)); var elementTypeBuilder = builder.Entity(typeof(SpecialOrder), ConfigurationSource.Convention)! .Property(nameof(SpecialOrder.OrderIds), ConfigurationSource.Convention)! - .ElementType(true, ConfigurationSource.Convention)!; + .SetElementType(typeof(int), ConfigurationSource.Convention)!; var scope = useScope ? builder.Metadata.ConventionDispatcher.DelayConventions() : null; @@ -5174,7 +5174,7 @@ public void OnElementTypeNullabilityChanged_calls_conventions_in_order(bool useB var builder = new InternalModelBuilder(model); var elementTypeBuilder = builder.Entity(typeof(SpecialOrder), ConfigurationSource.Convention)! .Property(nameof(SpecialOrder.Notes), ConfigurationSource.Convention)! - .ElementType(true, ConfigurationSource.Convention)!; + .SetElementType(typeof(string), ConfigurationSource.Convention)!; if (useBuilder) { diff --git a/test/EFCore.Tests/Metadata/Internal/ClrPropertyGetterFactoryTest.cs b/test/EFCore.Tests/Metadata/Internal/ClrPropertyGetterFactoryTest.cs index a0fe8d5f71d..69125e024cb 100644 --- a/test/EFCore.Tests/Metadata/Internal/ClrPropertyGetterFactoryTest.cs +++ b/test/EFCore.Tests/Metadata/Internal/ClrPropertyGetterFactoryTest.cs @@ -19,16 +19,16 @@ public void Property_is_returned_if_it_implements_IClrPropertyGetter() private class FakeProperty : Annotatable, IProperty, IClrPropertyGetter { - public object GetClrValue(object entity) + public object GetClrValueUsingContainingEntity(object entity) => throw new NotImplementedException(); - public bool HasSentinel(object entity) + public bool HasSentinelUsingContainingEntity(object entity) => throw new NotImplementedException(); - public object GetStructuralTypeClrValue(object complexObject) + public object GetClrValue(object structuralObject) => throw new NotImplementedException(); - public bool HasStructuralTypeSentinelValue(object complexObject) + public bool HasSentinel(object structuralObject) => throw new NotImplementedException(); public IEnumerable GetContainingForeignKeys() @@ -88,6 +88,11 @@ public ValueComparer GetProviderValueComparer() public JsonValueReaderWriter GetJsonValueReaderWriter() => throw new NotImplementedException(); + IReadOnlyElementType IReadOnlyProperty.GetElementType() + => GetElementType(); + + public bool IsPrimitiveCollection { get; } + public IElementType GetElementType() => throw new NotImplementedException(); @@ -142,14 +147,14 @@ public void Delegate_getter_is_returned_for_IProperty_property() var idProperty = model.FindEntityType(typeof(Customer)).FindProperty(nameof(Customer.Id)); Assert.Equal( - 7, new ClrPropertyGetterFactory().Create(idProperty).GetClrValue( + 7, new ClrPropertyGetterFactory().Create(idProperty).GetClrValueUsingContainingEntity( new Customer { Id = 7 })); } [ConditionalFact] public void Delegate_getter_is_returned_for_property_info() => Assert.Equal( - 7, new ClrPropertyGetterFactory().Create(typeof(Customer).GetAnyProperty("Id")).GetClrValue( + 7, new ClrPropertyGetterFactory().Create(typeof(Customer).GetAnyProperty("Id")).GetClrValueUsingContainingEntity( new Customer { Id = 7 })); [ConditionalFact] @@ -162,7 +167,7 @@ public void Delegate_getter_is_returned_for_IProperty_struct_property() Assert.Equal( new Fuel(1.0), - new ClrPropertyGetterFactory().Create((IPropertyBase)fuelProperty).GetClrValue( + new ClrPropertyGetterFactory().Create((IPropertyBase)fuelProperty).GetClrValueUsingContainingEntity( new Customer { Id = 7, Fuel = new Fuel(1.0) })); } @@ -170,7 +175,7 @@ public void Delegate_getter_is_returned_for_IProperty_struct_property() public void Delegate_getter_is_returned_for_struct_property_info() => Assert.Equal( new Fuel(1.0), - new ClrPropertyGetterFactory().Create(typeof(Customer).GetAnyProperty("Fuel")).GetClrValue( + new ClrPropertyGetterFactory().Create(typeof(Customer).GetAnyProperty("Fuel")).GetClrValueUsingContainingEntity( new Customer { Id = 7, Fuel = new Fuel(1.0) })); [ConditionalFact] @@ -183,8 +188,8 @@ public void Delegate_getter_is_returned_for_index_property() modelBuilder.FinalizeModel(); Assert.Equal( - "ValueA", new ClrPropertyGetterFactory().Create((IPropertyBase)propertyA).GetClrValue(new IndexedClass { Id = 7 })); - Assert.Equal(123, new ClrPropertyGetterFactory().Create((IPropertyBase)propertyB).GetClrValue(new IndexedClass { Id = 7 })); + "ValueA", new ClrPropertyGetterFactory().Create((IPropertyBase)propertyA).GetClrValueUsingContainingEntity(new IndexedClass { Id = 7 })); + Assert.Equal(123, new ClrPropertyGetterFactory().Create((IPropertyBase)propertyB).GetClrValueUsingContainingEntity(new IndexedClass { Id = 7 })); } [ConditionalFact] @@ -205,11 +210,11 @@ public void Delegate_getter_is_returned_for_IProperty_complex_property() .ComplexType.FindProperty(nameof(Fuel.Volume))!; Assert.Equal( - 10.0, new ClrPropertyGetterFactory().Create(volumeProperty).GetClrValue( + 10.0, new ClrPropertyGetterFactory().Create(volumeProperty).GetClrValueUsingContainingEntity( new Customer { Id = 7, Fuel = new Fuel(10.0)})); Assert.Equal( - 10.0, new ClrPropertyGetterFactory().Create(volumeProperty).GetStructuralTypeClrValue(new Fuel(10.0))); + 10.0, new ClrPropertyGetterFactory().Create(volumeProperty).GetClrValue(new Fuel(10.0))); } private static TestHelpers.TestModelBuilder CreateModelBuilder() diff --git a/test/EFCore.Tests/Metadata/Internal/ClrPropertySetterFactoryTest.cs b/test/EFCore.Tests/Metadata/Internal/ClrPropertySetterFactoryTest.cs index 482694c17f7..ea2439a7ab8 100644 --- a/test/EFCore.Tests/Metadata/Internal/ClrPropertySetterFactoryTest.cs +++ b/test/EFCore.Tests/Metadata/Internal/ClrPropertySetterFactoryTest.cs @@ -100,6 +100,11 @@ public ValueComparer GetProviderValueComparer() public JsonValueReaderWriter GetJsonValueReaderWriter() => throw new NotImplementedException(); + IReadOnlyElementType IReadOnlyProperty.GetElementType() + => GetElementType(); + + public bool IsPrimitiveCollection { get; } + public IElementType GetElementType() => throw new NotImplementedException(); diff --git a/test/EFCore.Tests/Metadata/Internal/PropertyTest.cs b/test/EFCore.Tests/Metadata/Internal/PropertyTest.cs index 0f1c2fc2ebc..4c3cb76eeee 100644 --- a/test/EFCore.Tests/Metadata/Internal/PropertyTest.cs +++ b/test/EFCore.Tests/Metadata/Internal/PropertyTest.cs @@ -531,6 +531,42 @@ public void Throws_when_JsonValueReaderWriter_instance_cannot_be_created(Type ty () => property.GetJsonValueReaderWriter()).Message); } + [ConditionalFact] + public void Can_set_element_type_for_primitive_collection() + { + var model = CreateModel(); + var entityType = model.AddEntityType(typeof(object)); + var property = entityType.AddProperty("Random", typeof(IList)); + property.SetElementType(typeof(int)); + + Assert.Equal(typeof(int), property.GetElementType()!.ClrType); + Assert.True(property.IsPrimitiveCollection); + } + + [ConditionalFact] + public void Can_set_derived_element_type_for_primitive_collection() + { + var model = CreateModel(); + var entityType = model.AddEntityType(typeof(object)); + var property = entityType.AddProperty("Random", typeof(IList)); + property.SetElementType(typeof(int)); + + Assert.Equal(typeof(int), property.GetElementType()!.ClrType); + Assert.True(property.IsPrimitiveCollection); + } + + [ConditionalFact] + public void Can_set_element_type_for_non_primitive_collection() + { + var model = CreateModel(); + var entityType = model.AddEntityType(typeof(object)); + var property = entityType.AddProperty("Random", typeof(Random)); + property.SetElementType(typeof(int)); + + Assert.Equal(typeof(int), property.GetElementType()!.ClrType); + Assert.False(property.IsPrimitiveCollection); + } + private class SimpleJasonValueReaderWriter : JsonValueReaderWriter { public override string FromJsonTyped(ref Utf8JsonReaderManager manager, object existingObject = null) diff --git a/test/EFCore.Tests/ModelBuilding/ComplexTypeTestBase.cs b/test/EFCore.Tests/ModelBuilding/ComplexTypeTestBase.cs index caaf6931743..9264db0504f 100644 --- a/test/EFCore.Tests/ModelBuilding/ComplexTypeTestBase.cs +++ b/test/EFCore.Tests/ModelBuilding/ComplexTypeTestBase.cs @@ -39,7 +39,7 @@ public virtual void Can_set_complex_property_annotation() AlternateKey (Guid) Required Id (int) Required Name (string) - Notes (List)", complexProperty.ToDebugString(), ignoreLineEndingDifferences: true); + Notes (List) Element type: string", complexProperty.ToDebugString(), ignoreLineEndingDifferences: true); } [ConditionalFact] diff --git a/test/EFCore.Tests/ModelBuilding/TestModel.cs b/test/EFCore.Tests/ModelBuilding/TestModel.cs index 037a7e5f685..8aa6a51e038 100644 --- a/test/EFCore.Tests/ModelBuilding/TestModel.cs +++ b/test/EFCore.Tests/ModelBuilding/TestModel.cs @@ -894,9 +894,13 @@ protected interface IReplaceable int Property { get; set; } } - protected class ComplexProperties + protected class ComplexPropertiesBase { public int Id { get; set; } + } + + protected class ComplexProperties : ComplexPropertiesBase + { public required Customer Customer { get; set; } public required DoubleProperty DoubleProperty { get; set; } public required IndexedClass IndexedClass { get; set; } diff --git a/test/EFCore.TrimmingTests/EFCore.TrimmingTests.csproj b/test/EFCore.TrimmingTests/EFCore.TrimmingTests.csproj index a3982b8b516..bacc38a4e78 100644 --- a/test/EFCore.TrimmingTests/EFCore.TrimmingTests.csproj +++ b/test/EFCore.TrimmingTests/EFCore.TrimmingTests.csproj @@ -18,7 +18,7 @@ - + diff --git a/test/Microsoft.Data.Sqlite.Tests/Microsoft.Data.Sqlite.Tests.csproj b/test/Microsoft.Data.Sqlite.Tests/Microsoft.Data.Sqlite.Tests.csproj index efb3a50a1a4..2d4105b10d2 100644 --- a/test/Microsoft.Data.Sqlite.Tests/Microsoft.Data.Sqlite.Tests.csproj +++ b/test/Microsoft.Data.Sqlite.Tests/Microsoft.Data.Sqlite.Tests.csproj @@ -11,7 +11,7 @@ - + diff --git a/test/Microsoft.Data.Sqlite.Tests/Microsoft.Data.Sqlite.e_sqlcipher.Tests.csproj b/test/Microsoft.Data.Sqlite.Tests/Microsoft.Data.Sqlite.e_sqlcipher.Tests.csproj index 3f822ab6813..71c22ec4806 100644 --- a/test/Microsoft.Data.Sqlite.Tests/Microsoft.Data.Sqlite.e_sqlcipher.Tests.csproj +++ b/test/Microsoft.Data.Sqlite.Tests/Microsoft.Data.Sqlite.e_sqlcipher.Tests.csproj @@ -11,7 +11,7 @@ - + diff --git a/test/Microsoft.Data.Sqlite.Tests/Microsoft.Data.Sqlite.sqlite3.Tests.csproj b/test/Microsoft.Data.Sqlite.Tests/Microsoft.Data.Sqlite.sqlite3.Tests.csproj index cb4e5cf5976..26e733f085e 100644 --- a/test/Microsoft.Data.Sqlite.Tests/Microsoft.Data.Sqlite.sqlite3.Tests.csproj +++ b/test/Microsoft.Data.Sqlite.Tests/Microsoft.Data.Sqlite.sqlite3.Tests.csproj @@ -11,7 +11,7 @@ - + diff --git a/test/Microsoft.Data.Sqlite.Tests/Microsoft.Data.Sqlite.winsqlite3.Tests.csproj b/test/Microsoft.Data.Sqlite.Tests/Microsoft.Data.Sqlite.winsqlite3.Tests.csproj index 4db547295fa..7a8b5f7933a 100644 --- a/test/Microsoft.Data.Sqlite.Tests/Microsoft.Data.Sqlite.winsqlite3.Tests.csproj +++ b/test/Microsoft.Data.Sqlite.Tests/Microsoft.Data.Sqlite.winsqlite3.Tests.csproj @@ -11,7 +11,7 @@ - + diff --git a/test/Microsoft.Data.Sqlite.Tests/SqliteConnectionTest.cs b/test/Microsoft.Data.Sqlite.Tests/SqliteConnectionTest.cs index 9a4162de41a..b9557052b60 100644 --- a/test/Microsoft.Data.Sqlite.Tests/SqliteConnectionTest.cs +++ b/test/Microsoft.Data.Sqlite.Tests/SqliteConnectionTest.cs @@ -850,6 +850,65 @@ public void CreateAggregate_works() Assert.Equal("AX1Z", result); } + [Fact] + public void CreateAggregate_works_called_twice() + { + using var connection = new SqliteConnection("Data Source=:memory:"); + connection.Open(); + connection.ExecuteNonQuery("CREATE TABLE dual2 (dummy1, dummy2); INSERT INTO dual2 (dummy1, dummy2) VALUES ('X', 'Y');"); + connection.CreateAggregate( + "test", + "A", + (string a, string x, string y) => a + x + y, + a => a + "Z"); + + var result = connection.ExecuteScalar("SELECT test(dummy1, dummy2) FROM dual2;"); + Assert.Equal("AXYZ", result); + + result = connection.ExecuteScalar("SELECT test(dummy1, dummy2) FROM dual2;"); + Assert.Equal("AXYZ", result); + } + + [Fact] + public void CreateAggregate_works_called_twice_in_same_query() + { + using var connection = new SqliteConnection("Data Source=:memory:"); + connection.Open(); + connection.ExecuteNonQuery("CREATE TABLE dual2 (dummy1, dummy2); INSERT INTO dual2 (dummy1, dummy2) VALUES ('X', 'Y');"); + connection.CreateAggregate( + "test", + "A", + (string a, string x, string y) => a + x + y, + a => a + "Z"); + + using (var reader = connection.ExecuteReader("SELECT test(dummy1, dummy2), test(dummy2, dummy1) FROM dual2;")) + { + Assert.True(reader.Read()); + + Assert.Equal("AXYZ", reader.GetString(0)); + Assert.Equal("AYXZ", reader.GetString(1)); + + Assert.False(reader.Read()); + } + } + + [Fact] + public void CreateAggregate_works_when_no_rows() + { + using var connection = new SqliteConnection("Data Source=:memory:"); + connection.Open(); + connection.ExecuteNonQuery("CREATE TABLE dual2 (dummy1, dummy2);"); + connection.CreateAggregate( + "test", + "A", + (string a, string x, string y) => a + x + y, + a => a + "Z"); + + var result = connection.ExecuteScalar("SELECT test(dummy1, dummy2) FROM dual2;"); + + Assert.Equal("AZ", result); + } + [Fact] public void CreateAggregate_works_when_params() {