diff --git a/src/EFCore.Relational/Design/AnnotationCodeGenerator.cs b/src/EFCore.Relational/Design/AnnotationCodeGenerator.cs index 9f72d0b44c4..f77bf697d50 100644 --- a/src/EFCore.Relational/Design/AnnotationCodeGenerator.cs +++ b/src/EFCore.Relational/Design/AnnotationCodeGenerator.cs @@ -243,6 +243,18 @@ public virtual IReadOnlyList GenerateFluentApiCalls( #pragma warning restore CS0618 } + if (annotations.TryGetValue(RelationalAnnotationNames.ContainerColumnType, out var containerColumnTypeAnnotation) + && containerColumnTypeAnnotation is { Value: string containerColumnType } + && entityType.IsOwned()) + { + methodCallCodeFragments.Add( + new MethodCallCodeFragment( + nameof(RelationalOwnedNavigationBuilderExtensions.HasColumnType), + containerColumnType)); + + annotations.Remove(RelationalAnnotationNames.ContainerColumnType); + } + methodCallCodeFragments.AddRange(GenerateFluentApiCallsHelper(entityType, annotations, GenerateFluentApi)); return methodCallCodeFragments; diff --git a/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs b/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs index ff816843742..93beae7be86 100644 --- a/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs +++ b/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs @@ -1606,9 +1606,46 @@ public static void SetContainerColumnName(this IMutableEntityType entityType, st /// The entity type to get the container column name for. /// The container column name to which the entity type is mapped. public static string? GetContainerColumnName(this IReadOnlyEntityType entityType) - => entityType.FindAnnotation(RelationalAnnotationNames.ContainerColumnName)?.Value is string columnName - ? columnName - : (entityType.FindOwnership()?.PrincipalEntityType.GetContainerColumnName()); + => entityType.FindAnnotation(RelationalAnnotationNames.ContainerColumnName)?.Value as string + ?? entityType.FindOwnership()?.PrincipalEntityType.GetContainerColumnName(); + + /// + /// Sets the column type to use for the container column to which the entity type is mapped. + /// + /// The entity type. + /// The database column type. + public static void SetContainerColumnType(this IMutableEntityType entityType, string? columnType) + => entityType.SetOrRemoveAnnotation(RelationalAnnotationNames.ContainerColumnType, columnType); + + /// + /// Sets the column type to use for the container column to which the entity type is mapped. + /// + /// The entity type. + /// The database column type. + /// Indicates whether the configuration was specified using a data annotation. + /// The configured value. + public static string? SetContainerColumnType( + this IConventionEntityType entityType, + string? columnType, + bool fromDataAnnotation = false) + => (string?)entityType.SetAnnotation(RelationalAnnotationNames.ContainerColumnType, columnType, fromDataAnnotation)?.Value; + + /// + /// Gets the for the container column type. + /// + /// The entity type. + /// The . + public static ConfigurationSource? GetContainerColumnTypeConfigurationSource(this IConventionEntityType entityType) + => entityType.FindAnnotation(RelationalAnnotationNames.ContainerColumnType) + ?.GetConfigurationSource(); + + /// + /// Gets the column type to use for the container column to which the entity type is mapped. + /// + /// The entity type. + /// The database column type. + public static string? GetContainerColumnType(this IReadOnlyEntityType entityType) + => entityType.FindAnnotation(RelationalAnnotationNames.ContainerColumnType)?.Value as string; /// /// Sets the type mapping for the container column to which the entity type is mapped. diff --git a/src/EFCore.Relational/Extensions/RelationalOwnedNavigationBuilderExtensions.cs b/src/EFCore.Relational/Extensions/RelationalOwnedNavigationBuilderExtensions.cs index 0f0329748a5..17952095f08 100644 --- a/src/EFCore.Relational/Extensions/RelationalOwnedNavigationBuilderExtensions.cs +++ b/src/EFCore.Relational/Extensions/RelationalOwnedNavigationBuilderExtensions.cs @@ -23,12 +23,7 @@ public static class RelationalOwnedNavigationBuilderExtensions /// The builder for the owned navigation being configured. /// The same builder instance so that multiple calls can be chained. public static OwnedNavigationBuilder ToJson(this OwnedNavigationBuilder builder) - { - var navigationName = builder.Metadata.GetNavigation(pointsToPrincipal: false)!.Name; - builder.ToJson(navigationName); - - return builder; - } + => builder.ToJson(builder.Metadata.GetNavigation(pointsToPrincipal: false)!.Name); /// /// Configures a relationship where this entity type and the entities that it owns are mapped to a JSON column in the database. @@ -45,12 +40,7 @@ public static OwnedNavigationBuilder ToJson builder) where TOwnerEntity : class where TDependentEntity : class - { - var navigationName = builder.Metadata.GetNavigation(pointsToPrincipal: false)!.Name; - builder.ToJson(navigationName); - - return builder; - } + => (OwnedNavigationBuilder)((OwnedNavigationBuilder)builder).ToJson(); /// /// Configures a relationship where this entity type and the entities that it owns are mapped to a JSON column in the database. @@ -68,11 +58,7 @@ public static OwnedNavigationBuilder ToJson (OwnedNavigationBuilder)((OwnedNavigationBuilder)builder).ToJson(jsonColumnName); /// /// Configures a relationship where this entity type and the entities that it owns are mapped to a JSON column in the database. @@ -94,6 +80,40 @@ public static OwnedNavigationBuilder ToJson( return builder; } + /// + /// Set the relational database column type to be used to store the document represented by this owned entity. + /// + /// + /// This method should only be specified for the outer-most owned entity in the given ownership structure and + /// only when mapping the column to a database document type. + /// + /// The builder for the owned navigation being configured. + /// The database type for the column, or to use the database default. + /// The same builder instance so that multiple calls can be chained. + public static OwnedNavigationBuilder HasColumnType( + this OwnedNavigationBuilder builder, + string? columnType) + where TOwnerEntity : class + where TDependentEntity : class + => (OwnedNavigationBuilder)((OwnedNavigationBuilder)builder).HasColumnType(columnType); + + /// + /// Set the relational database column type to be used to store the document represented by this owned entity. + /// + /// + /// This method should only be specified for the outer-most owned entity in the given ownership structure and + /// only when mapping the column to a database document type. + /// + /// The builder for the owned navigation being configured. + /// The database type for the column, or to use the database default. + /// The same builder instance so that multiple calls can be chained. + public static OwnedNavigationBuilder HasColumnType(this OwnedNavigationBuilder builder, string? columnType) + { + builder.OwnedEntityType.SetContainerColumnType(columnType); + + return builder; + } + /// /// Configures the navigation of an entity mapped to a JSON column, mapping the navigation to a specific JSON property, /// rather than using the navigation name. diff --git a/src/EFCore.Relational/Extensions/RelationalTypeBaseExtensions.cs b/src/EFCore.Relational/Extensions/RelationalTypeBaseExtensions.cs index 08e551a6ebc..0963c7b1db6 100644 --- a/src/EFCore.Relational/Extensions/RelationalTypeBaseExtensions.cs +++ b/src/EFCore.Relational/Extensions/RelationalTypeBaseExtensions.cs @@ -368,6 +368,17 @@ public static bool IsMappedToJson(this IReadOnlyTypeBase typeBase) ? entityType.GetContainerColumnName() : ((IReadOnlyComplexType)typeBase).GetContainerColumnName(); + + /// + /// Gets the column type to use for the container column to which the type is mapped. + /// + /// The type. + /// The database column type. + public static string? GetContainerColumnType(this IReadOnlyTypeBase typeBase) + => typeBase is IReadOnlyEntityType entityType + ? entityType.GetContainerColumnType() + : null; + /// /// Gets the value of JSON property name used for the given entity mapped to a JSON column. /// diff --git a/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs b/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs index 4c5f8b5db56..4a33ba5f4ad 100644 --- a/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs +++ b/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs @@ -2592,6 +2592,23 @@ protected virtual void ValidateJsonEntities( IModel model, IDiagnosticsLogger logger) { + foreach (var entityType in model.GetEntityTypes()) + { + if (entityType[RelationalAnnotationNames.ContainerColumnType] != null) + { + if (entityType.FindOwnership()?.PrincipalEntityType.IsOwned() == true) + { + throw new InvalidOperationException(RelationalStrings.ContainerTypeOnNestedOwnedEntityType(entityType.DisplayName())); + } + + if (!entityType.IsOwned() + || entityType.GetContainerColumnName() == null) + { + throw new InvalidOperationException(RelationalStrings.ContainerTypeOnNonContainer(entityType.DisplayName())); + } + } + } + var tables = BuildSharedTableEntityMap(model.GetEntityTypes()); foreach (var (table, mappedTypes) in tables) { diff --git a/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs b/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs index 637be020b0e..3bfa99bd9c3 100644 --- a/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs +++ b/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs @@ -286,12 +286,14 @@ private static void AddDefaultMappings( includesDerivedTypes: entityType.GetDirectlyDerivedTypes().Any() ? !isTpc && mappedType == entityType : null); + var containerColumnName = mappedType.GetContainerColumnName(); + var containerColumnType = mappedType.GetContainerColumnType(); if (!string.IsNullOrEmpty(containerColumnName)) { CreateContainerColumn( - defaultTable, containerColumnName, mappedType, relationalTypeMappingSource, - static (c, t, m) => new JsonColumnBase(c, m.StoreType, t, m)); + defaultTable, containerColumnName, containerColumnType, mappedType, relationalTypeMappingSource, + static (colName, colType, table, mapping) => new JsonColumnBase(colName, colType ?? mapping.StoreType, table, mapping)); } else { @@ -492,11 +494,12 @@ private static void CreateTableMapping( }; var containerColumnName = mappedType.GetContainerColumnName(); + var containerColumnType = mappedType.GetContainerColumnType(); if (!string.IsNullOrEmpty(containerColumnName)) { CreateContainerColumn( - table, containerColumnName, (IEntityType)mappedType, relationalTypeMappingSource, - static (c, t, m) => new JsonColumn(c, m.StoreType, (Table)t, m)); + table, containerColumnName, containerColumnType, (IEntityType)mappedType, relationalTypeMappingSource, + static (colName, colType, table, mapping) => new JsonColumn(colName, colType ?? mapping.StoreType, (Table)table, mapping)); } else { @@ -567,9 +570,10 @@ private static void CreateTableMapping( private static void CreateContainerColumn( TableBase tableBase, string containerColumnName, + string? containerColumnType, IEntityType mappedType, IRelationalTypeMappingSource relationalTypeMappingSource, - Func> createColumn) + Func> createColumn) where TColumnMappingBase : class, IColumnMappingBase { var ownership = mappedType.GetForeignKeys().Single(fk => fk.IsOwnership); @@ -577,8 +581,8 @@ private static void CreateContainerColumn( { Check.DebugAssert(tableBase.FindColumn(containerColumnName) == null, $"Table does not have column '{containerColumnName}'."); - var jsonColumnTypeMapping = relationalTypeMappingSource.FindMapping(typeof(JsonElement), mappedType.Model)!; - var jsonColumn = createColumn(containerColumnName, tableBase, jsonColumnTypeMapping); + var jsonColumnTypeMapping = relationalTypeMappingSource.FindMapping(typeof(JsonElement), storeTypeName: containerColumnType)!; + var jsonColumn = createColumn(containerColumnName, containerColumnType, tableBase, jsonColumnTypeMapping); tableBase.Columns.Add(containerColumnName, jsonColumn); jsonColumn.IsNullable = !ownership.IsRequiredDependent || !ownership.IsUnique; @@ -684,11 +688,12 @@ private static void CreateViewMapping( }; var containerColumnName = mappedType.GetContainerColumnName(); + var containerColumnType = mappedType.GetContainerColumnType(); if (!string.IsNullOrEmpty(containerColumnName)) { CreateContainerColumn( - view, containerColumnName, mappedType, relationalTypeMappingSource, - static (c, t, m) => new JsonViewColumn(c, m.StoreType, (View)t, m)); + view, containerColumnName, containerColumnType, mappedType, relationalTypeMappingSource, + static (colName, colType, table, mapping) => new JsonViewColumn(colName, colType ?? mapping.StoreType, (View)table, mapping)); } else { diff --git a/src/EFCore.Relational/Metadata/RelationalAnnotationNames.cs b/src/EFCore.Relational/Metadata/RelationalAnnotationNames.cs index 6041736eca8..5910fd347d5 100644 --- a/src/EFCore.Relational/Metadata/RelationalAnnotationNames.cs +++ b/src/EFCore.Relational/Metadata/RelationalAnnotationNames.cs @@ -324,6 +324,11 @@ public static class RelationalAnnotationNames /// public const string ContainerColumnName = Prefix + "ContainerColumnName"; + /// + /// The column type for the container column to which the object is mapped. + /// + public const string ContainerColumnType = Prefix + nameof(ContainerColumnType); + /// /// The name for the annotation specifying container column type mapping. /// @@ -408,6 +413,7 @@ public static class RelationalAnnotationNames ModelDependencies, FieldValueGetter, ContainerColumnName, + ContainerColumnType, #pragma warning disable CS0618 // Type or member is obsolete ContainerColumnTypeMapping, #pragma warning restore CS0618 // Type or member is obsolete diff --git a/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs b/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs index 54aabd7a2f9..38b41f9bb8a 100644 --- a/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs +++ b/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs @@ -195,6 +195,22 @@ public static string ConflictingTypeMappingsInferredForColumn(object? column) GetString("ConflictingTypeMappingsInferredForColumn", nameof(column)), column); + /// + /// The entity type '{entityType}' has a container column type configured, but is nested in another owned type. The container column type can only be specified on a top-level owned type mapped to a container. + /// + public static string ContainerTypeOnNestedOwnedEntityType(object? entityType) + => string.Format( + GetString("ContainerTypeOnNestedOwnedEntityType", nameof(entityType)), + entityType); + + /// + /// The entity type '{entityType}' has a container column type configured, but is not mapped to a container column, such as for JSON. The container column type can only be specified on a top-level owned type mapped to a container. + /// + public static string ContainerTypeOnNonContainer(object? entityType) + => string.Format( + GetString("ContainerTypeOnNonContainer", nameof(entityType)), + entityType); + /// /// {numSortOrderProperties} values were provided in CreateIndexOperations.IsDescending, but the operation has {numColumns} columns. /// @@ -1053,6 +1069,12 @@ public static string JsonCantNavigateToParentEntity(object? jsonEntity, object? GetString("JsonCantNavigateToParentEntity", nameof(jsonEntity), nameof(parentEntity), nameof(navigation)), jsonEntity, parentEntity, navigation); + /// + /// The database returned the empty string when a JSON object was expected. + /// + public static string JsonEmptyString + => GetString("JsonEmptyString"); + /// /// Entity '{jsonType}' is mapped to JSON and also to a table or view '{tableOrViewName}', but its owner '{ownerType}' is mapped to a different table or view '{ownerTableOrViewName}'. Every entity mapped to JSON must also map to the same table or view as its owner. /// diff --git a/src/EFCore.Relational/Properties/RelationalStrings.resx b/src/EFCore.Relational/Properties/RelationalStrings.resx index 84f26811cc3..06edbd5b860 100644 --- a/src/EFCore.Relational/Properties/RelationalStrings.resx +++ b/src/EFCore.Relational/Properties/RelationalStrings.resx @@ -187,6 +187,12 @@ Conflicting type mappings were inferred for column '{column}'. + + The entity type '{entityType}' has a container column type configured, but is nested in another owned type. The container column type can only be specified on a top-level owned type mapped to a container. + + + The entity type '{entityType}' has a container column type configured, but is not mapped to a container column, such as for JSON. The container column type can only be specified on a top-level owned type mapped to a container. + {numSortOrderProperties} values were provided in CreateIndexOperations.IsDescending, but the operation has {numColumns} columns. @@ -514,6 +520,9 @@ Can't navigate from JSON-mapped entity '{jsonEntity}' to its parent entity '{parentEntity}' using navigation '{navigation}'. Entities mapped to JSON can only navigate to their children. + + The database returned the empty string when a JSON object was expected. + Entity '{jsonType}' is mapped to JSON and also to a table or view '{tableOrViewName}', but its owner '{ownerType}' is mapped to a different table or view '{ownerTableOrViewName}'. Every entity mapped to JSON must also map to the same table or view as its owner. diff --git a/src/EFCore.SqlServer/Diagnostics/Internal/SqlServerLoggingDefinitions.cs b/src/EFCore.SqlServer/Diagnostics/Internal/SqlServerLoggingDefinitions.cs index 39aeb7ee552..a81a6df2ebd 100644 --- a/src/EFCore.SqlServer/Diagnostics/Internal/SqlServerLoggingDefinitions.cs +++ b/src/EFCore.SqlServer/Diagnostics/Internal/SqlServerLoggingDefinitions.cs @@ -194,4 +194,12 @@ public class SqlServerLoggingDefinitions : RelationalLoggingDefinitions /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public EventDefinitionBase? LogMissingViewDefinitionRights; + + /// + /// 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 EventDefinitionBase? LogJsonTypeExperimental; } diff --git a/src/EFCore.SqlServer/Diagnostics/SqlServerEventId.cs b/src/EFCore.SqlServer/Diagnostics/SqlServerEventId.cs index 2fd40c196ef..5940679dcf3 100644 --- a/src/EFCore.SqlServer/Diagnostics/SqlServerEventId.cs +++ b/src/EFCore.SqlServer/Diagnostics/SqlServerEventId.cs @@ -25,14 +25,14 @@ public static class SqlServerEventId // Try to use naming and be consistent with existing names. private enum Id { - // Model validation events + // All events + // Don't insert or delete anything in the middle of this section! DecimalTypeDefaultWarning = CoreEventId.ProviderBaseId, ByteIdentityColumnWarning, ConflictingValueGenerationStrategiesWarning, DecimalTypeKeyWarning, - - // Transaction events SavepointsDisabledBecauseOfMARS, + JsonTypeExperimental, // Scaffolding events ColumnFound = CoreEventId.ProviderDesignBaseId, @@ -115,6 +115,20 @@ private static EventId MakeValidationId(Id id) /// public static readonly EventId ByteIdentityColumnWarning = MakeValidationId(Id.ByteIdentityColumnWarning); + /// + /// An entity type makes use of the SQL Server native 'json' type. Please note that support for this type in EF Core 9 is + /// experimental and may change in future releases. + /// + /// + /// + /// This event is in the category. + /// + /// + /// This event uses the payload when used with a . + /// + /// + public static readonly EventId JsonTypeExperimental = MakeValidationId(Id.JsonTypeExperimental); + /// /// There are conflicting value generation methods for a property. /// diff --git a/src/EFCore.SqlServer/Extensions/Internal/SqlServerLoggerExtensions.cs b/src/EFCore.SqlServer/Extensions/Internal/SqlServerLoggerExtensions.cs index 64642e4a680..1be0de40b3a 100644 --- a/src/EFCore.SqlServer/Extensions/Internal/SqlServerLoggerExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/Internal/SqlServerLoggerExtensions.cs @@ -124,6 +124,32 @@ private static string ByteIdentityColumnWarning(EventDefinitionBase definition, p.Property.DeclaringType.DisplayName()); } + /// + /// 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 void JsonTypeExperimental( + this IDiagnosticsLogger diagnostics, + IEntityType entityType) + { + var definition = SqlServerResources.LogJsonTypeExperimental(diagnostics); + + if (diagnostics.ShouldLog(definition)) + { + definition.Log(diagnostics, entityType.DisplayName()); + } + + if (diagnostics.NeedsEventData(definition, out var diagnosticSourceEnabled, out var simpleLogEnabled)) + { + var eventData = new EntityTypeEventData(definition, (d, p) + => ((EventDefinition)d).GenerateMessage(((EntityTypeEventData)p).EntityType.DisplayName()), entityType); + + diagnostics.DispatchEventData(definition, eventData, diagnosticSourceEnabled, simpleLogEnabled); + } + } + /// /// 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/Internal/SqlServerModelValidator.cs b/src/EFCore.SqlServer/Infrastructure/Internal/SqlServerModelValidator.cs index 68cc2d344c5..b0dde8c6af6 100644 --- a/src/EFCore.SqlServer/Infrastructure/Internal/SqlServerModelValidator.cs +++ b/src/EFCore.SqlServer/Infrastructure/Internal/SqlServerModelValidator.cs @@ -45,6 +45,27 @@ public override void Validate(IModel model, IDiagnosticsLogger + /// 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. + /// + protected virtual void ValidateUseOfJsonType( + IModel model, + IDiagnosticsLogger logger) + { + foreach (var entityType in model.GetEntityTypes()) + { + if (string.Equals(entityType.GetContainerColumnType(), "json", StringComparison.OrdinalIgnoreCase) + || entityType.GetProperties().Any(p => string.Equals(p.GetColumnType(), "json", StringComparison.OrdinalIgnoreCase))) + { + logger.JsonTypeExperimental(entityType); + } + } } /// diff --git a/src/EFCore.SqlServer/Properties/SqlServerStrings.Designer.cs b/src/EFCore.SqlServer/Properties/SqlServerStrings.Designer.cs index a2394614e7c..00b27a4de1a 100644 --- a/src/EFCore.SqlServer/Properties/SqlServerStrings.Designer.cs +++ b/src/EFCore.SqlServer/Properties/SqlServerStrings.Designer.cs @@ -818,6 +818,31 @@ public static EventDefinition LogFoundUniqueConstraint(IDiagnost return (EventDefinition)definition; } + /// + /// The entity type '{entityType}' makes use of the SQL Server native 'json' type. Please note that support for this type in EF Core 9 is experimental and may change in future releases. + /// + public static EventDefinition LogJsonTypeExperimental(IDiagnosticsLogger logger) + { + var definition = ((Diagnostics.Internal.SqlServerLoggingDefinitions)logger.Definitions).LogJsonTypeExperimental; + if (definition == null) + { + definition = NonCapturingLazyInitializer.EnsureInitialized( + ref ((Diagnostics.Internal.SqlServerLoggingDefinitions)logger.Definitions).LogJsonTypeExperimental, + logger, + static logger => new EventDefinition( + logger.Options, + SqlServerEventId.JsonTypeExperimental, + LogLevel.Warning, + "SqlServerEventId.JsonTypeExperimental", + level => LoggerMessage.Define( + level, + SqlServerEventId.JsonTypeExperimental, + _resourceManager.GetString("LogJsonTypeExperimental")!))); + } + + return (EventDefinition)definition; + } + /// /// Unable to find a schema in the database matching the selected schema '{schema}'. /// diff --git a/src/EFCore.SqlServer/Properties/SqlServerStrings.resx b/src/EFCore.SqlServer/Properties/SqlServerStrings.resx index 5b734aebd29..2f8726af548 100644 --- a/src/EFCore.SqlServer/Properties/SqlServerStrings.resx +++ b/src/EFCore.SqlServer/Properties/SqlServerStrings.resx @@ -1,17 +1,17 @@  - @@ -264,6 +264,10 @@ Found unique constraint on table '{tableName}' with name '{uniqueConstraintName}'. Debug SqlServerEventId.UniqueConstraintFound string string + + The entity type '{entityType}' makes use of the SQL Server native 'json' type. Please note that support for this type in EF Core 9 is experimental and may change in future releases. + Warning SqlServerEventId.JsonTypeExperimental string + Unable to find a schema in the database matching the selected schema '{schema}'. Warning SqlServerEventId.MissingSchemaWarning string? diff --git a/src/EFCore.SqlServer/Query/Internal/SqlExpressions/SqlServerOpenJsonExpression.cs b/src/EFCore.SqlServer/Query/Internal/SqlExpressions/SqlServerOpenJsonExpression.cs index 09c39858a56..cc213ae489c 100644 --- a/src/EFCore.SqlServer/Query/Internal/SqlExpressions/SqlServerOpenJsonExpression.cs +++ b/src/EFCore.SqlServer/Query/Internal/SqlExpressions/SqlServerOpenJsonExpression.cs @@ -152,6 +152,15 @@ public virtual SqlServerOpenJsonExpression Update( : new SqlServerOpenJsonExpression(Alias, jsonExpression, path, columnInfos); } + /// + /// 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 SqlServerOpenJsonExpression Update(SqlExpression sqlExpression) + => new(Alias, sqlExpression, Path, ColumnInfos); + /// /// 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/Query/Internal/SqlServerJsonPostprocessor.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerJsonPostprocessor.cs index 9d301e6deb3..aaa815f459b 100644 --- a/src/EFCore.SqlServer/Query/Internal/SqlServerJsonPostprocessor.cs +++ b/src/EFCore.SqlServer/Query/Internal/SqlServerJsonPostprocessor.cs @@ -79,12 +79,12 @@ public Expression Process(Expression expression) if (table.UnwrapJoin() is SqlServerOpenJsonExpression { ColumnInfos: { } columnInfos } openJsonExpression // Condition 1: an ordering/projection still refers to the OPENJSON's [key] column - it needs to be preserved. && (selectExpression.Orderings.Select(o => o.Expression) - .Concat(selectExpression.Projection.Select(p => p.Expression)) - .Any(x => IsKeyColumn(x, openJsonExpression.Alias)) - || - // Condition 2: a column type in the WITH clause is a SQL Server "CLR type" (e.g. hierarchy id). - // These are not supported by OPENJSON with WITH. - columnInfos.Any(c => c.TypeMapping.StoreType is "hierarchyid"))) + .Concat(selectExpression.Projection.Select(p => p.Expression)) + .Any(x => IsKeyColumn(x, openJsonExpression.Alias)) + || + // Condition 2: a column type in the WITH clause is a SQL Server "CLR type" (e.g. hierarchy id). + // These are not supported by OPENJSON with WITH. + columnInfos.Any(c => c.TypeMapping.StoreType is "hierarchyid"))) { // Remove the WITH clause from the OPENJSON expression var newOpenJsonExpression = openJsonExpression.Update( @@ -199,7 +199,11 @@ when _columnsToRewrite.TryGetValue((columnExpression.TableAlias, columnExpressio // The new OPENJSON (without WITH) always projects a `value` column, instead of a properly named column for individual // values inside; create a new ColumnExpression with that name. SqlExpression rewrittenColumn = new ColumnExpression( - "value", columnExpression.TableAlias, columnExpression.Type, _nvarcharMaxTypeMapping, columnExpression.IsNullable); + "value", + columnExpression.TableAlias, + columnExpression.Type, + _nvarcharMaxTypeMapping, + columnExpression.IsNullable); // Prepend the path from the OPENJSON/WITH to the path in the JsonScalarExpression var path = columnInfo.Path is null @@ -207,7 +211,10 @@ when _columnsToRewrite.TryGetValue((columnExpression.TableAlias, columnExpressio : columnInfo.Path.Concat(jsonScalarExpression.Path).ToList(); return new JsonScalarExpression( - rewrittenColumn, path, jsonScalarExpression.Type, jsonScalarExpression.TypeMapping, + rewrittenColumn, + path, + jsonScalarExpression.Type, + jsonScalarExpression.TypeMapping, jsonScalarExpression.IsNullable); } @@ -219,23 +226,46 @@ when _columnsToRewrite.TryGetValue((columnExpression.TableAlias, columnExpressio case JsonScalarExpression { TypeMapping.StoreTypeNameBase: "varbinary" or "binary" } jsonScalar: { var name = jsonScalar.Path.LastOrDefault(ps => ps.PropertyName is not null).PropertyName - ?? (jsonScalar.Json as ColumnExpression)?.Name - ?? "Json"; + ?? (jsonScalar.Json as ColumnExpression)?.Name + ?? "Json"; var tableAlias = sqlAliasManager.GenerateTableAlias(name); var join = new OuterApplyExpression( new SqlServerOpenJsonExpression( - tableAlias, jsonScalar.Json, path: null, + tableAlias, + jsonScalar.Json, + path: null, columnInfos: [new(name, jsonScalar.TypeMapping, jsonScalar.Path)])); // We record the new OUTER APPLY in _openWithOuterAppliesToAdd (it gets added after visiting the SelectExpression above), // and return a ColumnExpression referencing that new OUTER APPLY. _openjsonOuterAppliesToAdd.Add(join); - return new ColumnExpression(name, tableAlias, jsonScalar.Type, jsonScalar.TypeMapping, + return new ColumnExpression( + name, + tableAlias, + jsonScalar.Type, + jsonScalar.TypeMapping, jsonScalar.IsNullable); } + case SqlServerOpenJsonExpression openJsonExpression: + // Currently, OPENJSON does not accept a "json" type, so we must cast the value to a string. + // We do this for both the case where is a string type mapping for a top-level property with the store type + // of "json", and when there is an "element" type mapping to something in the document but is now being used + // with OPENJSON. + return openJsonExpression.JsonExpression.TypeMapping + is SqlServerStringTypeMapping { StoreType: "json" } + or SqlServerOwnedJsonTypeMapping { StoreType: "json" } + + ? openJsonExpression.Update( + new SqlUnaryExpression( + ExpressionType.Convert, + (SqlExpression)Visit(openJsonExpression.JsonExpression), + typeof(string), + typeMappingSource.FindMapping(typeof(string))!)) + : base.Visit(expression); + default: return base.Visit(expression); } diff --git a/src/EFCore.SqlServer/Query/Internal/SqlServerQuerySqlGenerator.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerQuerySqlGenerator.cs index 6e5693a4670..5c0ba648d9b 100644 --- a/src/EFCore.SqlServer/Query/Internal/SqlServerQuerySqlGenerator.cs +++ b/src/EFCore.SqlServer/Query/Internal/SqlServerQuerySqlGenerator.cs @@ -475,7 +475,7 @@ protected override Expression VisitJsonScalar(JsonScalarExpression jsonScalarExp return jsonScalarExpression; } - if (jsonScalarExpression.TypeMapping is SqlServerJsonTypeMapping + if (jsonScalarExpression.TypeMapping is SqlServerOwnedJsonTypeMapping || jsonScalarExpression.TypeMapping?.ElementTypeMapping is not null) { Sql.Append("JSON_QUERY("); @@ -494,7 +494,7 @@ protected override Expression VisitJsonScalar(JsonScalarExpression jsonScalarExp GenerateJsonPath(jsonScalarExpression.Path); Sql.Append(")"); - if (jsonScalarExpression.TypeMapping is not SqlServerJsonTypeMapping and not StringTypeMapping) + if (jsonScalarExpression.TypeMapping is not SqlServerOwnedJsonTypeMapping and not StringTypeMapping) { Sql.Append(" AS "); Sql.Append(jsonScalarExpression.TypeMapping!.StoreType); diff --git a/src/EFCore.SqlServer/Storage/Internal/SqlServerJsonTypeMapping.cs b/src/EFCore.SqlServer/Storage/Internal/SqlServerOwnedJsonTypeMapping.cs similarity index 65% rename from src/EFCore.SqlServer/Storage/Internal/SqlServerJsonTypeMapping.cs rename to src/EFCore.SqlServer/Storage/Internal/SqlServerOwnedJsonTypeMapping.cs index 8f6e7923d3a..6bd4cfeffb0 100644 --- a/src/EFCore.SqlServer/Storage/Internal/SqlServerJsonTypeMapping.cs +++ b/src/EFCore.SqlServer/Storage/Internal/SqlServerOwnedJsonTypeMapping.cs @@ -1,8 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Data; using System.Text; using System.Text.Json; +using Microsoft.Data.SqlClient; namespace Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal; @@ -12,19 +14,21 @@ namespace Microsoft.EntityFrameworkCore.SqlServer.Storage.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 SqlServerJsonTypeMapping : JsonTypeMapping +public class SqlServerOwnedJsonTypeMapping : JsonTypeMapping { + private static readonly MethodInfo CreateUtf8StreamMethod + = typeof(SqlServerOwnedJsonTypeMapping).GetMethod(nameof(CreateUtf8Stream), [typeof(string)])!; + private static readonly MethodInfo GetStringMethod = typeof(DbDataReader).GetRuntimeMethod(nameof(DbDataReader.GetString), [typeof(int)])!; - private static readonly PropertyInfo UTF8Property - = typeof(Encoding).GetProperty(nameof(Encoding.UTF8))!; - - private static readonly MethodInfo EncodingGetBytesMethod - = typeof(Encoding).GetMethod(nameof(Encoding.GetBytes), [typeof(string)])!; - - private static readonly ConstructorInfo MemoryStreamConstructor - = typeof(MemoryStream).GetConstructor([typeof(byte[])])!; + /// + /// 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 SqlServerOwnedJsonTypeMapping Default { get; } = new("nvarchar(max)"); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -32,7 +36,7 @@ private static readonly ConstructorInfo MemoryStreamConstructor /// 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 SqlServerJsonTypeMapping Default { get; } = new("nvarchar(max)"); + public static SqlServerOwnedJsonTypeMapping OwnedJsonTypeDefault { get; } = new("json"); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -40,7 +44,7 @@ private static readonly ConstructorInfo MemoryStreamConstructor /// 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 SqlServerJsonTypeMapping(string storeType) + public SqlServerOwnedJsonTypeMapping(string storeType) : base(storeType, typeof(JsonElement), System.Data.DbType.String) { } @@ -54,6 +58,17 @@ public SqlServerJsonTypeMapping(string storeType) public override MethodInfo GetDataReaderMethod() => GetStringMethod; + /// + /// 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 MemoryStream CreateUtf8Stream(string json) + => json == "" + ? throw new InvalidOperationException(RelationalStrings.JsonEmptyString) + : new MemoryStream(Encoding.UTF8.GetBytes(json)); + /// /// 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 @@ -61,12 +76,7 @@ public override MethodInfo GetDataReaderMethod() /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public override Expression CustomizeDataReaderExpression(Expression expression) - => Expression.New( - MemoryStreamConstructor, - Expression.Call( - Expression.Property(null, UTF8Property), - EncodingGetBytesMethod, - expression)); + => Expression.Call(CreateUtf8StreamMethod, expression); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -74,7 +84,7 @@ public override Expression CustomizeDataReaderExpression(Expression expression) /// 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. /// - protected SqlServerJsonTypeMapping(RelationalTypeMappingParameters parameters) + protected SqlServerOwnedJsonTypeMapping(RelationalTypeMappingParameters parameters) : base(parameters) { } @@ -104,5 +114,23 @@ protected override string GenerateNonNullSqlLiteral(object value) /// doing so can result in application failures when updating to a new Entity Framework Core release. /// protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) - => new SqlServerJsonTypeMapping(parameters); + => new SqlServerOwnedJsonTypeMapping(parameters); + + /// + /// 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. + /// + protected override void ConfigureParameter(DbParameter parameter) + { + if (StoreType == "json" + && parameter is SqlParameter sqlParameter) // To avoid crashing wrapping providers + { + // TODO:SQLJSON Issue #34414 + sqlParameter.SqlDbType = ((SqlDbType)35); + } + + base.ConfigureParameter(parameter); + } } diff --git a/src/EFCore.SqlServer/Storage/Internal/SqlServerStringTypeMapping.cs b/src/EFCore.SqlServer/Storage/Internal/SqlServerStringTypeMapping.cs index 7d3d6f724e0..b0d232d53e9 100644 --- a/src/EFCore.SqlServer/Storage/Internal/SqlServerStringTypeMapping.cs +++ b/src/EFCore.SqlServer/Storage/Internal/SqlServerStringTypeMapping.cs @@ -34,6 +34,24 @@ public class SqlServerStringTypeMapping : StringTypeMapping /// public static new SqlServerStringTypeMapping Default { get; } = new(); + /// + /// 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. + /// + // TODO:SQLJSON Issue #34414 + public static SqlServerStringTypeMapping JsonTypeDefault { get; } = new("json", sqlDbType: (SqlDbType)35); + + /// + /// 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 SqlServerStringTypeMapping UnicodeDefault { get; } = new( + "nvarchar(max)", unicode: true, storeTypePostfix: StoreTypePostfix.None); + /// /// 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 @@ -138,10 +156,14 @@ protected override void ConfigureParameter(DbParameter parameter) var value = parameter.Value; var length = (value as string)?.Length; - if (_sqlDbType.HasValue + // TODO:SQLJSON Issue #34414 + var sqlDbType = _sqlDbType + ?? (StoreType == "json" ? (SqlDbType)35 : null); + + if (sqlDbType.HasValue && parameter is SqlParameter sqlParameter) // To avoid crashing wrapping providers { - sqlParameter.SqlDbType = _sqlDbType.Value; + sqlParameter.SqlDbType = sqlDbType.Value; } if ((value == null diff --git a/src/EFCore.SqlServer/Storage/Internal/SqlServerTypeMappingSource.cs b/src/EFCore.SqlServer/Storage/Internal/SqlServerTypeMappingSource.cs index be70a79a7af..50d50413c28 100644 --- a/src/EFCore.SqlServer/Storage/Internal/SqlServerTypeMappingSource.cs +++ b/src/EFCore.SqlServer/Storage/Internal/SqlServerTypeMappingSource.cs @@ -141,8 +141,7 @@ static SqlServerTypeMappingSource() { typeof(float), SqlServerFloatTypeMapping.Default }, { typeof(decimal), SqlServerDecimalTypeMapping.Default }, { typeof(TimeOnly), SqlServerTimeOnlyTypeMapping.Default }, - { typeof(TimeSpan), SqlServerTimeSpanTypeMapping.Default }, - { typeof(JsonElement), SqlServerJsonTypeMapping.Default } + { typeof(TimeSpan), SqlServerTimeSpanTypeMapping.Default } }; _clrNoFacetTypeMappings @@ -180,6 +179,7 @@ static SqlServerTypeMappingSource() { "float", [SqlServerDoubleTypeMapping.Default] }, { "image", [ImageBinary] }, { "int", [IntTypeMapping.Default] }, + { "json", [SqlServerStringTypeMapping.JsonTypeDefault] }, { "money", [Money] }, { "national char varying", [VariableLengthUnicodeString] }, { "national char varying(max)", [VariableLengthMaxUnicodeString] }, @@ -239,6 +239,13 @@ public SqlServerTypeMappingSource( var clrType = mappingInfo.ClrType; var storeTypeName = mappingInfo.StoreTypeName; + if (clrType == typeof(JsonElement)) + { + return storeTypeName == "json" + ? SqlServerOwnedJsonTypeMapping.OwnedJsonTypeDefault + : SqlServerOwnedJsonTypeMapping.Default; + } + if (storeTypeName != null) { var storeTypeNameBase = mappingInfo.StoreTypeNameBase; @@ -310,6 +317,11 @@ public SqlServerTypeMappingSource( if (clrType == typeof(string)) { + if (storeTypeName == "json") + { + return SqlServerStringTypeMapping.JsonTypeDefault; + } + var isAnsi = mappingInfo.IsUnicode == false; var isFixedLength = mappingInfo.IsFixedLength == true; var maxSize = isAnsi ? 8000 : 4000; diff --git a/src/EFCore.SqlServer/Update/Internal/SqlServerModificationCommand.cs b/src/EFCore.SqlServer/Update/Internal/SqlServerModificationCommand.cs index 7f46d58d144..d4d8ae07587 100644 --- a/src/EFCore.SqlServer/Update/Internal/SqlServerModificationCommand.cs +++ b/src/EFCore.SqlServer/Update/Internal/SqlServerModificationCommand.cs @@ -1,6 +1,8 @@ // 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.SqlServer.Storage.Internal; + namespace Microsoft.EntityFrameworkCore.SqlServer.Update.Internal; /// @@ -32,4 +34,60 @@ public SqlServerModificationCommand(in NonTrackedModificationCommandParameters m : base(modificationCommandParameters) { } + + /// + /// 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. + /// + protected override void ProcessSinglePropertyJsonUpdate(ref ColumnModificationParameters parameters) + { + // See: Issue #34432 + var property = parameters.Property!; + var mapping = property.GetRelationalTypeMapping(); + var propertyProviderClrType = (mapping.Converter?.ProviderClrType ?? property.ClrType).UnwrapNullableType(); + var value = parameters.Value; + + // JSON-compatible non-string values (bool, numeric, null) are sent directly as non-string parameters. + if (value is null + || propertyProviderClrType == typeof(bool) + || propertyProviderClrType.IsNumeric()) + { + parameters = parameters with { Value = value, TypeMapping = mapping }; + } + else + { + // Everything else must go as either a string parameter or a json parameter, depending on whether the json type + // is being used or not. To determine this, we get the JSON value and check if it is a string or some other + // type of JSON object. + var jsonValueReaderWriter = mapping.JsonValueReaderWriter; + if (jsonValueReaderWriter != null) + { + var stringValue = jsonValueReaderWriter.ToJsonString(value); + if (!stringValue.StartsWith('\"')) + { + // This is actual JSON, so send with the original type mapping, which may indicate the column type is JSON. + parameters = parameters with { Value = stringValue }; + + return; + } + + // Otherwise remove the quotes and send the value as a string. + value = stringValue[1..^1]; + } + else if (mapping.Converter != null) + { + value = mapping.Converter.ConvertToProvider(value); + } + + parameters = parameters with + { + Value = value, + TypeMapping = parameters.TypeMapping is SqlServerOwnedJsonTypeMapping + ? SqlServerStringTypeMapping.UnicodeDefault + : parameters.TypeMapping + }; + } + } } diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/JsonQueryCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/JsonQueryCosmosTest.cs index 46f242cc84c..e1794c0a85d 100644 --- a/test/EFCore.Cosmos.FunctionalTests/Query/JsonQueryCosmosTest.cs +++ b/test/EFCore.Cosmos.FunctionalTests/Query/JsonQueryCosmosTest.cs @@ -4,6 +4,7 @@ using Microsoft.Azure.Cosmos; using Microsoft.EntityFrameworkCore.Cosmos.Internal; using Microsoft.EntityFrameworkCore.TestModels.JsonQuery; +using Microsoft.EntityFrameworkCore.TestUtilities.Xunit; namespace Microsoft.EntityFrameworkCore.Query; @@ -486,6 +487,7 @@ public override Task Group_by_on_json_scalar_using_collection_indexer(bool async public override Task Group_by_Skip_Take_on_json_scalar(bool async) => base.Group_by_Skip_Take_on_json_scalar(async); + [SkipOnCiCondition] public override Task Json_all_types_entity_projection(bool async) => Fixture.NoSyncTest( async, async a => @@ -500,6 +502,7 @@ FROM root c """); }); + [SkipOnCiCondition] public override Task Json_all_types_projection_from_owned_entity_reference(bool async) => Fixture.NoSyncTest( async, async a => @@ -528,6 +531,8 @@ FROM root c WHERE (c["Discriminator"] = "AllTypes") """); }); + + [SkipOnCiCondition] public override Task Json_boolean_predicate(bool async) => Fixture.NoSyncTest( async, async a => @@ -1437,6 +1442,7 @@ FROM root c """); }); + [SkipOnCiCondition] public override Task Json_predicate_on_byte(bool async) => Fixture.NoSyncTest( async, async a => @@ -1451,6 +1457,7 @@ FROM root c """); }); + [SkipOnCiCondition] public override Task Json_predicate_on_byte_array(bool async) => Fixture.NoSyncTest( async, async a => @@ -1465,6 +1472,7 @@ FROM root c """); }); + [SkipOnCiCondition] public override Task Json_predicate_on_character(bool async) => Fixture.NoSyncTest( async, async a => @@ -1479,6 +1487,7 @@ FROM root c """); }); + [SkipOnCiCondition] public override Task Json_predicate_on_dateonly(bool async) => Fixture.NoSyncTest( async, async a => @@ -1493,6 +1502,7 @@ FROM root c """); }); + [SkipOnCiCondition] public override Task Json_predicate_on_datetime(bool async) => Fixture.NoSyncTest( async, async a => @@ -1507,6 +1517,7 @@ FROM root c """); }); + [SkipOnCiCondition] public override Task Json_predicate_on_datetimeoffset(bool async) => Fixture.NoSyncTest( async, async a => @@ -1521,6 +1532,7 @@ FROM root c """); }); + [SkipOnCiCondition] public override Task Json_predicate_on_decimal(bool async) => Fixture.NoSyncTest( async, async a => @@ -1535,6 +1547,7 @@ FROM root c """); }); + [SkipOnCiCondition] public override Task Json_predicate_on_default_string(bool async) => Fixture.NoSyncTest( async, async a => @@ -1549,6 +1562,7 @@ FROM root c """); }); + [SkipOnCiCondition] public override Task Json_predicate_on_double(bool async) => Fixture.NoSyncTest( async, async a => @@ -1563,6 +1577,7 @@ FROM root c """); }); + [SkipOnCiCondition] public override Task Json_predicate_on_enum(bool async) => Fixture.NoSyncTest( async, async a => @@ -1577,6 +1592,7 @@ FROM root c """); }); + [SkipOnCiCondition] public override Task Json_predicate_on_enumwithintconverter(bool async) => Fixture.NoSyncTest( async, async a => @@ -1591,6 +1607,7 @@ FROM root c """); }); + [SkipOnCiCondition] public override Task Json_predicate_on_guid(bool async) => Fixture.NoSyncTest( async, async a => @@ -1605,6 +1622,7 @@ FROM root c """); }); + [SkipOnCiCondition] public override Task Json_predicate_on_int16(bool async) => Fixture.NoSyncTest( async, async a => @@ -1619,6 +1637,7 @@ FROM root c """); }); + [SkipOnCiCondition] public override Task Json_predicate_on_int32(bool async) => Fixture.NoSyncTest( async, async a => @@ -1633,6 +1652,7 @@ FROM root c """); }); + [SkipOnCiCondition] public override Task Json_predicate_on_int64(bool async) => Fixture.NoSyncTest( async, async a => @@ -1661,6 +1681,7 @@ FROM root c """); }); + [SkipOnCiCondition] public override Task Json_predicate_on_max_length_string(bool async) => Fixture.NoSyncTest( async, async a => @@ -1675,6 +1696,7 @@ FROM root c """); }); + [SkipOnCiCondition] public override Task Json_predicate_on_nullableenum1(bool async) => Fixture.NoSyncTest( async, async a => @@ -1689,6 +1711,7 @@ FROM root c """); }); + [SkipOnCiCondition] public override Task Json_predicate_on_nullableenum2(bool async) => Fixture.NoSyncTest( async, async a => @@ -1703,6 +1726,7 @@ FROM root c """); }); + [SkipOnCiCondition] public override Task Json_predicate_on_nullableenumwithconverter1(bool async) => Fixture.NoSyncTest( async, async a => @@ -1717,6 +1741,7 @@ FROM root c """); }); + [SkipOnCiCondition] public override Task Json_predicate_on_nullableenumwithconverter2(bool async) => Fixture.NoSyncTest( async, async a => @@ -1731,6 +1756,7 @@ FROM root c """); }); + [SkipOnCiCondition] public override Task Json_predicate_on_nullableenumwithconverterthathandlesnulls1(bool async) => Fixture.NoSyncTest( async, async a => @@ -1754,6 +1780,7 @@ public override Task Json_predicate_on_nullableenumwithconverterthathandlesnulls AssertSql(""); }); + [SkipOnCiCondition] public override Task Json_predicate_on_nullableint321(bool async) => Fixture.NoSyncTest( async, async a => @@ -1768,6 +1795,7 @@ FROM root c """); }); + [SkipOnCiCondition] public override Task Json_predicate_on_nullableint322(bool async) => Fixture.NoSyncTest( async, async a => @@ -1782,6 +1810,7 @@ FROM root c """); }); + [SkipOnCiCondition] public override Task Json_predicate_on_signedbyte(bool async) => Fixture.NoSyncTest( async, async a => @@ -1796,6 +1825,7 @@ FROM root c """); }); + [SkipOnCiCondition] public override Task Json_predicate_on_single(bool async) => Fixture.NoSyncTest( async, async a => @@ -1810,6 +1840,7 @@ FROM root c """); }); + [SkipOnCiCondition] public override Task Json_predicate_on_string_condition(bool async) => Fixture.NoSyncTest( async, async a => @@ -1852,6 +1883,7 @@ FROM root c """); }); + [SkipOnCiCondition] public override Task Json_predicate_on_timeonly(bool async) => Fixture.NoSyncTest( async, async a => @@ -1866,6 +1898,7 @@ FROM root c """); }); + [SkipOnCiCondition] public override Task Json_predicate_on_timespan(bool async) => Fixture.NoSyncTest( async, async a => @@ -1880,6 +1913,7 @@ FROM root c """); }); + [SkipOnCiCondition] public override Task Json_predicate_on_unisgnedint16(bool async) => Fixture.NoSyncTest( async, async a => @@ -1894,6 +1928,7 @@ FROM root c """); }); + [SkipOnCiCondition] public override Task Json_predicate_on_unsignedint32(bool async) => Fixture.NoSyncTest( async, async a => @@ -1908,6 +1943,7 @@ FROM root c """); }); + [SkipOnCiCondition] public override Task Json_predicate_on_unsignedint64(bool async) => Fixture.NoSyncTest( async, async a => @@ -2236,7 +2272,7 @@ public override Task Json_with_projection_of_mix_of_json_collections_json_refere () => base.Json_with_projection_of_mix_of_json_collections_json_references_and_entity_collection(async), CosmosStrings.MultipleRootEntityTypesReferencedInQuery(nameof(JsonEntityBasicForReference), nameof(JsonEntityBasic))); - public override Task Json_with_projection_of_multiple_json_references_and_entity_collection(bool async) + public override Task Json_with_projection_of_multiple_json_references_and_entity_collection(bool async) => AssertTranslationFailedWithDetails( () => base.Json_with_projection_of_multiple_json_references_and_entity_collection(async), CosmosStrings.LimitOffsetNotSupportedInSubqueries); diff --git a/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.cs b/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.cs index 70997837f36..c044391de2c 100644 --- a/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.cs +++ b/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.cs @@ -95,6 +95,7 @@ public void Test_new_annotations_handled_for_entity_types() RelationalAnnotationNames.JsonPropertyName, // Appears on entity type but requires specific model (i.e. owned types that can map to json, otherwise validation throws) RelationalAnnotationNames.ContainerColumnName, + RelationalAnnotationNames.ContainerColumnType, #pragma warning disable CS0618 RelationalAnnotationNames.ContainerColumnTypeMapping, #pragma warning restore CS0618 @@ -262,6 +263,7 @@ public void Test_new_annotations_handled_for_properties() RelationalAnnotationNames.ModelDependencies, RelationalAnnotationNames.FieldValueGetter, RelationalAnnotationNames.ContainerColumnName, + RelationalAnnotationNames.ContainerColumnType, #pragma warning disable CS0618 RelationalAnnotationNames.ContainerColumnTypeMapping, #pragma warning restore CS0618 diff --git a/test/EFCore.Relational.Specification.Tests/Query/AdHocJsonQueryTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/AdHocJsonQueryTestBase.cs index d0467f17ecc..04dd844badd 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/AdHocJsonQueryTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/AdHocJsonQueryTestBase.cs @@ -10,16 +10,24 @@ public abstract class AdHocJsonQueryTestBase : NonSharedModelTestBase protected override string StoreName => "AdHocJsonQueryTest"; + protected virtual void ConfigureWarnings(WarningsConfigurationBuilder builder) + { + } + #region 32310 [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual async Task Contains_on_nested_collection_with_init_only_navigation(bool async) { - var contextFactory = await InitializeAsync(seed: Seed32310); + var contextFactory = await InitializeAsync( + onConfiguring: b => b.ConfigureWarnings(ConfigureWarnings), + onModelCreating: b => b.Entity().OwnsOne(e => e.Visits).ToJson().HasColumnType(JsonColumnType), + seed: Seed32310); + await using var context = contextFactory.CreateContext(); - var query = context.Pubs + var query = context.Set() .Where(u => u.Visits.DaysVisited.Contains(new DateOnly(2023, 1, 1))); var result = async @@ -30,7 +38,7 @@ public virtual async Task Contains_on_nested_collection_with_init_only_navigatio Assert.Equal(new DateOnly(2023, 1, 1), result.Visits.DaysVisited.Single()); } - protected virtual async Task Seed32310(MyContext32310 context) + protected virtual async Task Seed32310(DbContext context) { var user = new Pub32310 { Name = "FBI", Visits = new Visits32310 { LocationTag = "tag", DaysVisited = [new(2023, 1, 1)] } }; @@ -38,15 +46,6 @@ protected virtual async Task Seed32310(MyContext32310 context) await context.SaveChangesAsync(); } - protected class MyContext32310(DbContextOptions options) : DbContext(options) - { - public DbSet Pubs - => Set(); - - protected override void OnModelCreating(ModelBuilder modelBuilder) - => modelBuilder.Entity(b => { b.OwnsOne(e => e.Visits).ToJson(); }); - } - public class Pub32310 { public int Id { get; set; } @@ -68,12 +67,14 @@ public class Visits32310 [MemberData(nameof(IsAsyncData))] public virtual async Task Optional_json_properties_materialized_as_null_when_the_element_in_json_is_not_present(bool async) { - var contextFactory = await InitializeAsync( + var contextFactory = await InitializeAsync( + onModelCreating: BuildModel29219, + onConfiguring: b => b.ConfigureWarnings(ConfigureWarnings), seed: Seed29219); using (var context = contextFactory.CreateContext()) { - var query = context.Entities.Where(x => x.Id == 3); + var query = context.Set().Where(x => x.Id == 3); var result = async ? await query.SingleAsync() @@ -89,12 +90,14 @@ public virtual async Task Optional_json_properties_materialized_as_null_when_the [MemberData(nameof(IsAsyncData))] public virtual async Task Can_project_nullable_json_property_when_the_element_in_json_is_not_present(bool async) { - var contextFactory = await InitializeAsync( + var contextFactory = await InitializeAsync( + onModelCreating: BuildModel29219, + onConfiguring: b => b.ConfigureWarnings(ConfigureWarnings), seed: Seed29219); using (var context = contextFactory.CreateContext()) { - var query = context.Entities.OrderBy(x => x.Id).Select(x => x.Reference.NullableScalar); + var query = context.Set().OrderBy(x => x.Id).Select(x => x.Reference.NullableScalar); var result = async ? await query.ToListAsync() @@ -107,19 +110,17 @@ public virtual async Task Can_project_nullable_json_property_when_the_element_in } } - protected abstract Task Seed29219(MyContext29219 ctx); - - protected class MyContext29219(DbContextOptions options) : DbContext(options) - { - public DbSet Entities { get; set; } + protected void BuildModel29219(ModelBuilder modelBuilder) + => modelBuilder.Entity( + b => + { + b.ToTable("Entities"); + b.Property(x => x.Id).ValueGeneratedNever(); + b.OwnsOne(x => x.Reference).ToJson().HasColumnType(JsonColumnType); + b.OwnsMany(x => x.Collection).ToJson().HasColumnType(JsonColumnType); + }); - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - modelBuilder.Entity().Property(x => x.Id).ValueGeneratedNever(); - modelBuilder.Entity().OwnsOne(x => x.Reference).ToJson(); - modelBuilder.Entity().OwnsMany(x => x.Collection).ToJson(); - } - } + protected abstract Task Seed29219(DbContext ctx); public class MyEntity29219 { @@ -138,28 +139,24 @@ public class MyJsonEntity29219 #region 30028 - protected abstract Task Seed30028(MyContext30028 ctx); - - protected class MyContext30028(DbContextOptions options) : DbContext(options) - { - public DbSet Entities { get; set; } - - protected override void OnModelCreating(ModelBuilder modelBuilder) - => modelBuilder.Entity( - b => - { - b.Property(x => x.Id).ValueGeneratedNever(); - b.OwnsOne( - x => x.Json, nb => - { - nb.ToJson(); - nb.OwnsMany(x => x.Collection, nnb => nnb.OwnsOne(x => x.Nested)); - nb.OwnsOne(x => x.OptionalReference, nnb => nnb.OwnsOne(x => x.Nested)); - nb.OwnsOne(x => x.RequiredReference, nnb => nnb.OwnsOne(x => x.Nested)); - nb.Navigation(x => x.RequiredReference).IsRequired(); - }); - }); - } + protected abstract Task Seed30028(DbContext ctx); + + protected virtual void BuildModel30028(ModelBuilder modelBuilder) + => modelBuilder.Entity( + b => + { + b.Property(x => x.Id).ValueGeneratedNever(); + b.ToTable("Entities"); + b.OwnsOne( + x => x.Json, nb => + { + nb.ToJson().HasColumnType(JsonColumnType); + nb.OwnsMany(x => x.Collection, nnb => nnb.OwnsOne(x => x.Nested)); + nb.OwnsOne(x => x.OptionalReference, nnb => nnb.OwnsOne(x => x.Nested)); + nb.OwnsOne(x => x.RequiredReference, nnb => nnb.OwnsOne(x => x.Nested)); + nb.Navigation(x => x.RequiredReference).IsRequired(); + }); + }); public class MyEntity30028 { @@ -190,10 +187,14 @@ public class MyJsonLeafEntity30028 [MemberData(nameof(IsAsyncData))] public virtual async Task Accessing_missing_navigation_works(bool async) { - var contextFactory = await InitializeAsync(seed: Seed30028); + var contextFactory = await InitializeAsync( + onModelCreating: BuildModel30028, + onConfiguring: b => b.ConfigureWarnings(ConfigureWarnings), + seed: Seed30028); + using (var context = contextFactory.CreateContext()) { - var result = context.Entities.OrderBy(x => x.Id).ToList(); + var result = context.Set().OrderBy(x => x.Id).ToList(); Assert.Equal(4, result.Count); Assert.NotNull(result[0].Json.Collection); Assert.NotNull(result[0].Json.OptionalReference); @@ -217,10 +218,14 @@ public virtual async Task Accessing_missing_navigation_works(bool async) [MemberData(nameof(IsAsyncData))] public virtual async Task Missing_navigation_works_with_deduplication(bool async) { - var contextFactory = await InitializeAsync(seed: Seed30028); + var contextFactory = await InitializeAsync( + onModelCreating: BuildModel30028, + onConfiguring: b => b.ConfigureWarnings(ConfigureWarnings), + seed: Seed30028); + using (var context = contextFactory.CreateContext()) { - var result = context.Entities.OrderBy(x => x.Id).Select( + var queryable = context.Set().OrderBy(x => x.Id).Select( x => new { x, @@ -230,7 +235,9 @@ public virtual async Task Missing_navigation_works_with_deduplication(bool async NestedOptional = x.Json.OptionalReference.Nested, NestedRequired = x.Json.RequiredReference.Nested, x.Json.Collection, - }).AsNoTracking().ToList(); + }).AsNoTracking(); + + var result = async ? await queryable.ToListAsync() : queryable.ToList(); Assert.Equal(4, result.Count); Assert.NotNull(result[0].OptionalReference); @@ -266,108 +273,109 @@ public virtual async Task Missing_navigation_works_with_deduplication(bool async [ConditionalFact] public virtual async Task Project_json_with_no_properties() { - var contextFactory = await InitializeAsync(seed: Seed30028); + var contextFactory = await InitializeAsync( + onModelCreating: BuildModel32939, + onConfiguring: b => b.ConfigureWarnings(ConfigureWarnings), + seed: Seed32939); + using var context = contextFactory.CreateContext(); - context.Entities.ToList(); + context.Set().ToList(); } - protected Task Seed30028(Context32939 ctx) + protected Task Seed32939(DbContext ctx) { - var entity = new Context32939.Entity32939 + var entity = new Entity32939 { - Empty = new Context32939.JsonEmpty32939(), - FieldOnly = new Context32939.JsonFieldOnly32939() + Empty = new JsonEmpty32939(), + FieldOnly = new JsonFieldOnly32939() }; - ctx.Entities.Add(entity); + ctx.Add(entity); return ctx.SaveChangesAsync(); } - protected class Context32939(DbContextOptions options) : DbContext(options) + protected virtual void BuildModel32939(ModelBuilder modelBuilder) { - public DbSet Entities { get; set; } - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - modelBuilder.Entity().Property(x => x.Id).ValueGeneratedNever(); - modelBuilder.Entity().OwnsOne(x => x.Empty, b => b.ToJson()); - modelBuilder.Entity().OwnsOne(x => x.FieldOnly, b => b.ToJson()); - } + modelBuilder.Entity().Property(x => x.Id).ValueGeneratedNever(); + modelBuilder.Entity().OwnsOne(x => x.Empty, b => b.ToJson().HasColumnType(JsonColumnType)); + modelBuilder.Entity().OwnsOne(x => x.FieldOnly, b => b.ToJson().HasColumnType(JsonColumnType)); + } - public class Entity32939 - { - public int Id { get; set; } - public JsonEmpty32939 Empty { get; set; } - public JsonFieldOnly32939 FieldOnly { get; set; } + public class Entity32939 + { + public int Id { get; set; } + public JsonEmpty32939 Empty { get; set; } + public JsonFieldOnly32939 FieldOnly { get; set; } - } + } - public class JsonEmpty32939 - { - } + public class JsonEmpty32939 + { + } - public class JsonFieldOnly32939 - { - public int Field; - } + public class JsonFieldOnly32939 + { + public int Field; } #endregion #region 33046 - protected abstract Task Seed33046(Context33046 ctx); + protected abstract Task Seed33046(DbContext ctx); [ConditionalFact] public virtual async Task Query_with_nested_json_collection_mapped_to_private_field_via_IReadOnlyList() { - var contextFactory = await InitializeAsync(seed: Seed33046); + var contextFactory = await InitializeAsync( + onModelCreating: BuildModel33046, + onConfiguring: b => b.ConfigureWarnings(ConfigureWarnings), + seed: Seed33046); + using var context = contextFactory.CreateContext(); - var query = context.Reviews.ToList(); + var query = context.Set().ToList(); Assert.Equal(1, query.Count); } - protected class Context33046(DbContextOptions options) : DbContext(options) + protected virtual void BuildModel33046(ModelBuilder modelBuilder) + => modelBuilder.Entity( + b => + { + b.ToTable("Reviews"); + b.Property(x => x.Id).ValueGeneratedNever(); + b.OwnsMany( + x => x.Rounds, ownedBuilder => + { + ownedBuilder.ToJson().HasColumnType(JsonColumnType); + ownedBuilder.OwnsMany(r => r.SubRounds); + }); + }); + + public class Review { - public DbSet Reviews { get; set; } - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - modelBuilder.Entity().Property(x => x.Id).ValueGeneratedNever(); - modelBuilder.Entity().OwnsMany( - x => x.Rounds, ownedBuilder => - { - ownedBuilder.ToJson(); - ownedBuilder.OwnsMany(r => r.SubRounds); - }); - } - - public class Review - { - public int Id { get; set; } + public int Id { get; set; } #pragma warning disable IDE0044 // Add readonly modifier - private List _rounds = []; + private List _rounds = []; #pragma warning restore IDE0044 // Add readonly modifier - public IReadOnlyList Rounds - => _rounds.AsReadOnly(); - } + public IReadOnlyList Rounds + => _rounds.AsReadOnly(); + } - public class ReviewRound - { - public int RoundNumber { get; set; } + public class ReviewRound + { + public int RoundNumber { get; set; } #pragma warning disable IDE0044 // Add readonly modifier - private List _subRounds = []; + private List _subRounds = []; #pragma warning restore IDE0044 // Add readonly modifier - public IReadOnlyList SubRounds - => _subRounds.AsReadOnly(); - } + public IReadOnlyList SubRounds + => _subRounds.AsReadOnly(); + } - public class SubRound - { - public int SubRoundNumber { get; set; } - } + public class SubRound + { + public int SubRoundNumber { get; set; } } #endregion @@ -378,12 +386,14 @@ public class SubRound [MemberData(nameof(IsAsyncData))] public virtual async Task Project_json_array_of_primitives_on_reference(bool async) { - var contextFactory = await InitializeAsync( + var contextFactory = await InitializeAsync( + onModelCreating: BuildModelArrayOfPrimitives, + onConfiguring: b => b.ConfigureWarnings(ConfigureWarnings), seed: SeedArrayOfPrimitives); using (var context = contextFactory.CreateContext()) { - var query = context.Entities.OrderBy(x => x.Id).Select(x => new { x.Reference.IntArray, x.Reference.ListOfString }); + var query = context.Set().OrderBy(x => x.Id).Select(x => new { x.Reference.IntArray, x.Reference.ListOfString }); var result = async ? await query.ToListAsync() @@ -401,12 +411,14 @@ public virtual async Task Project_json_array_of_primitives_on_reference(bool asy [MemberData(nameof(IsAsyncData))] public virtual async Task Project_json_array_of_primitives_on_collection(bool async) { - var contextFactory = await InitializeAsync( + var contextFactory = await InitializeAsync( + onModelCreating: BuildModelArrayOfPrimitives, + onConfiguring: b => b.ConfigureWarnings(ConfigureWarnings), seed: SeedArrayOfPrimitives); using (var context = contextFactory.CreateContext()) { - var query = context.Entities.OrderBy(x => x.Id).Select(x => new { x.Collection[0].IntArray, x.Collection[1].ListOfString }); + var query = context.Set().OrderBy(x => x.Id).Select(x => new { x.Collection[0].IntArray, x.Collection[1].ListOfString }); var result = async ? await query.ToListAsync() @@ -424,12 +436,14 @@ public virtual async Task Project_json_array_of_primitives_on_collection(bool as [MemberData(nameof(IsAsyncData))] public virtual async Task Project_element_of_json_array_of_primitives(bool async) { - var contextFactory = await InitializeAsync( + var contextFactory = await InitializeAsync( + onModelCreating: BuildModelArrayOfPrimitives, + onConfiguring: b => b.ConfigureWarnings(ConfigureWarnings), seed: SeedArrayOfPrimitives); using (var context = contextFactory.CreateContext()) { - var query = context.Entities.OrderBy(x => x.Id).Select( + var query = context.Set().OrderBy(x => x.Id).Select( x => new { ArrayElement = x.Reference.IntArray[0], ListElement = x.Reference.ListOfString[1] }); var result = async @@ -442,12 +456,14 @@ public virtual async Task Project_element_of_json_array_of_primitives(bool async [MemberData(nameof(IsAsyncData))] public virtual async Task Predicate_based_on_element_of_json_array_of_primitives1(bool async) { - var contextFactory = await InitializeAsync( + var contextFactory = await InitializeAsync( + onModelCreating: BuildModelArrayOfPrimitives, + onConfiguring: b => b.ConfigureWarnings(ConfigureWarnings), seed: SeedArrayOfPrimitives); using (var context = contextFactory.CreateContext()) { - var query = context.Entities.Where(x => x.Reference.IntArray[0] == 1); + var query = context.Set().Where(x => x.Reference.IntArray[0] == 1); var result = async ? await query.ToListAsync() @@ -462,12 +478,14 @@ public virtual async Task Predicate_based_on_element_of_json_array_of_primitives [MemberData(nameof(IsAsyncData))] public virtual async Task Predicate_based_on_element_of_json_array_of_primitives2(bool async) { - var contextFactory = await InitializeAsync( + var contextFactory = await InitializeAsync( + onModelCreating: BuildModelArrayOfPrimitives, + onConfiguring: b => b.ConfigureWarnings(ConfigureWarnings), seed: SeedArrayOfPrimitives); using (var context = contextFactory.CreateContext()) { - var query = context.Entities.Where(x => x.Reference.ListOfString[1] == "Bar"); + var query = context.Set().Where(x => x.Reference.ListOfString[1] == "Bar"); var result = async ? await query.ToListAsync() @@ -482,12 +500,14 @@ public virtual async Task Predicate_based_on_element_of_json_array_of_primitives [MemberData(nameof(IsAsyncData))] public virtual async Task Predicate_based_on_element_of_json_array_of_primitives3(bool async) { - var contextFactory = await InitializeAsync( + var contextFactory = await InitializeAsync( + onModelCreating: BuildModelArrayOfPrimitives, + onConfiguring: b => b.ConfigureWarnings(ConfigureWarnings), seed: SeedArrayOfPrimitives); using (var context = contextFactory.CreateContext()) { - var query = context.Entities.Where( + var query = context.Set().Where( x => x.Reference.IntArray.AsQueryable().ElementAt(0) == 1 || x.Reference.ListOfString.AsQueryable().ElementAt(1) == "Bar") .OrderBy(e => e.Id); @@ -502,21 +522,16 @@ public virtual async Task Predicate_based_on_element_of_json_array_of_primitives } } - protected abstract Task SeedArrayOfPrimitives(MyContextArrayOfPrimitives ctx); + protected abstract Task SeedArrayOfPrimitives(DbContext ctx); - protected class MyContextArrayOfPrimitives(DbContextOptions options) : DbContext(options) + protected virtual void BuildModelArrayOfPrimitives(ModelBuilder modelBuilder) { - 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().Property(x => x.Id).ValueGeneratedNever(); + modelBuilder.Entity().OwnsOne( + x => x.Reference, b => b.ToJson().HasColumnType(JsonColumnType)); - modelBuilder.Entity().OwnsMany( - x => x.Collection, b => b.ToJson()); - } + modelBuilder.Entity().OwnsMany( + x => x.Collection, b => b.ToJson().HasColumnType(JsonColumnType)); } public class MyEntityArrayOfPrimitives @@ -540,12 +555,14 @@ public class MyJsonEntityArrayOfPrimitives [MemberData(nameof(IsAsyncData))] public virtual async Task Junk_in_json_basic_tracking(bool async) { - var contextFactory = await InitializeAsync( + var contextFactory = await InitializeAsync( + onModelCreating: BuildModelJunkInJson, + onConfiguring: b => b.ConfigureWarnings(ConfigureWarnings), seed: SeedJunkInJson); using (var context = contextFactory.CreateContext()) { - var query = context.Entities; + var query = context.Set(); var result = async ? await query.ToListAsync() @@ -565,12 +582,14 @@ public virtual async Task Junk_in_json_basic_tracking(bool async) [MemberData(nameof(IsAsyncData))] public virtual async Task Junk_in_json_basic_no_tracking(bool async) { - var contextFactory = await InitializeAsync( + var contextFactory = await InitializeAsync( + onModelCreating: BuildModelJunkInJson, + onConfiguring: b => b.ConfigureWarnings(ConfigureWarnings), seed: SeedJunkInJson); using (var context = contextFactory.CreateContext()) { - var query = context.Entities.AsNoTracking(); + var query = context.Set().AsNoTracking(); var result = async ? await query.ToListAsync() @@ -586,45 +605,47 @@ public virtual async Task Junk_in_json_basic_no_tracking(bool async) } } - protected abstract Task SeedJunkInJson(MyContextJunkInJson ctx); - - protected class MyContextJunkInJson(DbContextOptions options) : DbContext(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(); - b.OwnsOne(x => x.NestedReference); - b.OwnsMany(x => x.NestedCollection); - }); - modelBuilder.Entity().OwnsOne( - x => x.ReferenceWithCtor, b => - { - b.ToJson(); - b.OwnsOne(x => x.NestedReference); - b.OwnsMany(x => x.NestedCollection); - }); - modelBuilder.Entity().OwnsMany( - x => x.Collection, b => - { - b.ToJson(); - b.OwnsOne(x => x.NestedReference); - b.OwnsMany(x => x.NestedCollection); - }); - modelBuilder.Entity().OwnsMany( - x => x.CollectionWithCtor, b => - { - b.ToJson(); - b.OwnsOne(x => x.NestedReference); - b.OwnsMany(x => x.NestedCollection); - }); - } - } + protected abstract Task SeedJunkInJson(DbContext ctx); + + protected virtual void BuildModelJunkInJson(ModelBuilder modelBuilder) + => modelBuilder.Entity( + b => + { + b.ToTable("Entities"); + b.Property(x => x.Id).ValueGeneratedNever(); + + b.OwnsOne( + x => x.Reference, b => + { + b.ToJson().HasColumnType(JsonColumnType); + b.OwnsOne(x => x.NestedReference); + b.OwnsMany(x => x.NestedCollection); + }); + + b.OwnsOne( + x => x.ReferenceWithCtor, b => + { + b.ToJson().HasColumnType(JsonColumnType); + b.OwnsOne(x => x.NestedReference); + b.OwnsMany(x => x.NestedCollection); + }); + + b.OwnsMany( + x => x.Collection, b => + { + b.ToJson().HasColumnType(JsonColumnType); + b.OwnsOne(x => x.NestedReference); + b.OwnsMany(x => x.NestedCollection); + }); + + b.OwnsMany( + x => x.CollectionWithCtor, b => + { + b.ToJson().HasColumnType(JsonColumnType); + b.OwnsOne(x => x.NestedReference); + b.OwnsMany(x => x.NestedCollection); + }); + }); public class MyEntityJunkInJson { @@ -671,12 +692,14 @@ public class MyJsonEntityJunkInJsonWithCtorNested(DateTime doB) [MemberData(nameof(IsAsyncData))] public virtual async Task Tricky_buffering_basic(bool async) { - var contextFactory = await InitializeAsync( + var contextFactory = await InitializeAsync( + onModelCreating: BuildModelTrickyBuffering, + onConfiguring: b => b.ConfigureWarnings(ConfigureWarnings), seed: SeedTrickyBuffering); using (var context = contextFactory.CreateContext()) { - var query = context.Entities; + var query = context.Set(); var result = async ? await query.ToListAsync() @@ -690,24 +713,22 @@ public virtual async Task Tricky_buffering_basic(bool async) } } - protected abstract Task SeedTrickyBuffering(MyContextTrickyBuffering ctx); - - protected class MyContextTrickyBuffering(DbContextOptions options) : DbContext(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(); - b.OwnsOne(x => x.NestedReference); - b.OwnsMany(x => x.NestedCollection); - }); - } - } + protected abstract Task SeedTrickyBuffering(DbContext ctx); + + protected virtual void BuildModelTrickyBuffering(ModelBuilder modelBuilder) + => modelBuilder.Entity( + b => + { + b.ToTable("Entities"); + b.Property(x => x.Id).ValueGeneratedNever(); + b.OwnsOne( + x => x.Reference, b => + { + b.ToJson().HasColumnType(JsonColumnType); + b.OwnsOne(x => x.NestedReference); + b.OwnsMany(x => x.NestedCollection); + }); + }); public class MyEntityTrickyBuffering { @@ -736,12 +757,14 @@ public class MyJsonEntityTrickyBufferingNested [MemberData(nameof(IsAsyncData))] public virtual async Task Shadow_properties_basic_tracking(bool async) { - var contextFactory = await InitializeAsync( + var contextFactory = await InitializeAsync( + onModelCreating: BuildModelShadowProperties, + onConfiguring: b => b.ConfigureWarnings(ConfigureWarnings), seed: SeedShadowProperties); using (var context = contextFactory.CreateContext()) { - var query = context.Entities; + var query = context.Set(); var result = async ? await query.ToListAsync() @@ -775,12 +798,14 @@ public virtual async Task Shadow_properties_basic_tracking(bool async) [MemberData(nameof(IsAsyncData))] public virtual async Task Shadow_properties_basic_no_tracking(bool async) { - var contextFactory = await InitializeAsync( + var contextFactory = await InitializeAsync( + onModelCreating: BuildModelShadowProperties, + onConfiguring: b => b.ConfigureWarnings(ConfigureWarnings), seed: SeedShadowProperties); using (var context = contextFactory.CreateContext()) { - var query = context.Entities.AsNoTracking(); + var query = context.Set().AsNoTracking(); var result = async ? await query.ToListAsync() @@ -798,12 +823,14 @@ public virtual async Task Shadow_properties_basic_no_tracking(bool async) [MemberData(nameof(IsAsyncData))] public virtual async Task Project_shadow_properties_from_json_entity(bool async) { - var contextFactory = await InitializeAsync( + var contextFactory = await InitializeAsync( + onModelCreating: BuildModelShadowProperties, + onConfiguring: b => b.ConfigureWarnings(ConfigureWarnings), seed: SeedShadowProperties); using (var context = contextFactory.CreateContext()) { - var query = context.Entities.Select( + var query = context.Set().Select( x => new { ShadowString = EF.Property(x.Reference, "ShadowString"), @@ -820,40 +847,44 @@ public virtual async Task Project_shadow_properties_from_json_entity(bool async) } } - protected abstract Task SeedShadowProperties(MyContextShadowProperties ctx); + protected abstract Task SeedShadowProperties(DbContext ctx); - protected class MyContextShadowProperties(DbContextOptions options) : DbContext(options) + protected virtual void BuildModelShadowProperties(ModelBuilder modelBuilder) { - 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(); - b.Property("ShadowString"); - }); - modelBuilder.Entity().OwnsOne( - x => x.ReferenceWithCtor, b => - { - b.ToJson(); - b.Property("Shadow_Int").HasJsonPropertyName("ShadowInt"); - }); - modelBuilder.Entity().OwnsMany( - x => x.Collection, b => - { - b.ToJson(); - b.Property("ShadowDouble"); - }); - modelBuilder.Entity().OwnsMany( - x => x.CollectionWithCtor, b => - { - b.ToJson(); - b.Property("ShadowNullableByte"); - }); - } + modelBuilder.Entity( + b => + { + b.ToTable("Entities"); + b.Property(x => x.Id).ValueGeneratedNever(); + + b.OwnsOne( + x => x.Reference, b => + { + b.ToJson().HasColumnType(JsonColumnType); + b.Property("ShadowString"); + }); + + b.OwnsOne( + x => x.ReferenceWithCtor, b => + { + b.ToJson().HasColumnType(JsonColumnType); + b.Property("Shadow_Int").HasJsonPropertyName("ShadowInt"); + }); + + b.OwnsMany( + x => x.Collection, b => + { + b.ToJson().HasColumnType(JsonColumnType); + b.Property("ShadowDouble"); + }); + + b.OwnsMany( + x => x.CollectionWithCtor, b => + { + b.ToJson().HasColumnType(JsonColumnType); + b.Property("ShadowNullableByte"); + }); + }); } public class MyEntityShadowProperties @@ -885,14 +916,19 @@ public class MyJsonEntityShadowPropertiesWithCtor(string name) [MemberData(nameof(IsAsyncData))] public virtual async Task Project_proxies_entity_with_json(bool async) { - var contextFactory = await InitializeAsync( + var contextFactory = await InitializeAsync( + onModelCreating: BuildModelLazyLoadingProxies, seed: SeedLazyLoadingProxies, - onConfiguring: OnConfiguringLazyLoadingProxies, + onConfiguring: b => + { + b = b.ConfigureWarnings(ConfigureWarnings); + OnConfiguringLazyLoadingProxies(b); + }, addServices: AddServicesLazyLoadingProxies); using (var context = contextFactory.CreateContext()) { - var query = context.Entities; + var query = context.Set(); var result = async ? await query.ToListAsync() @@ -908,7 +944,7 @@ protected void OnConfiguringLazyLoadingProxies(DbContextOptionsBuilder optionsBu protected IServiceCollection AddServicesLazyLoadingProxies(IServiceCollection addServices) => addServices.AddEntityFrameworkProxies(); - private Task SeedLazyLoadingProxies(MyContextLazyLoadingProxies ctx) + private Task SeedLazyLoadingProxies(DbContext ctx) { var r1 = new MyJsonEntityLazyLoadingProxiesWithCtor("r1", 1); var c11 = new MyJsonEntityLazyLoadingProxies { Name = "c11", Number = 11 }; @@ -940,20 +976,15 @@ private Task SeedLazyLoadingProxies(MyContextLazyLoadingProxies ctx) Collection = [c21, c22] }; - ctx.Entities.AddRange(e1, e2); + ctx.Set().AddRange(e1, e2); return ctx.SaveChangesAsync(); } - protected class MyContextLazyLoadingProxies(DbContextOptions options) : DbContext(options) + protected virtual void BuildModelLazyLoadingProxies(ModelBuilder modelBuilder) { - 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()); - } + modelBuilder.Entity().Property(x => x.Id).ValueGeneratedNever(); + modelBuilder.Entity().OwnsOne(x => x.Reference, b => b.ToJson().HasColumnType(JsonColumnType)); + modelBuilder.Entity().OwnsMany(x => x.Collection, b => b.ToJson().HasColumnType(JsonColumnType)); } public class MyEntityLazyLoadingProxies @@ -985,12 +1016,14 @@ public class MyJsonEntityLazyLoadingProxies [MemberData(nameof(IsAsyncData))] public virtual async Task Not_ICollection_basic_projection(bool async) { - var contextFactory = await InitializeAsync( + var contextFactory = await InitializeAsync( + onModelCreating: BuildModelNotICollection, + onConfiguring: b => b.ConfigureWarnings(ConfigureWarnings), seed: SeedNotICollection); using (var context = contextFactory.CreateContext()) { - var query = context.Entities; + var query = context.Set(); var result = async ? await query.ToListAsync() @@ -1000,7 +1033,7 @@ public virtual async Task Not_ICollection_basic_projection(bool async) } } - protected abstract Task SeedNotICollection(MyContextNotICollection ctx); + protected abstract Task SeedNotICollection(DbContext ctx); public class MyEntityNotICollection { @@ -1023,24 +1056,25 @@ public class MyJsonNestedEntityNotICollection public int Bar { get; set; } } - public class MyContextNotICollection(DbContextOptions options) : DbContext(options) - { - public DbSet Entities { get; set; } - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - modelBuilder.Entity().Property(x => x.Id).ValueGeneratedNever(); - modelBuilder.Entity().OwnsOne( - cr => cr.Json, nb => - { - nb.ToJson(); - nb.OwnsMany(x => x.Collection); - }); - } - } + protected virtual void BuildModelNotICollection(ModelBuilder modelBuilder) + => modelBuilder.Entity( + b => + { + b.ToTable("Entities"); + b.Property(x => x.Id).ValueGeneratedNever(); + b.OwnsOne( + cr => cr.Json, nb => + { + nb.ToJson().HasColumnType(JsonColumnType); + nb.OwnsMany(x => x.Collection); + }); + }); #endregion protected TestSqlLoggerFactory TestSqlLoggerFactory => (TestSqlLoggerFactory)ListLoggerFactory; + + protected virtual string JsonColumnType + => null; } diff --git a/test/EFCore.Relational.Specification.Tests/Query/JsonQueryRelationalFixture.cs b/test/EFCore.Relational.Specification.Tests/Query/JsonQueryRelationalFixture.cs index 7004dac0a80..8a054c59f57 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/JsonQueryRelationalFixture.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/JsonQueryRelationalFixture.cs @@ -7,7 +7,7 @@ namespace Microsoft.EntityFrameworkCore.Query; #nullable disable -public abstract class JsonQueryRelationalFixture: JsonQueryFixtureBase, ITestSqlLoggerFactory +public abstract class JsonQueryRelationalFixture : JsonQueryFixtureBase, ITestSqlLoggerFactory { public new RelationalTestStore TestStore => (RelationalTestStore)base.TestStore; @@ -21,12 +21,25 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con modelBuilder.Entity().OwnsOne(x => x.OwnedReferenceRoot).ToJson(); modelBuilder.Entity().OwnsMany(x => x.OwnedCollectionRoot).ToJson(); - - modelBuilder.Entity().OwnsOne(x => x.OwnedReferenceRoot).ToJson("json_reference_custom_naming"); - modelBuilder.Entity().OwnsMany(x => x.OwnedCollectionRoot).ToJson("json_collection_custom_naming"); - + + modelBuilder.Entity().OwnsOne( + x => x.OwnedReferenceRoot, b => + { + b.ToJson("json_reference_custom_naming"); + b.OwnsOne(x => x.OwnedReferenceBranch); + b.OwnsMany(x => x.OwnedCollectionBranch); + }); + + modelBuilder.Entity().OwnsMany( + x => x.OwnedCollectionRoot, b => + { + b.ToJson("json_collection_custom_naming"); + b.OwnsOne(x => x.OwnedReferenceBranch); + b.OwnsMany(x => x.OwnedCollectionBranch); + }); + modelBuilder.Entity().OwnsMany(x => x.OwnedCollection).ToJson(); - + modelBuilder.Entity( b => { diff --git a/test/EFCore.Relational.Specification.Tests/TestUtilities/RelationalModelAsserter.cs b/test/EFCore.Relational.Specification.Tests/TestUtilities/RelationalModelAsserter.cs index e76efee1a10..d096745b403 100644 --- a/test/EFCore.Relational.Specification.Tests/TestUtilities/RelationalModelAsserter.cs +++ b/test/EFCore.Relational.Specification.Tests/TestUtilities/RelationalModelAsserter.cs @@ -205,6 +205,7 @@ public override bool AssertEqual( Assert.Multiple( () => Assert.Equal(expected.GetDbSetName(), actual.GetDbSetName()), () => Assert.Equal(expected.GetContainerColumnName(), actual.GetContainerColumnName()), + () => Assert.Equal(expected.GetContainerColumnType(), actual.GetContainerColumnType()), () => Assert.Equal(expected.GetJsonPropertyName(), actual.GetJsonPropertyName()), () => { @@ -543,6 +544,7 @@ public override bool AssertEqual( Assert.Multiple( () => Assert.Equal(expected.GetContainerColumnName(), actual.GetContainerColumnName()), + () => Assert.Equal(expected.GetContainerColumnType(), actual.GetContainerColumnType()), () => Assert.Equal(expectedStructuralType.GetJsonPropertyName(), actualStructuralType.GetJsonPropertyName()), () => Assert.Equal(expectedStructuralType.GetTableName(), actualStructuralType.GetTableName()), () => Assert.Equal(expectedStructuralType.GetViewName(), actualStructuralType.GetViewName()), diff --git a/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.Json.cs b/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.Json.cs index 203b04f5174..52cdd7793d2 100644 --- a/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.Json.cs +++ b/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.Json.cs @@ -5,6 +5,50 @@ namespace Microsoft.EntityFrameworkCore.Infrastructure; public partial class RelationalModelValidatorTest { + [ConditionalFact] + public void Throw_when_non_json_entity_has_column_type_set() + { + var modelBuilder = CreateConventionModelBuilder(); + modelBuilder.Entity( + b => + { + b.OwnsOne( + x => x.OwnedReference, bb => + { + bb.HasColumnType("nvarchar(2000)"); + bb.Ignore(x => x.NestedCollection); + bb.Ignore(x => x.NestedReference); + }); + b.Ignore(x => x.OwnedCollection); + }); + + VerifyError( + RelationalStrings.ContainerTypeOnNonContainer(nameof(ValidatorJsonOwnedRoot)), + modelBuilder); + } + + [ConditionalFact] + public void Throw_when_non_root_json_entity_has_column_type_set() + { + var modelBuilder = CreateConventionModelBuilder(); + modelBuilder.Entity( + b => + { + b.OwnsOne( + x => x.OwnedReference, bb => + { + bb.ToJson(); + bb.Ignore(x => x.NestedCollection); + bb.OwnsOne(x => x.NestedReference, bbb => bbb.HasColumnType("nvarchar(2000)")); + }); + b.Ignore(x => x.OwnedCollection); + }); + + VerifyError( + RelationalStrings.ContainerTypeOnNestedOwnedEntityType(nameof(ValidatorJsonOwnedBranch)), + modelBuilder); + } + [ConditionalFact] public void Throw_when_non_json_entity_is_the_owner_of_json_entity_ref_ref() { @@ -614,26 +658,26 @@ public void SeedData_on_entity_with_json_navigation_throws_meaningful_exception( modelBuilder); } - private class ValidatorJsonEntityBasic + protected class ValidatorJsonEntityBasic { public int Id { get; set; } public ValidatorJsonOwnedRoot OwnedReference { get; set; } public List OwnedCollection { get; set; } } - private abstract class ValidatorJsonEntityInheritanceAbstract : ValidatorJsonEntityInheritanceBase + protected abstract class ValidatorJsonEntityInheritanceAbstract : ValidatorJsonEntityInheritanceBase { public Guid Guid { get; set; } } - private class ValidatorJsonEntityInheritanceBase + protected class ValidatorJsonEntityInheritanceBase { public int Id { get; set; } public string Name { get; set; } public ValidatorJsonOwnedBranch ReferenceOnBase { get; set; } } - private class ValidatorJsonEntityInheritanceDerived : ValidatorJsonEntityInheritanceAbstract + protected class ValidatorJsonEntityInheritanceDerived : ValidatorJsonEntityInheritanceAbstract { public bool Switch { get; set; } @@ -642,7 +686,7 @@ private class ValidatorJsonEntityInheritanceDerived : ValidatorJsonEntityInherit public List CollectionOnDerived { get; set; } } - private class ValidatorJsonOwnedRoot + protected class ValidatorJsonOwnedRoot { public string Name { get; set; } public int Number { get; } @@ -651,12 +695,12 @@ private class ValidatorJsonOwnedRoot public List NestedCollection { get; } } - private class ValidatorJsonOwnedBranch + protected class ValidatorJsonOwnedBranch { public double Number { get; set; } } - private class ValidatorJsonEntityExplicitOrdinal + protected class ValidatorJsonEntityExplicitOrdinal { public int Id { get; set; } @@ -665,19 +709,19 @@ private class ValidatorJsonEntityExplicitOrdinal public List OwnedCollection { get; set; } } - private class ValidatorJsonOwnedExplicitOrdinal + protected class ValidatorJsonOwnedExplicitOrdinal { public int Ordinal { get; set; } public DateTime Date { get; set; } } - private class ValidatorJsonEntityJsonReferencingRegularEntity + protected class ValidatorJsonEntityJsonReferencingRegularEntity { public int Id { get; set; } public ValidatorJsonOwnedReferencingRegularEntity Owned { get; set; } } - private class ValidatorJsonOwnedReferencingRegularEntity + protected class ValidatorJsonOwnedReferencingRegularEntity { public string Foo { get; set; } @@ -685,13 +729,13 @@ private class ValidatorJsonOwnedReferencingRegularEntity public ValidatorJsonEntityReferencedEntity Reference { get; } } - private class ValidatorJsonEntityReferencedEntity + protected class ValidatorJsonEntityReferencedEntity { public int Id { get; set; } public DateTime Date { get; set; } } - private class ValidatorJsonEntitySideBySide + protected class ValidatorJsonEntitySideBySide { public int Id { get; set; } public string Name { get; set; } @@ -701,7 +745,7 @@ private class ValidatorJsonEntitySideBySide public List Collection2 { get; set; } } - private class ValidatorJsonEntityTableSplitting + protected class ValidatorJsonEntityTableSplitting { public int Id { get; set; } public ValidatorJsonEntityBasic Link { get; set; } diff --git a/test/EFCore.Specification.Tests/Query/JsonQueryFixtureBase.cs b/test/EFCore.Specification.Tests/Query/JsonQueryFixtureBase.cs index db55c4a6700..9044d53af71 100644 --- a/test/EFCore.Specification.Tests/Query/JsonQueryFixtureBase.cs +++ b/test/EFCore.Specification.Tests/Query/JsonQueryFixtureBase.cs @@ -651,7 +651,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con }); } - protected override string StoreName { get; } = "JsonQueryTest"; + protected override string StoreName + => "JsonQueryTest"; public override JsonQueryContext CreateContext() { diff --git a/test/EFCore.Specification.Tests/TestModels/JsonQuery/JsonQueryData.cs b/test/EFCore.Specification.Tests/TestModels/JsonQuery/JsonQueryData.cs index 4d50437b494..ce165a2824d 100644 --- a/test/EFCore.Specification.Tests/TestModels/JsonQuery/JsonQueryData.cs +++ b/test/EFCore.Specification.Tests/TestModels/JsonQuery/JsonQueryData.cs @@ -1169,6 +1169,7 @@ public static IReadOnlyList CreateJsonEntitiesAllTypes() "S3" ], TestBooleanCollection = new[] { true, false }, + TestByteCollection = [], TestCharacterCollection = [ 'A', @@ -1263,6 +1264,7 @@ public static IReadOnlyList CreateJsonEntitiesAllTypes() "S3" ], TestBooleanCollection = new[] { true, false }, + TestByteCollection = [], TestCharacterCollection = [ 'A', diff --git a/test/EFCore.Specification.Tests/TestUtilities/Xunit/SkipOnCiConditionAttribute.cs b/test/EFCore.Specification.Tests/TestUtilities/Xunit/SkipOnCiConditionAttribute.cs new file mode 100644 index 00000000000..66f7f760ed3 --- /dev/null +++ b/test/EFCore.Specification.Tests/TestUtilities/Xunit/SkipOnCiConditionAttribute.cs @@ -0,0 +1,14 @@ +// 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.TestUtilities.Xunit; + +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly)] +public sealed class SkipOnCiConditionAttribute : Attribute, ITestCondition +{ + public ValueTask IsMetAsync() + => new(Environment.GetEnvironmentVariable("PIPELINE_WORKSPACE") == null + && Environment.GetEnvironmentVariable("GITHUB_RUN_ID") == null); + + public string SkipReason { get; set; } = "Tests not reliable on C.I."; +} diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/AdHocJsonQuerySqlServerJsonTypeTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/AdHocJsonQuerySqlServerJsonTypeTest.cs new file mode 100644 index 00000000000..fb0e7c2d8ec --- /dev/null +++ b/test/EFCore.SqlServer.FunctionalTests/Query/AdHocJsonQuerySqlServerJsonTypeTest.cs @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable disable +using Microsoft.Data.SqlClient; + +namespace Microsoft.EntityFrameworkCore.Query; + +[SqlServerCondition(SqlServerCondition.SupportsJsonType)] +public class AdHocJsonQuerySqlServerJsonTypeTest : AdHocJsonQuerySqlServerTestBase +{ + public override async Task Missing_navigation_works_with_deduplication(bool async) + { + // TODO:SQLJSON Returns empty (invalid) JSON (See BadJson.cs) + if (async) + { + Assert.Equal( + "Unable to cast object of type 'System.DBNull' to type 'System.String'.", + (await Assert.ThrowsAsync(() => base.Missing_navigation_works_with_deduplication(true))).Message); + } + else + { + Assert.Equal( + RelationalStrings.JsonEmptyString, + (await Assert.ThrowsAsync(() => base.Missing_navigation_works_with_deduplication(false))) + .Message); + } + } + + public override async Task Contains_on_nested_collection_with_init_only_navigation(bool async) + // TODO:SQLJSON (See JsonTypeToFunction.cs) + => Assert.Equal( + "OpenJson support not yet supported for JSON native data type.", + (await Assert.ThrowsAsync( + () => base.Contains_on_nested_collection_with_init_only_navigation(async))).Message); + + protected override string StoreName + => "AdHocJsonQueryJsonTypeTest"; + + protected override string JsonColumnType + => "json"; +} diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/AdHocJsonQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/AdHocJsonQuerySqlServerTest.cs index 3f42c52b152..02bdd29574b 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/AdHocJsonQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/AdHocJsonQuerySqlServerTest.cs @@ -1,384 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.Data.SqlClient; -using Microsoft.EntityFrameworkCore.Diagnostics.Internal; -using Microsoft.EntityFrameworkCore.SqlServer.Diagnostics.Internal; - namespace Microsoft.EntityFrameworkCore.Query; #nullable disable -public class AdHocJsonQuerySqlServerTest : AdHocJsonQueryTestBase +public class AdHocJsonQuerySqlServerTest : AdHocJsonQuerySqlServerTestBase { - protected override ITestStoreFactory TestStoreFactory - => SqlServerTestStoreFactory.Instance; - - protected override async Task Seed29219(MyContext29219 ctx) - { - var entity1 = new MyEntity29219 - { - Id = 1, - Reference = new MyJsonEntity29219 { NonNullableScalar = 10, NullableScalar = 11 }, - Collection = - [ - new() { NonNullableScalar = 100, NullableScalar = 101 }, - new() { NonNullableScalar = 200, NullableScalar = 201 }, - new() { NonNullableScalar = 300, NullableScalar = null } - ] - }; - - var entity2 = new MyEntity29219 - { - Id = 2, - Reference = new MyJsonEntity29219 { NonNullableScalar = 20, NullableScalar = null }, - Collection = [new() { NonNullableScalar = 1001, NullableScalar = null }] - }; - - ctx.Entities.AddRange(entity1, entity2); - await ctx.SaveChangesAsync(); - - await ctx.Database.ExecuteSqlAsync( - $$""" -INSERT INTO [Entities] ([Id], [Reference], [Collection]) -VALUES(3, N'{ "NonNullableScalar" : 30 }', N'[{ "NonNullableScalar" : 10001 }]') -"""); - } - - protected override async Task Seed30028(MyContext30028 ctx) - { - // complete - await ctx.Database.ExecuteSqlAsync( - $$$$""" -INSERT INTO [Entities] ([Id], [Json]) -VALUES( -1, -N'{"RootName":"e1","Collection":[{"BranchName":"e1 c1","Nested":{"LeafName":"e1 c1 l"}},{"BranchName":"e1 c2","Nested":{"LeafName":"e1 c2 l"}}],"OptionalReference":{"BranchName":"e1 or","Nested":{"LeafName":"e1 or l"}},"RequiredReference":{"BranchName":"e1 rr","Nested":{"LeafName":"e1 rr l"}}}') -"""); - - // missing collection - await ctx.Database.ExecuteSqlAsync( - $$$$""" -INSERT INTO [Entities] ([Id], [Json]) -VALUES( -2, -N'{"RootName":"e2","OptionalReference":{"BranchName":"e2 or","Nested":{"LeafName":"e2 or l"}},"RequiredReference":{"BranchName":"e2 rr","Nested":{"LeafName":"e2 rr l"}}}') -"""); - - // missing optional reference - await ctx.Database.ExecuteSqlAsync( - $$$$""" -INSERT INTO [Entities] ([Id], [Json]) -VALUES( -3, -N'{"RootName":"e3","Collection":[{"BranchName":"e3 c1","Nested":{"LeafName":"e3 c1 l"}},{"BranchName":"e3 c2","Nested":{"LeafName":"e3 c2 l"}}],"RequiredReference":{"BranchName":"e3 rr","Nested":{"LeafName":"e3 rr l"}}}') -"""); - - // missing required reference - await ctx.Database.ExecuteSqlAsync( - $$$$""" -INSERT INTO [Entities] ([Id], [Json]) -VALUES( -4, -N'{"RootName":"e4","Collection":[{"BranchName":"e4 c1","Nested":{"LeafName":"e4 c1 l"}},{"BranchName":"e4 c2","Nested":{"LeafName":"e4 c2 l"}}],"OptionalReference":{"BranchName":"e4 or","Nested":{"LeafName":"e4 or l"}}}') -"""); - } - - protected override Task Seed33046(Context33046 ctx) - => ctx.Database.ExecuteSqlAsync( - $$""" -INSERT INTO [Reviews] ([Rounds], [Id]) -VALUES(N'[{"RoundNumber":11,"SubRounds":[{"SubRoundNumber":111},{"SubRoundNumber":112}]}]', 1) -"""); - - protected override Task SeedArrayOfPrimitives(MyContextArrayOfPrimitives ctx) - { - var entity1 = new MyEntityArrayOfPrimitives - { - Id = 1, - Reference = new MyJsonEntityArrayOfPrimitives - { - IntArray = [1, 2, 3], - ListOfString = - [ - "Foo", - "Bar", - "Baz" - ] - }, - Collection = - [ - new() { IntArray = [111, 112, 113], ListOfString = ["Foo11", "Bar11"] }, - new() { IntArray = [211, 212, 213], ListOfString = ["Foo12", "Bar12"] } - ] - }; - - var entity2 = new MyEntityArrayOfPrimitives - { - Id = 2, - Reference = new MyJsonEntityArrayOfPrimitives - { - IntArray = [10, 20, 30], - ListOfString = - [ - "A", - "B", - "C" - ] - }, - Collection = - [ - new() { IntArray = [110, 120, 130], ListOfString = ["A1", "Z1"] }, - new() { IntArray = [210, 220, 230], ListOfString = ["A2", "Z2"] } - ] - }; - - ctx.Entities.AddRange(entity1, entity2); - return ctx.SaveChangesAsync(); - } - - protected override Task SeedJunkInJson(MyContextJunkInJson ctx) - => ctx.Database.ExecuteSqlAsync( - $$$$""" -INSERT INTO [Entities] ([Collection], [CollectionWithCtor], [Reference], [ReferenceWithCtor], [Id]) -VALUES( -N'[{"JunkReference":{"Something":"SomeValue" },"Name":"c11","JunkProperty1":50,"Number":11.5,"JunkCollection1":[],"JunkCollection2":[{"Foo":"junk value"}],"NestedCollection":[{"DoB":"2002-04-01T00:00:00","DummyProp":"Dummy value"},{"DoB":"2002-04-02T00:00:00","DummyReference":{"Foo":5}}],"NestedReference":{"DoB":"2002-03-01T00:00:00"}},{"Name":"c12","Number":12.5,"NestedCollection":[{"DoB":"2002-06-01T00:00:00"},{"DoB":"2002-06-02T00:00:00"}],"NestedDummy":59,"NestedReference":{"DoB":"2002-05-01T00:00:00"}}]', -N'[{"MyBool":true,"Name":"c11 ctor","JunkReference":{"Something":"SomeValue","JunkCollection":[{"Foo":"junk value"}]},"NestedCollection":[{"DoB":"2002-08-01T00:00:00"},{"DoB":"2002-08-02T00:00:00"}],"NestedReference":{"DoB":"2002-07-01T00:00:00"}},{"MyBool":false,"Name":"c12 ctor","NestedCollection":[{"DoB":"2002-10-01T00:00:00"},{"DoB":"2002-10-02T00:00:00"}],"JunkCollection":[{"Foo":"junk value"}],"NestedReference":{"DoB":"2002-09-01T00:00:00"}}]', -N'{"Name":"r1","JunkCollection":[{"Foo":"junk value"}],"JunkReference":{"Something":"SomeValue" },"Number":1.5,"NestedCollection":[{"DoB":"2000-02-01T00:00:00","JunkReference":{"Something":"SomeValue"}},{"DoB":"2000-02-02T00:00:00"}],"NestedReference":{"DoB":"2000-01-01T00:00:00"}}', -N'{"MyBool":true,"JunkCollection":[{"Foo":"junk value"}],"Name":"r1 ctor","JunkReference":{"Something":"SomeValue" },"NestedCollection":[{"DoB":"2001-02-01T00:00:00"},{"DoB":"2001-02-02T00:00:00"}],"NestedReference":{"JunkCollection":[{"Foo":"junk value"}],"DoB":"2001-01-01T00:00:00"}}', -1) -"""); - - protected override Task SeedTrickyBuffering(MyContextTrickyBuffering ctx) - => ctx.Database.ExecuteSqlAsync( - $$$""" -INSERT INTO [Entities] ([Reference], [Id]) -VALUES( -N'{"Name": "r1", "Number": 7, "JunkReference":{"Something": "SomeValue" }, "JunkCollection": [{"Foo": "junk value"}], "NestedReference": {"DoB": "2000-01-01T00:00:00"}, "NestedCollection": [{"DoB": "2000-02-01T00:00:00", "JunkReference": {"Something": "SomeValue"}}, {"DoB": "2000-02-02T00:00:00"}]}',1) -"""); - - protected override Task SeedShadowProperties(MyContextShadowProperties ctx) - => ctx.Database.ExecuteSqlAsync( - $$""" -INSERT INTO [Entities] ([Collection], [CollectionWithCtor], [Reference], [ReferenceWithCtor], [Id], [Name]) -VALUES( -N'[{"Name":"e1_c1","ShadowDouble":5.5},{"ShadowDouble":20.5,"Name":"e1_c2"}]', -N'[{"Name":"e1_c1 ctor","ShadowNullableByte":6},{"ShadowNullableByte":null,"Name":"e1_c2 ctor"}]', -N'{"Name":"e1_r", "ShadowString":"Foo"}', -N'{"ShadowInt":143,"Name":"e1_r ctor"}', -1, -N'e1') -"""); - - protected override async Task SeedNotICollection(MyContextNotICollection ctx) - { - await ctx.Database.ExecuteSqlAsync( - $$""" -INSERT INTO [Entities] ([Json], [Id]) -VALUES( -N'{"Collection":[{"Bar":11,"Foo":"c11"},{"Bar":12,"Foo":"c12"},{"Bar":13,"Foo":"c13"}]}', -1) -"""); - - await ctx.Database.ExecuteSqlAsync( - $$$""" -INSERT INTO [Entities] ([Json], [Id]) -VALUES( -N'{"Collection":[{"Bar":21,"Foo":"c21"},{"Bar":22,"Foo":"c22"}]}', -2) -"""); - } - - #region EnumLegacyValues - - [ConditionalTheory] - [MemberData(nameof(IsAsyncData))] - public virtual async Task Read_enum_property_with_legacy_values(bool async) - { - var contextFactory = await InitializeAsync( - seed: SeedEnumLegacyValues); - - using (var context = contextFactory.CreateContext()) - { - var query = context.Entities.Select( - x => new - { - x.Reference.IntEnum, - x.Reference.ByteEnum, - x.Reference.LongEnum, - x.Reference.NullableEnum - }); - - var exception = async - ? await (Assert.ThrowsAsync(() => query.ToListAsync())) - : Assert.Throws(() => query.ToList()); - - // Conversion failed when converting the nvarchar value '...' to data type int - Assert.Equal(245, exception.Number); - } - } - - [ConditionalTheory] - [MemberData(nameof(IsAsyncData))] - public virtual async Task Read_json_entity_with_enum_properties_with_legacy_values(bool async) - { - var contextFactory = await InitializeAsync( - seed: SeedEnumLegacyValues, - shouldLogCategory: c => c == DbLoggerCategory.Query.Name); - - using (var context = contextFactory.CreateContext()) - { - var query = context.Entities.Select(x => x.Reference).AsNoTracking(); - - var result = async - ? await query.ToListAsync() - : query.ToList(); - - Assert.Equal(1, result.Count); - Assert.Equal(ByteEnumLegacyValues.Redmond, result[0].ByteEnum); - Assert.Equal(IntEnumLegacyValues.Foo, result[0].IntEnum); - Assert.Equal(LongEnumLegacyValues.Three, result[0].LongEnum); - Assert.Equal(ULongEnumLegacyValues.Three, result[0].ULongEnum); - Assert.Equal(IntEnumLegacyValues.Bar, result[0].NullableEnum); - } - - var testLogger = new TestLogger(); - Assert.Single( - ListLoggerFactory.Log.Where( - l => l.Message == CoreResources.LogStringEnumValueInJson(testLogger).GenerateMessage(nameof(ByteEnumLegacyValues)))); - Assert.Single( - ListLoggerFactory.Log.Where( - l => l.Message == CoreResources.LogStringEnumValueInJson(testLogger).GenerateMessage(nameof(IntEnumLegacyValues)))); - Assert.Single( - ListLoggerFactory.Log.Where( - l => l.Message == CoreResources.LogStringEnumValueInJson(testLogger).GenerateMessage(nameof(LongEnumLegacyValues)))); - Assert.Single( - ListLoggerFactory.Log.Where( - l => l.Message == CoreResources.LogStringEnumValueInJson(testLogger).GenerateMessage(nameof(ULongEnumLegacyValues)))); - } - - [ConditionalTheory] - [MemberData(nameof(IsAsyncData))] - public virtual async Task Read_json_entity_collection_with_enum_properties_with_legacy_values(bool async) - { - var contextFactory = await InitializeAsync( - seed: SeedEnumLegacyValues, - shouldLogCategory: c => c == DbLoggerCategory.Query.Name); - - using (var context = contextFactory.CreateContext()) - { - var query = context.Entities.Select(x => x.Collection).AsNoTracking(); - - var result = async - ? await query.ToListAsync() - : query.ToList(); - - Assert.Equal(1, result.Count); - Assert.Equal(2, result[0].Count); - Assert.Equal(ByteEnumLegacyValues.Bellevue, result[0][0].ByteEnum); - Assert.Equal(IntEnumLegacyValues.Foo, result[0][0].IntEnum); - Assert.Equal(LongEnumLegacyValues.One, result[0][0].LongEnum); - Assert.Equal(ULongEnumLegacyValues.One, result[0][0].ULongEnum); - Assert.Equal(IntEnumLegacyValues.Bar, result[0][0].NullableEnum); - Assert.Equal(ByteEnumLegacyValues.Seattle, result[0][1].ByteEnum); - Assert.Equal(IntEnumLegacyValues.Baz, result[0][1].IntEnum); - Assert.Equal(LongEnumLegacyValues.Two, result[0][1].LongEnum); - Assert.Equal(ULongEnumLegacyValues.Two, result[0][1].ULongEnum); - Assert.Null(result[0][1].NullableEnum); - } - - var testLogger = new TestLogger(); - Assert.Single( - ListLoggerFactory.Log.Where( - l => l.Message == CoreResources.LogStringEnumValueInJson(testLogger).GenerateMessage(nameof(ByteEnumLegacyValues)))); - Assert.Single( - ListLoggerFactory.Log.Where( - l => l.Message == CoreResources.LogStringEnumValueInJson(testLogger).GenerateMessage(nameof(IntEnumLegacyValues)))); - Assert.Single( - ListLoggerFactory.Log.Where( - l => l.Message == CoreResources.LogStringEnumValueInJson(testLogger).GenerateMessage(nameof(LongEnumLegacyValues)))); - Assert.Single( - ListLoggerFactory.Log.Where( - l => l.Message == CoreResources.LogStringEnumValueInJson(testLogger).GenerateMessage(nameof(ULongEnumLegacyValues)))); - } - - private Task SeedEnumLegacyValues(MyContextEnumLegacyValues ctx) - => ctx.Database.ExecuteSqlAsync( - $$""" -INSERT INTO [Entities] ([Collection], [Reference], [Id], [Name]) -VALUES( -N'[{"ByteEnum":"Bellevue","IntEnum":"Foo","LongEnum":"One","ULongEnum":"One","Name":"e1_c1","NullableEnum":"Bar"},{"ByteEnum":"Seattle","IntEnum":"Baz","LongEnum":"Two","ULongEnum":"Two","Name":"e1_c2","NullableEnum":null}]', -N'{"ByteEnum":"Redmond","IntEnum":"Foo","LongEnum":"Three","ULongEnum":"Three","Name":"e1_r","NullableEnum":"Bar"}', -1, -N'e1') -"""); - - private class MyContextEnumLegacyValues(DbContextOptions options) : DbContext((new DbContextOptionsBuilder(options)).ConfigureWarnings(b => b.Log(CoreEventId.StringEnumValueInJson)).Options) - { - - // ReSharper disable once UnusedAutoPropertyAccessor.Local - 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()); - } - } - - private class MyEntityEnumLegacyValues - { - public int Id { get; set; } - public string Name { get; set; } - - public MyJsonEntityEnumLegacyValues Reference { get; set; } - public List Collection { get; set; } - } - - private class MyJsonEntityEnumLegacyValues - { - public string Name { get; set; } - - // ReSharper disable once UnusedAutoPropertyAccessor.Local - public IntEnumLegacyValues IntEnum { get; set; } - // ReSharper disable once UnusedAutoPropertyAccessor.Local - public ByteEnumLegacyValues ByteEnum { get; set; } - // ReSharper disable once UnusedAutoPropertyAccessor.Local - public LongEnumLegacyValues LongEnum { get; set; } - // ReSharper disable once UnusedAutoPropertyAccessor.Local - public ULongEnumLegacyValues ULongEnum { get; set; } - // ReSharper disable once UnusedAutoPropertyAccessor.Local - public IntEnumLegacyValues? NullableEnum { get; set; } - } - - private enum IntEnumLegacyValues - { - Foo = int.MinValue, - Bar, - Baz = int.MaxValue, - } - - private enum ByteEnumLegacyValues : byte - { - Seattle, - Redmond, - Bellevue = 255, - } - - private enum LongEnumLegacyValues : long - { - One = long.MinValue, - Two = 1, - Three = long.MaxValue, - } - - private enum ULongEnumLegacyValues : ulong - { - One = ulong.MinValue, - Two = 1, - Three = ulong.MaxValue, - } - - #endregion } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/AdHocJsonQuerySqlServerTestBase.cs b/test/EFCore.SqlServer.FunctionalTests/Query/AdHocJsonQuerySqlServerTestBase.cs new file mode 100644 index 00000000000..97c07f97af5 --- /dev/null +++ b/test/EFCore.SqlServer.FunctionalTests/Query/AdHocJsonQuerySqlServerTestBase.cs @@ -0,0 +1,392 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable disable +using Microsoft.Data.SqlClient; +using Microsoft.EntityFrameworkCore.Diagnostics.Internal; +using Microsoft.EntityFrameworkCore.SqlServer.Diagnostics.Internal; + +namespace Microsoft.EntityFrameworkCore.Query; + +public abstract class AdHocJsonQuerySqlServerTestBase : AdHocJsonQueryTestBase +{ + protected override ITestStoreFactory TestStoreFactory + => SqlServerTestStoreFactory.Instance; + + protected override void ConfigureWarnings(WarningsConfigurationBuilder builder) + { + base.ConfigureWarnings(builder); + + builder.Log(CoreEventId.StringEnumValueInJson, SqlServerEventId.JsonTypeExperimental); + } + + protected override async Task Seed29219(DbContext ctx) + { + var entity1 = new MyEntity29219 + { + Id = 1, + Reference = new MyJsonEntity29219 { NonNullableScalar = 10, NullableScalar = 11 }, + Collection = + [ + new() { NonNullableScalar = 100, NullableScalar = 101 }, + new() { NonNullableScalar = 200, NullableScalar = 201 }, + new() { NonNullableScalar = 300, NullableScalar = null } + ] + }; + + var entity2 = new MyEntity29219 + { + Id = 2, + Reference = new MyJsonEntity29219 { NonNullableScalar = 20, NullableScalar = null }, + Collection = [new() { NonNullableScalar = 1001, NullableScalar = null }] + }; + + ctx.AddRange(entity1, entity2); + await ctx.SaveChangesAsync(); + + await ctx.Database.ExecuteSqlAsync( + $$""" +INSERT INTO [Entities] ([Id], [Reference], [Collection]) +VALUES(3, N'{ "NonNullableScalar" : 30 }', N'[{ "NonNullableScalar" : 10001 }]') +"""); + } + + protected override async Task Seed30028(DbContext ctx) + { + // complete + await ctx.Database.ExecuteSqlAsync( + $$$$""" +INSERT INTO [Entities] ([Id], [Json]) +VALUES( +1, +N'{"RootName":"e1","Collection":[{"BranchName":"e1 c1","Nested":{"LeafName":"e1 c1 l"}},{"BranchName":"e1 c2","Nested":{"LeafName":"e1 c2 l"}}],"OptionalReference":{"BranchName":"e1 or","Nested":{"LeafName":"e1 or l"}},"RequiredReference":{"BranchName":"e1 rr","Nested":{"LeafName":"e1 rr l"}}}') +"""); + + // missing collection + await ctx.Database.ExecuteSqlAsync( + $$$$""" +INSERT INTO [Entities] ([Id], [Json]) +VALUES( +2, +N'{"RootName":"e2","OptionalReference":{"BranchName":"e2 or","Nested":{"LeafName":"e2 or l"}},"RequiredReference":{"BranchName":"e2 rr","Nested":{"LeafName":"e2 rr l"}}}') +"""); + + // missing optional reference + await ctx.Database.ExecuteSqlAsync( + $$$$""" +INSERT INTO [Entities] ([Id], [Json]) +VALUES( +3, +N'{"RootName":"e3","Collection":[{"BranchName":"e3 c1","Nested":{"LeafName":"e3 c1 l"}},{"BranchName":"e3 c2","Nested":{"LeafName":"e3 c2 l"}}],"RequiredReference":{"BranchName":"e3 rr","Nested":{"LeafName":"e3 rr l"}}}') +"""); + + // missing required reference + await ctx.Database.ExecuteSqlAsync( + $$$$""" +INSERT INTO [Entities] ([Id], [Json]) +VALUES( +4, +N'{"RootName":"e4","Collection":[{"BranchName":"e4 c1","Nested":{"LeafName":"e4 c1 l"}},{"BranchName":"e4 c2","Nested":{"LeafName":"e4 c2 l"}}],"OptionalReference":{"BranchName":"e4 or","Nested":{"LeafName":"e4 or l"}}}') +"""); + } + + protected override Task Seed33046(DbContext ctx) + => ctx.Database.ExecuteSqlAsync( + $$""" +INSERT INTO [Reviews] ([Rounds], [Id]) +VALUES(N'[{"RoundNumber":11,"SubRounds":[{"SubRoundNumber":111},{"SubRoundNumber":112}]}]', 1) +"""); + + protected override Task SeedArrayOfPrimitives(DbContext ctx) + { + var entity1 = new MyEntityArrayOfPrimitives + { + Id = 1, + Reference = new MyJsonEntityArrayOfPrimitives + { + IntArray = [1, 2, 3], + ListOfString = + [ + "Foo", + "Bar", + "Baz" + ] + }, + Collection = + [ + new() { IntArray = [111, 112, 113], ListOfString = ["Foo11", "Bar11"] }, + new() { IntArray = [211, 212, 213], ListOfString = ["Foo12", "Bar12"] } + ] + }; + + var entity2 = new MyEntityArrayOfPrimitives + { + Id = 2, + Reference = new MyJsonEntityArrayOfPrimitives + { + IntArray = [10, 20, 30], + ListOfString = + [ + "A", + "B", + "C" + ] + }, + Collection = + [ + new() { IntArray = [110, 120, 130], ListOfString = ["A1", "Z1"] }, + new() { IntArray = [210, 220, 230], ListOfString = ["A2", "Z2"] } + ] + }; + + ctx.AddRange(entity1, entity2); + return ctx.SaveChangesAsync(); + } + + protected override Task SeedJunkInJson(DbContext ctx) + => ctx.Database.ExecuteSqlAsync( + $$$$""" +INSERT INTO [Entities] ([Collection], [CollectionWithCtor], [Reference], [ReferenceWithCtor], [Id]) +VALUES( +N'[{"JunkReference":{"Something":"SomeValue" },"Name":"c11","JunkProperty1":50,"Number":11.5,"JunkCollection1":[],"JunkCollection2":[{"Foo":"junk value"}],"NestedCollection":[{"DoB":"2002-04-01T00:00:00","DummyProp":"Dummy value"},{"DoB":"2002-04-02T00:00:00","DummyReference":{"Foo":5}}],"NestedReference":{"DoB":"2002-03-01T00:00:00"}},{"Name":"c12","Number":12.5,"NestedCollection":[{"DoB":"2002-06-01T00:00:00"},{"DoB":"2002-06-02T00:00:00"}],"NestedDummy":59,"NestedReference":{"DoB":"2002-05-01T00:00:00"}}]', +N'[{"MyBool":true,"Name":"c11 ctor","JunkReference":{"Something":"SomeValue","JunkCollection":[{"Foo":"junk value"}]},"NestedCollection":[{"DoB":"2002-08-01T00:00:00"},{"DoB":"2002-08-02T00:00:00"}],"NestedReference":{"DoB":"2002-07-01T00:00:00"}},{"MyBool":false,"Name":"c12 ctor","NestedCollection":[{"DoB":"2002-10-01T00:00:00"},{"DoB":"2002-10-02T00:00:00"}],"JunkCollection":[{"Foo":"junk value"}],"NestedReference":{"DoB":"2002-09-01T00:00:00"}}]', +N'{"Name":"r1","JunkCollection":[{"Foo":"junk value"}],"JunkReference":{"Something":"SomeValue" },"Number":1.5,"NestedCollection":[{"DoB":"2000-02-01T00:00:00","JunkReference":{"Something":"SomeValue"}},{"DoB":"2000-02-02T00:00:00"}],"NestedReference":{"DoB":"2000-01-01T00:00:00"}}', +N'{"MyBool":true,"JunkCollection":[{"Foo":"junk value"}],"Name":"r1 ctor","JunkReference":{"Something":"SomeValue" },"NestedCollection":[{"DoB":"2001-02-01T00:00:00"},{"DoB":"2001-02-02T00:00:00"}],"NestedReference":{"JunkCollection":[{"Foo":"junk value"}],"DoB":"2001-01-01T00:00:00"}}', +1) +"""); + + protected override Task SeedTrickyBuffering(DbContext ctx) + => ctx.Database.ExecuteSqlAsync( + $$$""" +INSERT INTO [Entities] ([Reference], [Id]) +VALUES( +N'{"Name": "r1", "Number": 7, "JunkReference":{"Something": "SomeValue" }, "JunkCollection": [{"Foo": "junk value"}], "NestedReference": {"DoB": "2000-01-01T00:00:00"}, "NestedCollection": [{"DoB": "2000-02-01T00:00:00", "JunkReference": {"Something": "SomeValue"}}, {"DoB": "2000-02-02T00:00:00"}]}',1) +"""); + + protected override Task SeedShadowProperties(DbContext ctx) + => ctx.Database.ExecuteSqlAsync( + $$""" +INSERT INTO [Entities] ([Collection], [CollectionWithCtor], [Reference], [ReferenceWithCtor], [Id], [Name]) +VALUES( +N'[{"Name":"e1_c1","ShadowDouble":5.5},{"ShadowDouble":20.5,"Name":"e1_c2"}]', +N'[{"Name":"e1_c1 ctor","ShadowNullableByte":6},{"ShadowNullableByte":null,"Name":"e1_c2 ctor"}]', +N'{"Name":"e1_r", "ShadowString":"Foo"}', +N'{"ShadowInt":143,"Name":"e1_r ctor"}', +1, +N'e1') +"""); + + protected override async Task SeedNotICollection(DbContext ctx) + { + await ctx.Database.ExecuteSqlAsync( + $$""" +INSERT INTO [Entities] ([Json], [Id]) +VALUES( +N'{"Collection":[{"Bar":11,"Foo":"c11"},{"Bar":12,"Foo":"c12"},{"Bar":13,"Foo":"c13"}]}', +1) +"""); + + await ctx.Database.ExecuteSqlAsync( + $$$""" +INSERT INTO [Entities] ([Json], [Id]) +VALUES( +N'{"Collection":[{"Bar":21,"Foo":"c21"},{"Bar":22,"Foo":"c22"}]}', +2) +"""); + } + + #region EnumLegacyValues + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual async Task Read_enum_property_with_legacy_values(bool async) + { + var contextFactory = await InitializeAsync( + onModelCreating: BuildModelEnumLegacyValues, + onConfiguring: b => b.ConfigureWarnings(ConfigureWarnings), + seed: SeedEnumLegacyValues); + + using (var context = contextFactory.CreateContext()) + { + var query = context.Set().Select( + x => new + { + x.Reference.IntEnum, + x.Reference.ByteEnum, + x.Reference.LongEnum, + x.Reference.NullableEnum + }); + + var exception = async + ? await (Assert.ThrowsAsync(() => query.ToListAsync())) + : Assert.Throws(() => query.ToList()); + + // Conversion failed when converting the nvarchar value '...' to data type int + Assert.Equal(245, exception.Number); + } + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual async Task Read_json_entity_with_enum_properties_with_legacy_values(bool async) + { + var contextFactory = await InitializeAsync( + onModelCreating: BuildModelEnumLegacyValues, + onConfiguring: b => b.ConfigureWarnings(ConfigureWarnings), + seed: SeedEnumLegacyValues, + shouldLogCategory: c => c == DbLoggerCategory.Query.Name); + + using (var context = contextFactory.CreateContext()) + { + var query = context.Set().Select(x => x.Reference).AsNoTracking(); + + var result = async + ? await query.ToListAsync() + : query.ToList(); + + Assert.Equal(1, result.Count); + Assert.Equal(ByteEnumLegacyValues.Redmond, result[0].ByteEnum); + Assert.Equal(IntEnumLegacyValues.Foo, result[0].IntEnum); + Assert.Equal(LongEnumLegacyValues.Three, result[0].LongEnum); + Assert.Equal(ULongEnumLegacyValues.Three, result[0].ULongEnum); + Assert.Equal(IntEnumLegacyValues.Bar, result[0].NullableEnum); + } + + var testLogger = new TestLogger(); + Assert.Single( + ListLoggerFactory.Log.Where( + l => l.Message == CoreResources.LogStringEnumValueInJson(testLogger).GenerateMessage(nameof(ByteEnumLegacyValues)))); + Assert.Single( + ListLoggerFactory.Log.Where( + l => l.Message == CoreResources.LogStringEnumValueInJson(testLogger).GenerateMessage(nameof(IntEnumLegacyValues)))); + Assert.Single( + ListLoggerFactory.Log.Where( + l => l.Message == CoreResources.LogStringEnumValueInJson(testLogger).GenerateMessage(nameof(LongEnumLegacyValues)))); + Assert.Single( + ListLoggerFactory.Log.Where( + l => l.Message == CoreResources.LogStringEnumValueInJson(testLogger).GenerateMessage(nameof(ULongEnumLegacyValues)))); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual async Task Read_json_entity_collection_with_enum_properties_with_legacy_values(bool async) + { + var contextFactory = await InitializeAsync( + onModelCreating: BuildModelEnumLegacyValues, + onConfiguring: b => b.ConfigureWarnings(ConfigureWarnings), + seed: SeedEnumLegacyValues, + shouldLogCategory: c => c == DbLoggerCategory.Query.Name); + + using (var context = contextFactory.CreateContext()) + { + var query = context.Set().Select(x => x.Collection).AsNoTracking(); + + var result = async + ? await query.ToListAsync() + : query.ToList(); + + Assert.Equal(1, result.Count); + Assert.Equal(2, result[0].Count); + Assert.Equal(ByteEnumLegacyValues.Bellevue, result[0][0].ByteEnum); + Assert.Equal(IntEnumLegacyValues.Foo, result[0][0].IntEnum); + Assert.Equal(LongEnumLegacyValues.One, result[0][0].LongEnum); + Assert.Equal(ULongEnumLegacyValues.One, result[0][0].ULongEnum); + Assert.Equal(IntEnumLegacyValues.Bar, result[0][0].NullableEnum); + Assert.Equal(ByteEnumLegacyValues.Seattle, result[0][1].ByteEnum); + Assert.Equal(IntEnumLegacyValues.Baz, result[0][1].IntEnum); + Assert.Equal(LongEnumLegacyValues.Two, result[0][1].LongEnum); + Assert.Equal(ULongEnumLegacyValues.Two, result[0][1].ULongEnum); + Assert.Null(result[0][1].NullableEnum); + } + + var testLogger = new TestLogger(); + Assert.Single( + ListLoggerFactory.Log.Where( + l => l.Message == CoreResources.LogStringEnumValueInJson(testLogger).GenerateMessage(nameof(ByteEnumLegacyValues)))); + Assert.Single( + ListLoggerFactory.Log.Where( + l => l.Message == CoreResources.LogStringEnumValueInJson(testLogger).GenerateMessage(nameof(IntEnumLegacyValues)))); + Assert.Single( + ListLoggerFactory.Log.Where( + l => l.Message == CoreResources.LogStringEnumValueInJson(testLogger).GenerateMessage(nameof(LongEnumLegacyValues)))); + Assert.Single( + ListLoggerFactory.Log.Where( + l => l.Message == CoreResources.LogStringEnumValueInJson(testLogger).GenerateMessage(nameof(ULongEnumLegacyValues)))); + } + + private Task SeedEnumLegacyValues(DbContext ctx) + => ctx.Database.ExecuteSqlAsync( + $$""" +INSERT INTO [Entities] ([Collection], [Reference], [Id], [Name]) +VALUES( +N'[{"ByteEnum":"Bellevue","IntEnum":"Foo","LongEnum":"One","ULongEnum":"One","Name":"e1_c1","NullableEnum":"Bar"},{"ByteEnum":"Seattle","IntEnum":"Baz","LongEnum":"Two","ULongEnum":"Two","Name":"e1_c2","NullableEnum":null}]', +N'{"ByteEnum":"Redmond","IntEnum":"Foo","LongEnum":"Three","ULongEnum":"Three","Name":"e1_r","NullableEnum":"Bar"}', +1, +N'e1') +"""); + + protected virtual void BuildModelEnumLegacyValues(ModelBuilder modelBuilder) + => modelBuilder.Entity( + b => + { + b.ToTable("Entities"); + b.Property(x => x.Id).ValueGeneratedNever(); + b.OwnsOne(x => x.Reference, b => b.ToJson().HasColumnType(JsonColumnType)); + b.OwnsMany(x => x.Collection, b => b.ToJson().HasColumnType(JsonColumnType)); + }); + + private class MyEntityEnumLegacyValues + { + public int Id { get; set; } + public string Name { get; set; } + + public MyJsonEntityEnumLegacyValues Reference { get; set; } + public List Collection { get; set; } + } + + private class MyJsonEntityEnumLegacyValues + { + public string Name { get; set; } + + // ReSharper disable once UnusedAutoPropertyAccessor.Local + public IntEnumLegacyValues IntEnum { get; set; } + // ReSharper disable once UnusedAutoPropertyAccessor.Local + public ByteEnumLegacyValues ByteEnum { get; set; } + // ReSharper disable once UnusedAutoPropertyAccessor.Local + public LongEnumLegacyValues LongEnum { get; set; } + // ReSharper disable once UnusedAutoPropertyAccessor.Local + public ULongEnumLegacyValues ULongEnum { get; set; } + // ReSharper disable once UnusedAutoPropertyAccessor.Local + public IntEnumLegacyValues? NullableEnum { get; set; } + } + + private enum IntEnumLegacyValues + { + Foo = int.MinValue, + Bar, + Baz = int.MaxValue, + } + + private enum ByteEnumLegacyValues : byte + { + Seattle, + Redmond, + Bellevue = 255, + } + + private enum LongEnumLegacyValues : long + { + One = long.MinValue, + Two = 1, + Three = long.MaxValue, + } + + private enum ULongEnumLegacyValues : ulong + { + One = ulong.MinValue, + Two = 1, + Three = ulong.MaxValue, + } + + #endregion +} diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/JsonQueryJsonTypeSqlServerFixture.cs b/test/EFCore.SqlServer.FunctionalTests/Query/JsonQueryJsonTypeSqlServerFixture.cs new file mode 100644 index 00000000000..e59e6565b88 --- /dev/null +++ b/test/EFCore.SqlServer.FunctionalTests/Query/JsonQueryJsonTypeSqlServerFixture.cs @@ -0,0 +1,83 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable disable +using Microsoft.EntityFrameworkCore.TestModels.JsonQuery; + +namespace Microsoft.EntityFrameworkCore.Query; + +public class JsonQueryJsonTypeSqlServerFixture : JsonQuerySqlServerFixture +{ + protected override string StoreName + => "JsonQueryJsonTypeTest"; + + protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context) + { + base.OnModelCreating(modelBuilder, context); + + modelBuilder.Entity( + b => + { + b.OwnsOne(x => x.OwnedReferenceRoot).ToJson().HasColumnType("json"); + b.OwnsMany(x => x.OwnedCollectionRoot).ToJson().HasColumnType("json"); + }); + + modelBuilder.Entity( + b => + { + b.OwnsOne(x => x.OwnedReferenceRoot).ToJson("json_reference_custom_naming").HasColumnType("json");; + b.OwnsMany(x => x.OwnedCollectionRoot).HasColumnType("json").ToJson("json_collection_custom_naming"); + }); + + modelBuilder.Entity().OwnsMany(x => x.OwnedCollection).ToJson().HasColumnType("json"); + + modelBuilder.Entity( + b => + { + b.OwnsOne(x => x.ReferenceOnBase).ToJson().HasColumnType("json"); + b.OwnsMany(x => x.CollectionOnBase).ToJson().HasColumnType("json"); + }); + + modelBuilder.Entity( + b => + { + b.HasBaseType(); + b.OwnsOne(x => x.ReferenceOnDerived).ToJson().HasColumnType("json"); + b.OwnsMany(x => x.CollectionOnDerived).ToJson().HasColumnType("json"); + }); + + modelBuilder.Entity( + b => + { + b.OwnsOne(x => x.Reference).ToJson().HasColumnType("json"); + b.OwnsMany(x => x.Collection).ToJson().HasColumnType("json"); + b.PrimitiveCollection(e => e.TestDefaultStringCollection).HasColumnType("json").IsRequired(); + b.PrimitiveCollection(e => e.TestMaxLengthStringCollection).HasColumnType("json").IsRequired(); + b.PrimitiveCollection(e => e.TestInt16Collection).HasColumnType("json").IsRequired(); + b.PrimitiveCollection(e => e.TestInt32Collection).HasColumnType("json").IsRequired(); + b.PrimitiveCollection(e => e.TestDecimalCollection).HasColumnType("json").IsRequired(); + b.PrimitiveCollection(e => e.TestDateTimeCollection).HasColumnType("json").IsRequired(); + b.PrimitiveCollection(e => e.TestDateTimeOffsetCollection).HasColumnType("json").IsRequired(); + b.PrimitiveCollection(e => e.TestTimeSpanCollection).HasColumnType("json").IsRequired(); + b.PrimitiveCollection(e => e.TestInt64Collection).HasColumnType("json").IsRequired(); + b.PrimitiveCollection(e => e.TestDoubleCollection).HasColumnType("json").IsRequired(); + b.PrimitiveCollection(e => e.TestSingleCollection).HasColumnType("json").IsRequired(); + b.PrimitiveCollection(e => e.TestBooleanCollection).HasColumnType("json"); + b.PrimitiveCollection(e => e.TestCharacterCollection).HasColumnType("json"); + b.PrimitiveCollection(e => e.TestByteCollection).HasColumnType("json"); + b.PrimitiveCollection(e => e.TestGuidCollection).HasColumnType("json"); + b.PrimitiveCollection(e => e.TestUnsignedInt16Collection).HasColumnType("json"); + b.PrimitiveCollection(e => e.TestUnsignedInt32Collection).HasColumnType("json"); + b.PrimitiveCollection(e => e.TestUnsignedInt64Collection).HasColumnType("json"); + b.PrimitiveCollection(e => e.TestSignedByteCollection).HasColumnType("json"); + b.PrimitiveCollection(e => e.TestNullableInt32Collection).HasColumnType("json"); + b.PrimitiveCollection(e => e.TestEnumCollection).HasColumnType("json"); + b.PrimitiveCollection(e => e.TestEnumWithIntConverterCollection).HasColumnType("json"); + b.PrimitiveCollection(e => e.TestNullableEnumCollection).HasColumnType("json"); + b.PrimitiveCollection(e => e.TestNullableEnumWithIntConverterCollection).HasColumnType("json"); + b.PrimitiveCollection(e => e.TestNullableEnumWithConverterThatHandlesNullsCollection).HasColumnType("json"); + }); + + modelBuilder.Entity().OwnsOne(x => x.Reference).ToJson().HasColumnType("json"); + } +} diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/JsonQueryJsonTypeSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/JsonQueryJsonTypeSqlServerTest.cs new file mode 100644 index 00000000000..95f302374ec --- /dev/null +++ b/test/EFCore.SqlServer.FunctionalTests/Query/JsonQueryJsonTypeSqlServerTest.cs @@ -0,0 +1,3236 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable disable +using Microsoft.Data.SqlClient; +using Microsoft.EntityFrameworkCore.TestModels.JsonQuery; + +namespace Microsoft.EntityFrameworkCore.Query; + +[SqlServerCondition(SqlServerCondition.SupportsJsonType)] +public class JsonQueryJsonTypeSqlServerTest : JsonQueryRelationalTestBase +{ + public JsonQueryJsonTypeSqlServerTest(JsonQueryJsonTypeSqlServerFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + public override async Task Basic_json_projection_owner_entity(bool async) + { + await base.Basic_json_projection_owner_entity(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Basic_json_projection_owner_entity_NoTracking(bool async) + { + await base.Basic_json_projection_owner_entity_NoTracking(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Basic_json_projection_owner_entity_NoTrackingWithIdentityResolution(bool async) + { + await base.Basic_json_projection_owner_entity_NoTrackingWithIdentityResolution(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Basic_json_projection_owner_entity_duplicated(bool async) + { + await base.Basic_json_projection_owner_entity_duplicated(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Basic_json_projection_owner_entity_duplicated_NoTracking(bool async) + { + await base.Basic_json_projection_owner_entity_duplicated_NoTracking(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[Name], [j].[OwnedCollection], [j].[OwnedCollection] +FROM [JsonEntitiesSingleOwned] AS [j] +"""); + } + + public override async Task Basic_json_projection_owner_entity_duplicated_NoTrackingWithIdentityResolution(bool async) + { + await base.Basic_json_projection_owner_entity_duplicated_NoTrackingWithIdentityResolution(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[Name], [j].[OwnedCollection], [j].[OwnedCollection] +FROM [JsonEntitiesSingleOwned] AS [j] +"""); + } + + public override async Task Basic_json_projection_owner_entity_twice(bool async) + { + await base.Basic_json_projection_owner_entity_twice(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Basic_json_projection_owner_entity_twice_NoTracking(bool async) + { + await base.Basic_json_projection_owner_entity_twice_NoTracking(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Basic_json_projection_owner_entity_twice_NoTrackingWithIdentityResolution(bool async) + { + await base.Basic_json_projection_owner_entity_twice_NoTrackingWithIdentityResolution(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Basic_json_projection_owned_reference_root(bool async) + { + await base.Basic_json_projection_owned_reference_root(async); + + AssertSql( + """ +SELECT [j].[OwnedReferenceRoot], [j].[Id] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Basic_json_projection_owned_reference_root_NoTrackingWithIdentityResolution(bool async) + { + await base.Basic_json_projection_owned_reference_root_NoTrackingWithIdentityResolution(async); + + AssertSql( + """ +SELECT [j].[OwnedReferenceRoot], [j].[Id] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Basic_json_projection_owned_reference_duplicated(bool async) + { + await base.Basic_json_projection_owned_reference_duplicated(async); + + AssertSql( + """ +SELECT [j].[OwnedReferenceRoot], [j].[Id], JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch'), [j].[OwnedReferenceRoot], JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch') +FROM [JsonEntitiesBasic] AS [j] +ORDER BY [j].[Id] +"""); + } + + public override async Task Basic_json_projection_owned_reference_duplicated_NoTrackingWithIdentityResolution(bool async) + { + await base.Basic_json_projection_owned_reference_duplicated_NoTrackingWithIdentityResolution(async); + + AssertSql( + """ +SELECT [j].[OwnedReferenceRoot], [j].[Id], JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch'), [j].[OwnedReferenceRoot], JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch') +FROM [JsonEntitiesBasic] AS [j] +ORDER BY [j].[Id] +"""); + } + + public override async Task Basic_json_projection_owned_reference_duplicated2(bool async) + { + await base.Basic_json_projection_owned_reference_duplicated2(async); + + AssertSql( + """ +SELECT [j].[OwnedReferenceRoot], [j].[Id], JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch.OwnedReferenceLeaf'), [j].[OwnedReferenceRoot], JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch.OwnedReferenceLeaf') +FROM [JsonEntitiesBasic] AS [j] +ORDER BY [j].[Id] +"""); + } + + public override async Task Basic_json_projection_owned_reference_duplicated2_NoTrackingWithIdentityResolution(bool async) + { + await base.Basic_json_projection_owned_reference_duplicated2_NoTrackingWithIdentityResolution(async); + + AssertSql( + """ +SELECT [j].[OwnedReferenceRoot], [j].[Id], JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch.OwnedReferenceLeaf'), [j].[OwnedReferenceRoot], JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch.OwnedReferenceLeaf') +FROM [JsonEntitiesBasic] AS [j] +ORDER BY [j].[Id] +"""); + } + + public override async Task Basic_json_projection_owned_collection_root(bool async) + { + await base.Basic_json_projection_owned_collection_root(async); + + AssertSql( + """ +SELECT [j].[OwnedCollectionRoot], [j].[Id] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Basic_json_projection_owned_collection_root_NoTrackingWithIdentityResolution(bool async) + { + await base.Basic_json_projection_owned_collection_root_NoTrackingWithIdentityResolution(async); + + AssertSql( + """ +SELECT [j].[OwnedCollectionRoot], [j].[Id] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Basic_json_projection_owned_reference_branch(bool async) + { + await base.Basic_json_projection_owned_reference_branch(async); + + AssertSql( + """ +SELECT JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch'), [j].[Id] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Basic_json_projection_owned_reference_branch_NoTrackingWithIdentityResolution(bool async) + { + await base.Basic_json_projection_owned_reference_branch_NoTrackingWithIdentityResolution(async); + + AssertSql( + """ +SELECT JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch'), [j].[Id] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Basic_json_projection_owned_collection_branch(bool async) + { + await base.Basic_json_projection_owned_collection_branch(async); + + AssertSql( + """ +SELECT JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedCollectionBranch'), [j].[Id] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Basic_json_projection_owned_collection_branch_NoTrackingWithIdentityResolution(bool async) + { + await base.Basic_json_projection_owned_collection_branch_NoTrackingWithIdentityResolution(async); + + AssertSql( + """ +SELECT JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedCollectionBranch'), [j].[Id] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Basic_json_projection_owned_reference_leaf(bool async) + { + await base.Basic_json_projection_owned_reference_leaf(async); + + AssertSql( + """ +SELECT JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch.OwnedReferenceLeaf'), [j].[Id] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Basic_json_projection_owned_collection_leaf(bool async) + { + await base.Basic_json_projection_owned_collection_leaf(async); + + AssertSql( + """ +SELECT JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch.OwnedCollectionLeaf'), [j].[Id] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Basic_json_projection_scalar(bool async) + { + await base.Basic_json_projection_scalar(async); + + AssertSql( + """ +SELECT JSON_VALUE([j].[OwnedReferenceRoot], '$.Name') +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Json_scalar_length(bool async) + { + await base.Json_scalar_length(async); + + AssertSql( + """ +SELECT [j].[Name] +FROM [JsonEntitiesBasic] AS [j] +WHERE CAST(LEN(JSON_VALUE([j].[OwnedReferenceRoot], '$.Name')) AS int) > 2 +"""); + } + + public override async Task Basic_json_projection_enum_inside_json_entity(bool async) + { + await base.Basic_json_projection_enum_inside_json_entity(async); + + AssertSql( + """ +SELECT [j].[Id], CAST(JSON_VALUE([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch.Enum') AS int) AS [Enum] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Json_projection_enum_with_custom_conversion(bool async) + { + await base.Json_projection_enum_with_custom_conversion(async); + + AssertSql( + """ +SELECT [j].[Id], CAST(JSON_VALUE([j].[json_reference_custom_naming], '$."1CustomEnum"') AS int) AS [Enum] +FROM [JsonEntitiesCustomNaming] AS [j] +"""); + } + + public override async Task Json_projection_with_deduplication(bool async) + { + await base.Json_projection_with_deduplication(async); + + AssertSql( + """ +SELECT [j].[Id], JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch'), JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch.OwnedReferenceLeaf'), JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch.OwnedCollectionLeaf'), JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedCollectionBranch'), JSON_VALUE([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch.OwnedReferenceLeaf.SomethingSomething') +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Json_projection_with_deduplication_reverse_order(bool async) + { + await base.Json_projection_with_deduplication_reverse_order(async); + + AssertSql( + """ +SELECT JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch.OwnedReferenceLeaf'), [j].[Id], [j].[OwnedReferenceRoot], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Json_property_in_predicate(bool async) + { + await base.Json_property_in_predicate(async); + + AssertSql( + """ +SELECT [j].[Id] +FROM [JsonEntitiesBasic] AS [j] +WHERE CAST(JSON_VALUE([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch.Fraction') AS decimal(18,2)) < 20.5 +"""); + } + + public override async Task Json_subquery_property_pushdown_length(bool async) + { + await base.Json_subquery_property_pushdown_length(async); + + AssertSql( + """ +@__p_0='3' + +SELECT CAST(LEN([j1].[c]) AS int) +FROM ( + SELECT DISTINCT [j0].[c] + FROM ( + SELECT TOP(@__p_0) JSON_VALUE([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch.OwnedReferenceLeaf.SomethingSomething') AS [c] + FROM [JsonEntitiesBasic] AS [j] + ORDER BY [j].[Id] + ) AS [j0] +) AS [j1] +"""); + } + + public override async Task Json_subquery_reference_pushdown_reference(bool async) + { + // TODO:SQLJSON Json type is not comparable + Assert.Equal( + "The json data type cannot be selected as DISTINCT because it is not comparable.", + (await Assert.ThrowsAsync(() => base.Json_subquery_reference_pushdown_reference(async))).Message); + + AssertSql( + """ +@__p_0='10' + +SELECT JSON_QUERY([j1].[c], '$.OwnedReferenceBranch'), [j1].[Id] +FROM ( + SELECT DISTINCT [j0].[c] AS [c], [j0].[Id] + FROM ( + SELECT TOP(@__p_0) [j].[OwnedReferenceRoot] AS [c], [j].[Id] + FROM [JsonEntitiesBasic] AS [j] + ORDER BY [j].[Id] + ) AS [j0] +) AS [j1] +"""); + } + + public override async Task Json_subquery_reference_pushdown_reference_anonymous_projection(bool async) + { + await base.Json_subquery_reference_pushdown_reference_anonymous_projection(async); + + AssertSql( + """ +@__p_0='10' + +SELECT JSON_QUERY([t0].[c], '$.OwnedReferenceSharedBranch'), [t0].[Id], CAST(LEN([t0].[c0]) AS int) +FROM ( + SELECT DISTINCT JSON_QUERY([t].[c],'$') AS [c], [t].[Id], [t].[c0] + FROM ( + SELECT TOP(@__p_0) JSON_QUERY([j].[json_reference_shared], '$') AS [c], [j].[Id], CAST(JSON_VALUE([j].[json_reference_shared], '$.OwnedReferenceSharedBranch.OwnedReferenceSharedLeaf.SomethingSomething') AS nvarchar(max)) AS [c0] + FROM [JsonEntitiesBasic] AS [j] + ORDER BY [j].[Id] + ) AS [t] +) AS [t0] +"""); + } + + public override async Task Json_subquery_reference_pushdown_reference_pushdown_anonymous_projection(bool async) + { + await base.Json_subquery_reference_pushdown_reference_pushdown_anonymous_projection(async); + + AssertSql( + """ +@__p_0='10' + +SELECT JSON_QUERY([t2].[c],'$.OwnedReferenceSharedLeaf'), [t2].[Id], JSON_QUERY([t2].[c], '$.OwnedCollectionSharedLeaf'), [t2].[Length] +FROM ( + SELECT DISTINCT JSON_QUERY([t1].[c],'$') AS [c], [t1].[Id], [t1].[Length] + FROM ( + SELECT TOP(@__p_0) JSON_QUERY([t0].[c], '$.OwnedReferenceSharedBranch') AS [c], [t0].[Id], CAST(LEN([t0].[Scalar]) AS int) AS [Length] + FROM ( + SELECT DISTINCT JSON_QUERY([t].[c],'$') AS [c], [t].[Id], [t].[Scalar] + FROM ( + SELECT TOP(@__p_0) JSON_QUERY([j].[json_reference_shared], '$') AS [c], [j].[Id], CAST(JSON_VALUE([j].[json_reference_shared], '$.OwnedReferenceSharedBranch.OwnedReferenceSharedLeaf.SomethingSomething') AS nvarchar(max)) AS [Scalar] + FROM [JsonEntitiesBasic] AS [j] + ORDER BY [j].[Id] + ) AS [t] + ) AS [t0] + ORDER BY CAST(LEN([t0].[Scalar]) AS int) + ) AS [t1] +) AS [t2] +"""); + } + + public override async Task Json_subquery_reference_pushdown_reference_pushdown_reference(bool async) + { + // TODO:SQLJSON Json type is not comparable + Assert.StartsWith( + "The json data type cannot be selected as DISTINCT because it is not comparable.", + (await Assert.ThrowsAsync(() => base.Json_subquery_reference_pushdown_reference_pushdown_reference(async))).Message); + + AssertSql( + """ +@__p_0='10' + +SELECT JSON_QUERY([j3].[c], '$.OwnedReferenceLeaf'), [j3].[Id] +FROM ( + SELECT DISTINCT [j2].[c] AS [c], [j2].[Id] + FROM ( + SELECT TOP(@__p_0) JSON_QUERY([j1].[c], '$.OwnedReferenceBranch') AS [c], [j1].[Id] + FROM ( + SELECT DISTINCT [j0].[c] AS [c], [j0].[Id], [j0].[c] AS [c0] + FROM ( + SELECT TOP(@__p_0) [j].[OwnedReferenceRoot] AS [c], [j].[Id] + FROM [JsonEntitiesBasic] AS [j] + ORDER BY [j].[Id] + ) AS [j0] + ) AS [j1] + ORDER BY JSON_VALUE([j1].[c0], '$.Name') + ) AS [j2] +) AS [j3] +"""); + } + + public override async Task Json_subquery_reference_pushdown_reference_pushdown_collection(bool async) + { + // TODO:SQLJSON Json type is not comparable + Assert.StartsWith( + "The json data type cannot be selected as DISTINCT because it is not comparable.", + (await Assert.ThrowsAsync(() => base.Json_subquery_reference_pushdown_reference_pushdown_collection(async))).Message); + + AssertSql( + """ +@__p_0='10' + +SELECT JSON_QUERY([j3].[c], '$.OwnedCollectionLeaf'), [j3].[Id] +FROM ( + SELECT DISTINCT [j2].[c] AS [c], [j2].[Id] + FROM ( + SELECT TOP(@__p_0) JSON_QUERY([j1].[c], '$.OwnedReferenceBranch') AS [c], [j1].[Id] + FROM ( + SELECT DISTINCT [j0].[c] AS [c], [j0].[Id], [j0].[c] AS [c0] + FROM ( + SELECT TOP(@__p_0) [j].[OwnedReferenceRoot] AS [c], [j].[Id] + FROM [JsonEntitiesBasic] AS [j] + ORDER BY [j].[Id] + ) AS [j0] + ) AS [j1] + ORDER BY JSON_VALUE([j1].[c0], '$.Name') + ) AS [j2] +) AS [j3] +"""); + } + + public override async Task Json_subquery_reference_pushdown_property(bool async) + { + // TODO:SQLJSON Json type is not comparable + Assert.Equal( + "The json data type cannot be selected as DISTINCT because it is not comparable.", + (await Assert.ThrowsAsync(() => base.Json_subquery_reference_pushdown_property(async))).Message); + + AssertSql( + """ +@__p_0='10' + +SELECT JSON_VALUE([j1].[c], '$.SomethingSomething') +FROM ( + SELECT DISTINCT [j0].[c] AS [c], [j0].[Id] + FROM ( + SELECT TOP(@__p_0) JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch.OwnedReferenceLeaf') AS [c], [j].[Id] + FROM [JsonEntitiesBasic] AS [j] + ORDER BY [j].[Id] + ) AS [j0] +) AS [j1] +"""); + } + + public override async Task Custom_naming_projection_owner_entity(bool async) + { + await base.Custom_naming_projection_owner_entity(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[Title], [j].[json_collection_custom_naming], [j].[json_reference_custom_naming] +FROM [JsonEntitiesCustomNaming] AS [j] +"""); + } + + public override async Task Custom_naming_projection_owned_reference(bool async) + { + await base.Custom_naming_projection_owned_reference(async); + + AssertSql( + """ +SELECT JSON_QUERY([j].[json_reference_custom_naming], '$."Custom#OwnedReferenceBranch\u0060-=[]\\;\u0027,./~!@#$%^\u0026*()_\u002B{}|:\u0022\u003C\u003E?\u72EC\u89D2\u517D\u03C0\u7368\u89D2\u7378"'), [j].[Id] +FROM [JsonEntitiesCustomNaming] AS [j] +"""); + } + + public override async Task Custom_naming_projection_owned_collection(bool async) + { + await base.Custom_naming_projection_owned_collection(async); + + AssertSql( + """ +SELECT [j].[json_collection_custom_naming], [j].[Id] +FROM [JsonEntitiesCustomNaming] AS [j] +ORDER BY [j].[Id] +"""); + } + + public override async Task Custom_naming_projection_owned_scalar(bool async) + { + await base.Custom_naming_projection_owned_scalar(async); + + AssertSql( + """ +SELECT CAST(JSON_VALUE([j].[json_reference_custom_naming], '$."Custom#OwnedReferenceBranch\u0060-=[]\\;\u0027,./~!@#$%^\u0026*()_\u002B{}|:\u0022\u003C\u003E?\u72EC\u89D2\u517D\u03C0\u7368\u89D2\u7378"."\u30E6\u30CB\u30B3\u30FC\u30F3Fraction\u4E00\u89D2\u7363"') AS float) +FROM [JsonEntitiesCustomNaming] AS [j] +"""); + } + + public override async Task Custom_naming_projection_everything(bool async) + { + await base.Custom_naming_projection_everything(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[Title], [j].[json_collection_custom_naming], [j].[json_reference_custom_naming], [j].[json_reference_custom_naming], JSON_QUERY([j].[json_reference_custom_naming], '$."Custom#OwnedReferenceBranch\u0060-=[]\\;\u0027,./~!@#$%^\u0026*()_\u002B{}|:\u0022\u003C\u003E?\u72EC\u89D2\u517D\u03C0\u7368\u89D2\u7378"'), [j].[json_collection_custom_naming], JSON_QUERY([j].[json_reference_custom_naming], '$.CustomOwnedCollectionBranch'), JSON_VALUE([j].[json_reference_custom_naming], '$.CustomName'), CAST(JSON_VALUE([j].[json_reference_custom_naming], '$."Custom#OwnedReferenceBranch\u0060-=[]\\;\u0027,./~!@#$%^\u0026*()_\u002B{}|:\u0022\u003C\u003E?\u72EC\u89D2\u517D\u03C0\u7368\u89D2\u7378"."\u30E6\u30CB\u30B3\u30FC\u30F3Fraction\u4E00\u89D2\u7363"') AS float) +FROM [JsonEntitiesCustomNaming] AS [j] +"""); + } + + public override async Task Project_entity_with_single_owned(bool async) + { + await base.Project_entity_with_single_owned(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[Name], [j].[OwnedCollection] +FROM [JsonEntitiesSingleOwned] AS [j] +"""); + } + + public override async Task Left_join_json_entities(bool async) + { + // TODO:SQLJSON Returns empty (invalid) JSON (See BadJson.cs) + if (async) + { + Assert.Equal( + "Unable to cast object of type 'System.DBNull' to type 'System.String'.", + (await Assert.ThrowsAsync(() => base.Left_join_json_entities(true))).Message); + } + else + { + Assert.Equal( + RelationalStrings.JsonEmptyString, + (await Assert.ThrowsAsync(() => base.Left_join_json_entities(false))) + .Message); + } + + AssertSql( + """ +SELECT [j].[Id], [j].[Name], [j].[OwnedCollection], [j0].[Id], [j0].[EntityBasicId], [j0].[Name], [j0].[OwnedCollectionRoot], [j0].[OwnedReferenceRoot] +FROM [JsonEntitiesSingleOwned] AS [j] +LEFT JOIN [JsonEntitiesBasic] AS [j0] ON [j].[Id] = [j0].[Id] +"""); + } + + public override async Task Left_join_json_entities_complex_projection(bool async) + { + // TODO:SQLJSON Returns empty (invalid) JSON (See BadJson.cs) + if (async) + { + Assert.Equal( + "Unable to cast object of type 'System.DBNull' to type 'System.String'.", + (await Assert.ThrowsAsync(() => base.Left_join_json_entities_complex_projection(true))).Message); + } + else + { + Assert.Equal( + RelationalStrings.JsonEmptyString, + (await Assert.ThrowsAsync(() => base.Left_join_json_entities_complex_projection(false))) + .Message); + } + + AssertSql( + """ +SELECT [j].[Id], [j0].[Id], [j0].[OwnedReferenceRoot], JSON_QUERY([j0].[OwnedReferenceRoot], '$.OwnedReferenceBranch'), JSON_QUERY([j0].[OwnedReferenceRoot], '$.OwnedReferenceBranch.OwnedReferenceLeaf'), JSON_QUERY([j0].[OwnedReferenceRoot], '$.OwnedReferenceBranch.OwnedCollectionLeaf') +FROM [JsonEntitiesSingleOwned] AS [j] +LEFT JOIN [JsonEntitiesBasic] AS [j0] ON [j].[Id] = [j0].[Id] +"""); + } + + public override async Task Left_join_json_entities_json_being_inner(bool async) + { + await base.Left_join_json_entities_json_being_inner(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot], [j0].[Id], [j0].[Name], [j0].[OwnedCollection] +FROM [JsonEntitiesBasic] AS [j] +LEFT JOIN [JsonEntitiesSingleOwned] AS [j0] ON [j].[Id] = [j0].[Id] +"""); + } + + public override async Task Left_join_json_entities_complex_projection_json_being_inner(bool async) + { + await base.Left_join_json_entities_complex_projection_json_being_inner(async); + + AssertSql( + """ +SELECT [j].[Id], [j0].[Id], [j].[OwnedReferenceRoot], JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch'), JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch.OwnedReferenceLeaf'), JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch.OwnedCollectionLeaf'), [j0].[Name] +FROM [JsonEntitiesBasic] AS [j] +LEFT JOIN [JsonEntitiesSingleOwned] AS [j0] ON [j].[Id] = [j0].[Id] +"""); + } + + public override async Task Project_json_entity_FirstOrDefault_subquery(bool async) + { + await base.Project_json_entity_FirstOrDefault_subquery(async); + + AssertSql( + """ +SELECT [j1].[c], [j1].[Id] +FROM [JsonEntitiesBasic] AS [j] +OUTER APPLY ( + SELECT TOP(1) JSON_QUERY([j0].[OwnedReferenceRoot], '$.OwnedReferenceBranch') AS [c], [j0].[Id] + FROM [JsonEntitiesBasic] AS [j0] + ORDER BY [j0].[Id] +) AS [j1] +ORDER BY [j].[Id] +"""); + } + + public override async Task Project_json_entity_FirstOrDefault_subquery_with_binding_on_top(bool async) + { + await base.Project_json_entity_FirstOrDefault_subquery_with_binding_on_top(async); + + AssertSql( + """ +SELECT ( + SELECT TOP(1) CAST(JSON_VALUE([j0].[OwnedReferenceRoot], '$.OwnedReferenceBranch.Date') AS datetime2) + FROM [JsonEntitiesBasic] AS [j0] + ORDER BY [j0].[Id]) +FROM [JsonEntitiesBasic] AS [j] +ORDER BY [j].[Id] +"""); + } + + public override async Task Project_json_entity_FirstOrDefault_subquery_with_entity_comparison_on_top(bool async) + { + await base.Project_json_entity_FirstOrDefault_subquery_with_entity_comparison_on_top(async); + + AssertSql( + @""); + } + + public override async Task Project_json_entity_FirstOrDefault_subquery_deduplication(bool async) + { + await base.Project_json_entity_FirstOrDefault_subquery_deduplication(async); + + AssertSql( + """ +SELECT [j1].[c], [j1].[Id], [j1].[c0], [j1].[Id0], [j1].[c1], [j1].[c2], [j1].[c3], [j1].[c4] +FROM [JsonEntitiesBasic] AS [j] +OUTER APPLY ( + SELECT TOP(1) JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedCollectionBranch') AS [c], [j].[Id], [j0].[OwnedReferenceRoot] AS [c0], [j0].[Id] AS [Id0], JSON_QUERY([j0].[OwnedReferenceRoot], '$.OwnedReferenceBranch') AS [c1], JSON_VALUE([j0].[OwnedReferenceRoot], '$.Name') AS [c2], CAST(JSON_VALUE([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch.Enum') AS int) AS [c3], 1 AS [c4] + FROM [JsonEntitiesBasic] AS [j0] + ORDER BY [j0].[Id] +) AS [j1] +ORDER BY [j].[Id] +"""); + } + + public override async Task Project_json_entity_FirstOrDefault_subquery_deduplication_and_outer_reference(bool async) + { + await base.Project_json_entity_FirstOrDefault_subquery_deduplication_and_outer_reference(async); + + AssertSql( + """ +SELECT [j1].[c], [j1].[Id], [j1].[c0], [j1].[Id0], [j1].[c1], [j1].[c2], [j1].[c3], [j1].[c4] +FROM [JsonEntitiesBasic] AS [j] +OUTER APPLY ( + SELECT TOP(1) JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedCollectionBranch') AS [c], [j].[Id], [j0].[OwnedReferenceRoot] AS [c0], [j0].[Id] AS [Id0], JSON_QUERY([j0].[OwnedReferenceRoot], '$.OwnedReferenceBranch') AS [c1], JSON_VALUE([j0].[OwnedReferenceRoot], '$.Name') AS [c2], CAST(JSON_VALUE([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch.Enum') AS int) AS [c3], 1 AS [c4] + FROM [JsonEntitiesBasic] AS [j0] + ORDER BY [j0].[Id] +) AS [j1] +ORDER BY [j].[Id] +"""); + } + + public override async Task Project_json_entity_FirstOrDefault_subquery_deduplication_outer_reference_and_pruning(bool async) + { + await base.Project_json_entity_FirstOrDefault_subquery_deduplication_outer_reference_and_pruning(async); + + AssertSql( + """ +SELECT [j1].[c], [j1].[Id], [j1].[c0] +FROM [JsonEntitiesBasic] AS [j] +OUTER APPLY ( + SELECT TOP(1) JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedCollectionBranch') AS [c], [j].[Id], 1 AS [c0] + FROM [JsonEntitiesBasic] AS [j0] + ORDER BY [j0].[Id] +) AS [j1] +ORDER BY [j].[Id] +"""); + } + + public override async Task Json_entity_with_inheritance_basic_projection(bool async) + { + // TODO:SQLJSON Returns empty (invalid) JSON (See BadJson.cs) + if (async) + { + Assert.Equal( + "Unable to cast object of type 'System.DBNull' to type 'System.String'.", + (await Assert.ThrowsAsync(() => base.Json_entity_with_inheritance_basic_projection(true))).Message); + } + else + { + Assert.Equal( + RelationalStrings.JsonEmptyString, + (await Assert.ThrowsAsync(() => base.Json_entity_with_inheritance_basic_projection(false))) + .Message); + } + + AssertSql( + """ +SELECT [j].[Id], [j].[Discriminator], [j].[Name], [j].[Fraction], [j].[CollectionOnBase], [j].[ReferenceOnBase], [j].[CollectionOnDerived], [j].[ReferenceOnDerived] +FROM [JsonEntitiesInheritance] AS [j] +"""); + } + + public override async Task Json_entity_with_inheritance_project_derived(bool async) + { + await base.Json_entity_with_inheritance_project_derived(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[Discriminator], [j].[Name], [j].[Fraction], [j].[CollectionOnBase], [j].[ReferenceOnBase], [j].[CollectionOnDerived], [j].[ReferenceOnDerived] +FROM [JsonEntitiesInheritance] AS [j] +WHERE [j].[Discriminator] = N'JsonEntityInheritanceDerived' +"""); + } + + public override async Task Json_entity_with_inheritance_project_navigations(bool async) + { + await base.Json_entity_with_inheritance_project_navigations(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[ReferenceOnBase], [j].[CollectionOnBase] +FROM [JsonEntitiesInheritance] AS [j] +"""); + } + + public override async Task Json_entity_with_inheritance_project_navigations_on_derived(bool async) + { + await base.Json_entity_with_inheritance_project_navigations_on_derived(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[ReferenceOnBase], [j].[ReferenceOnDerived], [j].[CollectionOnBase], [j].[CollectionOnDerived] +FROM [JsonEntitiesInheritance] AS [j] +WHERE [j].[Discriminator] = N'JsonEntityInheritanceDerived' +"""); + } + + public override async Task Json_entity_backtracking(bool async) + { + await base.Json_entity_backtracking(async); + + AssertSql( + @""); + } + + public override async Task Json_collection_index_in_projection_basic(bool async) + { + await base.Json_collection_index_in_projection_basic(async); + + AssertSql( + """ +SELECT JSON_QUERY([j].[OwnedCollectionRoot], '$[1]'), [j].[Id] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Json_collection_ElementAt_in_projection(bool async) + { + await base.Json_collection_ElementAt_in_projection(async); + + AssertSql( + """ +SELECT JSON_QUERY([j].[OwnedCollectionRoot], '$[1]'), [j].[Id] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Json_collection_ElementAtOrDefault_in_projection(bool async) + { + await base.Json_collection_ElementAtOrDefault_in_projection(async); + + AssertSql( + """ +SELECT JSON_QUERY([j].[OwnedCollectionRoot], '$[1]'), [j].[Id] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Json_collection_index_in_projection_project_collection(bool async) + { + await base.Json_collection_index_in_projection_project_collection(async); + + AssertSql( + """ +SELECT JSON_QUERY([j].[OwnedCollectionRoot], '$[1].OwnedCollectionBranch'), [j].[Id] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Json_collection_ElementAt_project_collection(bool async) + { + await base.Json_collection_ElementAt_project_collection(async); + + AssertSql( + """ +SELECT JSON_QUERY([j].[OwnedCollectionRoot], '$[1].OwnedCollectionBranch'), [j].[Id] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Json_collection_ElementAtOrDefault_project_collection(bool async) + { + await base.Json_collection_ElementAtOrDefault_project_collection(async); + + AssertSql( + """ +SELECT JSON_QUERY([j].[OwnedCollectionRoot], '$[1].OwnedCollectionBranch'), [j].[Id] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + [SqlServerCondition(SqlServerCondition.SupportsJsonPathExpressions)] + public override async Task Json_collection_index_in_projection_using_parameter(bool async) + { + await base.Json_collection_index_in_projection_using_parameter(async); + + AssertSql( + """ +@__prm_0='0' + +SELECT JSON_QUERY([j].[OwnedCollectionRoot], '$[' + CAST(@__prm_0 AS nvarchar(max)) + ']'), [j].[Id], @__prm_0 +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + [SqlServerCondition(SqlServerCondition.SupportsJsonPathExpressions)] + public override async Task Json_collection_index_in_projection_using_column(bool async) + { + await base.Json_collection_index_in_projection_using_column(async); + + AssertSql( + """ +SELECT JSON_QUERY([j].[OwnedCollectionRoot], '$[' + CAST([j].[Id] AS nvarchar(max)) + ']'), [j].[Id] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Json_collection_index_in_projection_using_untranslatable_client_method(bool async) + { + var message = (await Assert.ThrowsAsync( + () => base.Json_collection_index_in_projection_using_untranslatable_client_method(async))).Message; + + Assert.Contains( + CoreStrings.QueryUnableToTranslateMethod( + "Microsoft.EntityFrameworkCore.Query.JsonQueryTestBase", + "MyMethod"), + message); + } + + public override async Task Json_collection_index_in_projection_using_untranslatable_client_method2(bool async) + { + var message = (await Assert.ThrowsAsync( + () => base.Json_collection_index_in_projection_using_untranslatable_client_method2(async))).Message; + + Assert.Contains( + CoreStrings.QueryUnableToTranslateMethod( + "Microsoft.EntityFrameworkCore.Query.JsonQueryTestBase", + "MyMethod"), + message); + } + + public override async Task Json_collection_index_outside_bounds(bool async) + { + // TODO:SQLJSON Returns empty (invalid) JSON (See BadJson.cs) + if (async) + { + Assert.Equal( + "Unable to cast object of type 'System.DBNull' to type 'System.String'.", + (await Assert.ThrowsAsync(() => base.Json_collection_index_outside_bounds(true))).Message); + } + else + { + Assert.Equal( + RelationalStrings.JsonEmptyString, + (await Assert.ThrowsAsync(() => base.Json_collection_index_outside_bounds(false))) + .Message); + } + + AssertSql( + """ +SELECT JSON_QUERY([j].[OwnedCollectionRoot], '$[25]'), [j].[Id] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Json_collection_index_outside_bounds2(bool async) + { + // TODO:SQLJSON Returns empty (invalid) JSON (See BadJson.cs) + if (async) + { + Assert.Equal( + "Unable to cast object of type 'System.DBNull' to type 'System.String'.", + (await Assert.ThrowsAsync(() => base.Json_collection_index_outside_bounds2(true))).Message); + } + else + { + Assert.Equal( + RelationalStrings.JsonEmptyString, + (await Assert.ThrowsAsync(() => base.Json_collection_index_outside_bounds2(false))) + .Message); + } + + AssertSql( + """ +SELECT JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch.OwnedCollectionLeaf[25]'), [j].[Id] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Json_collection_index_outside_bounds_with_property_access(bool async) + { + await base.Json_collection_index_outside_bounds_with_property_access(async); + + AssertSql( + """ +SELECT CAST(JSON_VALUE([j].[OwnedCollectionRoot], '$[25].Number') AS int) +FROM [JsonEntitiesBasic] AS [j] +ORDER BY [j].[Id] +"""); + } + + [SqlServerCondition(SqlServerCondition.SupportsJsonPathExpressions)] + public override async Task Json_collection_index_in_projection_nested(bool async) + { + await base.Json_collection_index_in_projection_nested(async); + + AssertSql( + """ +@__prm_0='1' + +SELECT JSON_QUERY([j].[OwnedCollectionRoot], '$[0].OwnedCollectionBranch[' + CAST(@__prm_0 AS nvarchar(max)) + ']'), [j].[Id], @__prm_0 +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + [SqlServerCondition(SqlServerCondition.SupportsJsonPathExpressions)] + public override async Task Json_collection_index_in_projection_nested_project_scalar(bool async) + { + await base.Json_collection_index_in_projection_nested_project_scalar(async); + + AssertSql( + """ +@__prm_0='1' + +SELECT CAST(JSON_VALUE([j].[OwnedCollectionRoot], '$[0].OwnedCollectionBranch[' + CAST(@__prm_0 AS nvarchar(max)) + '].Date') AS datetime2) +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + [SqlServerCondition(SqlServerCondition.SupportsJsonPathExpressions)] + public override async Task Json_collection_index_in_projection_nested_project_reference(bool async) + { + await base.Json_collection_index_in_projection_nested_project_reference(async); + + AssertSql( + """ +@__prm_0='1' + +SELECT JSON_QUERY([j].[OwnedCollectionRoot], '$[0].OwnedCollectionBranch[' + CAST(@__prm_0 AS nvarchar(max)) + '].OwnedReferenceLeaf'), [j].[Id], @__prm_0 +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + [SqlServerCondition(SqlServerCondition.SupportsJsonPathExpressions)] + public override async Task Json_collection_index_in_projection_nested_project_collection(bool async) + { + await base.Json_collection_index_in_projection_nested_project_collection(async); + + AssertSql( + """ +@__prm_0='1' + +SELECT JSON_QUERY([j].[OwnedCollectionRoot], '$[0].OwnedCollectionBranch[' + CAST(@__prm_0 AS nvarchar(max)) + '].OwnedCollectionLeaf'), [j].[Id], @__prm_0 +FROM [JsonEntitiesBasic] AS [j] +ORDER BY [j].[Id] +"""); + } + + [SqlServerCondition(SqlServerCondition.SupportsJsonPathExpressions)] + public override async Task Json_collection_index_in_projection_nested_project_collection_anonymous_projection(bool async) + { + await base.Json_collection_index_in_projection_nested_project_collection_anonymous_projection(async); + + AssertSql( + """ +@__prm_0='1' + +SELECT [j].[Id], JSON_QUERY([j].[OwnedCollectionRoot], '$[0].OwnedCollectionBranch[' + CAST(@__prm_0 AS nvarchar(max)) + '].OwnedCollectionLeaf'), @__prm_0 +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Json_collection_index_in_predicate_using_constant(bool async) + { + await base.Json_collection_index_in_predicate_using_constant(async); + + AssertSql( + """ +SELECT [j].[Id] +FROM [JsonEntitiesBasic] AS [j] +WHERE JSON_VALUE([j].[OwnedCollectionRoot], '$[0].Name') <> N'Foo' OR JSON_VALUE([j].[OwnedCollectionRoot], '$[0].Name') IS NULL +"""); + } + + [SqlServerCondition(SqlServerCondition.SupportsJsonPathExpressions)] + public override async Task Json_collection_index_in_predicate_using_variable(bool async) + { + await base.Json_collection_index_in_predicate_using_variable(async); + + AssertSql( + """ +@__prm_0='1' + +SELECT [j].[Id] +FROM [JsonEntitiesBasic] AS [j] +WHERE JSON_VALUE([j].[OwnedCollectionRoot], '$[' + CAST(@__prm_0 AS nvarchar(max)) + '].Name') <> N'Foo' OR JSON_VALUE([j].[OwnedCollectionRoot], '$[' + CAST(@__prm_0 AS nvarchar(max)) + '].Name') IS NULL +"""); + } + + [SqlServerCondition(SqlServerCondition.SupportsJsonPathExpressions)] + public override async Task Json_collection_index_in_predicate_using_column(bool async) + { + await base.Json_collection_index_in_predicate_using_column(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +WHERE JSON_VALUE([j].[OwnedCollectionRoot], '$[' + CAST([j].[Id] AS nvarchar(max)) + '].Name') = N'e1_c2' +"""); + } + + [SqlServerCondition(SqlServerCondition.SupportsJsonPathExpressions)] + public override async Task Json_collection_index_in_predicate_using_complex_expression1(bool async) + { + await base.Json_collection_index_in_predicate_using_complex_expression1(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +WHERE JSON_VALUE([j].[OwnedCollectionRoot], '$[' + CAST(CASE + WHEN [j].[Id] = 1 THEN 0 + ELSE 1 +END AS nvarchar(max)) + '].Name') = N'e1_c1' +"""); + } + + [SqlServerCondition(SqlServerCondition.SupportsJsonPathExpressions)] + public override async Task Json_collection_index_in_predicate_using_complex_expression2(bool async) + { + await base.Json_collection_index_in_predicate_using_complex_expression2(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +WHERE JSON_VALUE([j].[OwnedCollectionRoot], '$[' + CAST(( + SELECT MAX([j0].[Id]) + FROM [JsonEntitiesBasic] AS [j0]) AS nvarchar(max)) + '].Name') = N'e1_c2' +"""); + } + + public override async Task Json_collection_ElementAt_in_predicate(bool async) + { + await base.Json_collection_ElementAt_in_predicate(async); + + AssertSql( + """ +SELECT [j].[Id] +FROM [JsonEntitiesBasic] AS [j] +WHERE JSON_VALUE([j].[OwnedCollectionRoot], '$[1].Name') <> N'Foo' OR JSON_VALUE([j].[OwnedCollectionRoot], '$[1].Name') IS NULL +"""); + } + + [SqlServerCondition(SqlServerCondition.SupportsJsonPathExpressions)] + public override async Task Json_collection_index_in_predicate_nested_mix(bool async) + { + await base.Json_collection_index_in_predicate_nested_mix(async); + + AssertSql( + """ +@__prm_0='0' + +SELECT [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +WHERE JSON_VALUE([j].[OwnedCollectionRoot], '$[1].OwnedCollectionBranch[' + CAST(@__prm_0 AS nvarchar(max)) + '].OwnedCollectionLeaf[' + CAST([j].[Id] - 1 AS nvarchar(max)) + '].SomethingSomething') = N'e1_c2_c1_c1' +"""); + } + + public override async Task Json_collection_ElementAt_and_pushdown(bool async) + { + await base.Json_collection_ElementAt_and_pushdown(async); + + AssertSql( + """ +SELECT [j].[Id], CAST(JSON_VALUE([j].[OwnedCollectionRoot], '$[0].Number') AS int) AS [CollectionElement] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Json_collection_Any_with_predicate(bool async) + { + await base.Json_collection_Any_with_predicate(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +WHERE EXISTS ( + SELECT 1 + FROM OPENJSON(CAST([j].[OwnedReferenceRoot] AS nvarchar(max)), '$.OwnedCollectionBranch') WITH ([OwnedReferenceLeaf] nvarchar(max) '$.OwnedReferenceLeaf' AS JSON) AS [o] + WHERE JSON_VALUE([o].[OwnedReferenceLeaf], '$.SomethingSomething') = N'e1_r_c1_r') +"""); + } + + public override async Task Json_collection_Where_ElementAt(bool async) + { + await base.Json_collection_Where_ElementAt(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +WHERE ( + SELECT JSON_VALUE([o].[value], '$.OwnedReferenceLeaf.SomethingSomething') + FROM OPENJSON(CAST([j].[OwnedReferenceRoot] AS nvarchar(max)), '$.OwnedCollectionBranch') AS [o] + WHERE CAST(JSON_VALUE([o].[value], '$.Enum') AS int) = -3 + ORDER BY CAST([o].[key] AS int) + OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY) = N'e1_r_c2_r' +"""); + } + + public override async Task Json_collection_Skip(bool async) + { + await base.Json_collection_Skip(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +WHERE ( + SELECT [o0].[c] + FROM ( + SELECT JSON_VALUE([o].[value], '$.OwnedReferenceLeaf.SomethingSomething') AS [c], CAST([o].[key] AS int) AS [c0] + FROM OPENJSON(CAST([j].[OwnedReferenceRoot] AS nvarchar(max)), '$.OwnedCollectionBranch') AS [o] + ORDER BY CAST([o].[key] AS int) + OFFSET 1 ROWS + ) AS [o0] + ORDER BY [o0].[c0] + OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY) = N'e1_r_c2_r' +"""); + } + + public override async Task Json_collection_OrderByDescending_Skip_ElementAt(bool async) + { + await base.Json_collection_OrderByDescending_Skip_ElementAt(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +WHERE ( + SELECT [o0].[c] + FROM ( + SELECT JSON_VALUE([o].[OwnedReferenceLeaf], '$.SomethingSomething') AS [c], [o].[Date] AS [c0] + FROM OPENJSON(CAST([j].[OwnedReferenceRoot] AS nvarchar(max)), '$.OwnedCollectionBranch') WITH ( + [Date] datetime2 '$.Date', + [Enum] int '$.Enum', + [Fraction] decimal(18,2) '$.Fraction', + [OwnedReferenceLeaf] nvarchar(max) '$.OwnedReferenceLeaf' AS JSON + ) AS [o] + ORDER BY [o].[Date] DESC + OFFSET 1 ROWS + ) AS [o0] + ORDER BY [o0].[c0] DESC + OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY) = N'e1_r_c1_r' +"""); + } + + public override async Task Json_collection_Distinct_Count_with_predicate(bool async) + { + await base.Json_collection_Distinct_Count_with_predicate(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +WHERE ( + SELECT COUNT(*) + FROM ( + 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(CAST([j].[OwnedReferenceRoot] AS nvarchar(max)), '$.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] + WHERE JSON_VALUE([o].[OwnedReferenceLeaf], '$.SomethingSomething') = N'e1_r_c2_r' + ) AS [o0]) = 1 +"""); + } + + public override async Task Json_collection_within_collection_Count(bool async) + { + await base.Json_collection_within_collection_Count(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +WHERE EXISTS ( + SELECT 1 + FROM OPENJSON(CAST([j].[OwnedCollectionRoot] AS nvarchar(max)), '$') WITH ([OwnedCollectionBranch] nvarchar(max) '$.OwnedCollectionBranch' AS JSON) AS [o] + WHERE ( + SELECT COUNT(*) + FROM OPENJSON(CAST([o].[OwnedCollectionBranch] AS nvarchar(max)), '$') AS [o0]) = 2) +"""); + } + + public override async Task Json_collection_in_projection_with_composition_count(bool async) + { + await base.Json_collection_in_projection_with_composition_count(async); + + AssertSql( + """ +SELECT ( + SELECT COUNT(*) + FROM OPENJSON(CAST([j].[OwnedCollectionRoot] AS nvarchar(max)), '$') AS [o]) +FROM [JsonEntitiesBasic] AS [j] +ORDER BY [j].[Id] +"""); + } + + public override async Task Json_collection_in_projection_with_anonymous_projection_of_scalars(bool async) + { + await base.Json_collection_in_projection_with_anonymous_projection_of_scalars(async); + + AssertSql( + """ +SELECT [j].[Id], JSON_VALUE([o].[value], '$.Name'), CAST(JSON_VALUE([o].[value], '$.Number') AS int), [o].[key] +FROM [JsonEntitiesBasic] AS [j] +OUTER APPLY OPENJSON(CAST([j].[OwnedCollectionRoot] AS nvarchar(max)), '$') AS [o] +ORDER BY [j].[Id], CAST([o].[key] AS int) +"""); + } + + public override async Task Json_collection_in_projection_with_composition_where_and_anonymous_projection_of_scalars(bool async) + { + await base.Json_collection_in_projection_with_composition_where_and_anonymous_projection_of_scalars(async); + + AssertSql( + """ +SELECT [j].[Id], [o0].[Name], [o0].[Number], [o0].[key] +FROM [JsonEntitiesBasic] AS [j] +OUTER APPLY ( + SELECT JSON_VALUE([o].[value], '$.Name') AS [Name], CAST(JSON_VALUE([o].[value], '$.Number') AS int) AS [Number], [o].[key], CAST([o].[key] AS int) AS [c] + FROM OPENJSON(CAST([j].[OwnedCollectionRoot] AS nvarchar(max)), '$') AS [o] + WHERE JSON_VALUE([o].[value], '$.Name') = N'Foo' +) AS [o0] +ORDER BY [j].[Id], [o0].[c] +"""); + } + + public override async Task Json_collection_in_projection_with_composition_where_and_anonymous_projection_of_primitive_arrays(bool async) + { + await base.Json_collection_in_projection_with_composition_where_and_anonymous_projection_of_primitive_arrays(async); + + AssertSql( + """ +SELECT [j].[Id], [o0].[Names], [o0].[Numbers], [o0].[key] +FROM [JsonEntitiesBasic] AS [j] +OUTER APPLY ( + SELECT JSON_QUERY([o].[value], '$.Names') AS [Names], JSON_QUERY([o].[value], '$.Numbers') AS [Numbers], [o].[key], CAST([o].[key] AS int) AS [c] + FROM OPENJSON(CAST([j].[OwnedCollectionRoot] AS nvarchar(max)), '$') AS [o] + WHERE JSON_VALUE([o].[value], '$.Name') = N'Foo' +) AS [o0] +ORDER BY [j].[Id], [o0].[c] +"""); + } + + public override async Task Json_collection_filter_in_projection(bool async) + { + await base.Json_collection_filter_in_projection(async); + + AssertSql( + """ +SELECT [j].[Id], [o0].[Id], [o0].[Name], [o0].[Names], [o0].[Number], [o0].[Numbers], [o0].[c], [o0].[c0], [o0].[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(CAST([j].[OwnedCollectionRoot] AS nvarchar(max)), '$') AS [o] + WHERE JSON_VALUE([o].[value], '$.Name') <> N'Foo' OR JSON_VALUE([o].[value], '$.Name') IS NULL +) AS [o0] +ORDER BY [j].[Id], [o0].[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], [s].[key], [s].[Id], [s].[Date], [s].[Enum], [s].[Enums], [s].[Fraction], [s].[NullableEnum], [s].[NullableEnums], [s].[c], [s].[c0], [s].[key0] +FROM [JsonEntitiesBasic] AS [j] +OUTER APPLY ( + SELECT [o].[key], [o1].[Id], [o1].[Date], [o1].[Enum], [o1].[Enums], [o1].[Fraction], [o1].[NullableEnum], [o1].[NullableEnums], [o1].[c], [o1].[c0], [o1].[key] AS [key0], CAST([o].[key] AS int) AS [c1], [o1].[c1] AS [c10] + FROM OPENJSON(CAST([j].[OwnedCollectionRoot] AS nvarchar(max)), '$') 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(CAST(JSON_QUERY([o].[value], '$.OwnedCollectionBranch') AS nvarchar(max)), '$') AS [o0] + WHERE CAST(JSON_VALUE([o0].[value], '$.Date') AS datetime2) <> '2000-01-01T00:00:00.0000000' + ) AS [o1] +) AS [s] +ORDER BY [j].[Id], [s].[c1], [s].[key], [s].[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], [s].[key], [s].[c], [s].[c0], [s].[c1], [s].[c2], [s].[c3], [s].[Id], [s].[c4], [s].[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(CAST([j].[OwnedCollectionRoot] AS nvarchar(max)), '$') AS [o] + OUTER APPLY OPENJSON(CAST(JSON_QUERY([o].[value], '$.OwnedCollectionBranch') AS nvarchar(max)), '$') AS [o0] +) AS [s] +ORDER BY [j].[Id], [s].[c5], [s].[key], [s].[c6] +"""); + } + + public override async Task Json_collection_skip_take_in_projection(bool async) + { + await base.Json_collection_skip_take_in_projection(async); + + AssertSql( + """ +SELECT [j].[Id], [o0].[Id], [o0].[Name], [o0].[Names], [o0].[Number], [o0].[Numbers], [o0].[c], [o0].[c0], [o0].[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(CAST([j].[OwnedCollectionRoot] AS nvarchar(max)), '$') AS [o] + ORDER BY JSON_VALUE([o].[value], '$.Name') + OFFSET 1 ROWS FETCH NEXT 5 ROWS ONLY +) AS [o0] +ORDER BY [j].[Id], [o0].[c1] +"""); + } + + public override async Task Json_collection_skip_take_in_projection_project_into_anonymous_type(bool async) + { + await base.Json_collection_skip_take_in_projection_project_into_anonymous_type(async); + + AssertSql( + """ +SELECT [j].[Id], [o0].[c], [o0].[c0], [o0].[c1], [o0].[c2], [o0].[c3], [o0].[Id], [o0].[c4], [o0].[key] +FROM [JsonEntitiesBasic] AS [j] +OUTER APPLY ( + SELECT JSON_VALUE([o].[value], '$.Name') AS [c], JSON_QUERY([o].[value], '$.Names') AS [c0], CAST(JSON_VALUE([o].[value], '$.Number') AS int) AS [c1], JSON_QUERY([o].[value], '$.Numbers') AS [c2], JSON_QUERY([o].[value], '$.OwnedCollectionBranch') AS [c3], [j].[Id], JSON_QUERY([o].[value], '$.OwnedReferenceBranch') AS [c4], [o].[key] + FROM OPENJSON(CAST([j].[OwnedCollectionRoot] AS nvarchar(max)), '$') AS [o] + ORDER BY JSON_VALUE([o].[value], '$.Name') + OFFSET 1 ROWS FETCH NEXT 5 ROWS ONLY +) AS [o0] +ORDER BY [j].[Id], [o0].[c] +"""); + } + + public override async Task Json_collection_skip_take_in_projection_with_json_reference_access_as_final_operation(bool async) + { + await base.Json_collection_skip_take_in_projection_with_json_reference_access_as_final_operation(async); + + AssertSql( + """ +SELECT [j].[Id], [o0].[c], [o0].[Id], [o0].[key] +FROM [JsonEntitiesBasic] AS [j] +OUTER APPLY ( + SELECT JSON_QUERY([o].[value], '$.OwnedReferenceBranch') AS [c], [j].[Id], [o].[key], JSON_VALUE([o].[value], '$.Name') AS [c0] + FROM OPENJSON(CAST([j].[OwnedCollectionRoot] AS nvarchar(max)), '$') AS [o] + ORDER BY JSON_VALUE([o].[value], '$.Name') + OFFSET 1 ROWS FETCH NEXT 5 ROWS ONLY +) AS [o0] +ORDER BY [j].[Id], [o0].[c0] +"""); + } + + public override async Task Json_collection_distinct_in_projection(bool async) + { + await base.Json_collection_distinct_in_projection(async); + + AssertSql( + """ +SELECT [j].[Id], [o0].[Id], [o0].[Name], [o0].[Names], [o0].[Number], [o0].[Numbers], [o0].[c], [o0].[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(CAST([j].[OwnedCollectionRoot] AS nvarchar(max)), '$') 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 [o0] +ORDER BY [j].[Id], [o0].[Name], [o0].[Names], [o0].[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(""); + } + + public override async Task Json_collection_leaf_filter_in_projection(bool async) + { + await base.Json_collection_leaf_filter_in_projection(async); + + AssertSql( + """ +SELECT [j].[Id], [o0].[Id], [o0].[SomethingSomething], [o0].[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(CAST([j].[OwnedReferenceRoot] AS nvarchar(max)), '$.OwnedReferenceBranch.OwnedCollectionLeaf') AS [o] + WHERE JSON_VALUE([o].[value], '$.SomethingSomething') <> N'Baz' OR JSON_VALUE([o].[value], '$.SomethingSomething') IS NULL +) AS [o0] +ORDER BY [j].[Id], [o0].[c] +"""); + } + + public override async Task Json_multiple_collection_projections(bool async) + { + await base.Json_multiple_collection_projections(async); + + AssertSql( + """ +SELECT [j].[Id], [o4].[Id], [o4].[SomethingSomething], [o4].[key], [o1].[Id], [o1].[Name], [o1].[Names], [o1].[Number], [o1].[Numbers], [o1].[c], [o1].[c0], [s].[key], [s].[Id], [s].[Date], [s].[Enum], [s].[Enums], [s].[Fraction], [s].[NullableEnum], [s].[NullableEnums], [s].[c], [s].[c0], [s].[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(CAST([j].[OwnedReferenceRoot] AS nvarchar(max)), '$.OwnedReferenceBranch.OwnedCollectionLeaf') AS [o] + WHERE JSON_VALUE([o].[value], '$.SomethingSomething') <> N'Baz' OR JSON_VALUE([o].[value], '$.SomethingSomething') IS NULL +) AS [o4] +OUTER APPLY ( + SELECT DISTINCT [j].[Id], [o0].[Name], [o0].[Names], [o0].[Number], [o0].[Numbers], [o0].[OwnedCollectionBranch] AS [c], [o0].[OwnedReferenceBranch] AS [c0] + FROM OPENJSON(CAST([j].[OwnedCollectionRoot] AS nvarchar(max)), '$') 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 [o1] +OUTER APPLY ( + SELECT [o2].[key], [o5].[Id], [o5].[Date], [o5].[Enum], [o5].[Enums], [o5].[Fraction], [o5].[NullableEnum], [o5].[NullableEnums], [o5].[c], [o5].[c0], [o5].[key] AS [key0], CAST([o2].[key] AS int) AS [c1], [o5].[c1] AS [c10] + FROM OPENJSON(CAST([j].[OwnedCollectionRoot] AS nvarchar(max)), '$') AS [o2] + OUTER APPLY ( + SELECT [j].[Id], CAST(JSON_VALUE([o3].[value], '$.Date') AS datetime2) AS [Date], CAST(JSON_VALUE([o3].[value], '$.Enum') AS int) AS [Enum], JSON_QUERY([o3].[value], '$.Enums') AS [Enums], CAST(JSON_VALUE([o3].[value], '$.Fraction') AS decimal(18,2)) AS [Fraction], CAST(JSON_VALUE([o3].[value], '$.NullableEnum') AS int) AS [NullableEnum], JSON_QUERY([o3].[value], '$.NullableEnums') AS [NullableEnums], JSON_QUERY([o3].[value], '$.OwnedCollectionLeaf') AS [c], JSON_QUERY([o3].[value], '$.OwnedReferenceLeaf') AS [c0], [o3].[key], CAST([o3].[key] AS int) AS [c1] + FROM OPENJSON(CAST(JSON_QUERY([o2].[value], '$.OwnedCollectionBranch') AS nvarchar(max)), '$') AS [o3] + WHERE CAST(JSON_VALUE([o3].[value], '$.Date') AS datetime2) <> '2000-01-01T00:00:00.0000000' + ) AS [o5] +) AS [s] +LEFT JOIN [JsonEntitiesBasicForCollection] AS [j0] ON [j].[Id] = [j0].[ParentId] +ORDER BY [j].[Id], [o4].[c], [o4].[key], [o1].[Name], [o1].[Names], [o1].[Number], [o1].[Numbers], [s].[c1], [s].[key], [s].[c10], [s].[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], [o0].[Id], [o0].[Date], [o0].[Enum], [o0].[Enums], [o0].[Fraction], [o0].[NullableEnum], [o0].[NullableEnums], [o0].[c], [o0].[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(CAST([j].[OwnedReferenceRoot] AS nvarchar(max)), '$.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 [o0] +LEFT JOIN [JsonEntitiesBasicForCollection] AS [j0] ON [j].[Id] = [j0].[ParentId] +ORDER BY [j].[Id], [o0].[Date], [o0].[Enum], [o0].[Enums], [o0].[Fraction], [o0].[NullableEnum], [o0].[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], [o0].[Id], [o0].[SomethingSomething], [j0].[Id], [j0].[Name], [j0].[ParentId] +FROM [JsonEntitiesBasic] AS [j] +OUTER APPLY ( + SELECT DISTINCT [j].[Id], [o].[SomethingSomething] + FROM OPENJSON(CAST([j].[OwnedReferenceRoot] AS nvarchar(max)), '$.OwnedReferenceBranch.OwnedCollectionLeaf') WITH ([SomethingSomething] nvarchar(max) '$.SomethingSomething') AS [o] +) AS [o0] +LEFT JOIN [JsonEntitiesBasicForCollection] AS [j0] ON [j].[Id] = [j0].[ParentId] +ORDER BY [j].[Id], [o0].[SomethingSomething] +"""); + } + + public override async Task Json_collection_SelectMany(bool async) + { + await base.Json_collection_SelectMany(async); + + AssertSql( + """ +SELECT [j].[Id], [o].[Name], [o].[Names], [o].[Number], [o].[Numbers], [o].[OwnedCollectionBranch], [o].[OwnedReferenceBranch] +FROM [JsonEntitiesBasic] AS [j] +CROSS APPLY OPENJSON(CAST([j].[OwnedCollectionRoot] AS nvarchar(max)), '$') 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( + """ +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(CAST([j].[OwnedReferenceRoot] AS nvarchar(max)), '$.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) + { + // TODO:SQLJSON (See JsonTypeToFunction.cs) + Assert.Equal( + "OpenJson support not yet supported for JSON native data type.", + (await Assert.ThrowsAsync( + () => base.Json_collection_of_primitives_SelectMany(async))).Message); + + AssertSql( + """ +SELECT [n].[value] +FROM [JsonEntitiesBasic] AS [j] +CROSS APPLY OPENJSON(JSON_QUERY([j].[OwnedReferenceRoot], '$.Names')) WITH ([value] nvarchar(max) '$') AS [n] +"""); + } + + public override async Task Json_collection_of_primitives_index_used_in_predicate(bool async) + { + await base.Json_collection_of_primitives_index_used_in_predicate(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +WHERE JSON_VALUE([j].[OwnedReferenceRoot], '$.Names[0]') = N'e1_r1' +"""); + } + + public override async Task Json_collection_of_primitives_index_used_in_projection(bool async) + { + await base.Json_collection_of_primitives_index_used_in_projection(async); + + AssertSql( + """ +SELECT CAST(JSON_VALUE([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch.Enums[0]') AS int) +FROM [JsonEntitiesBasic] AS [j] +ORDER BY [j].[Id] +"""); + } + + public override async Task Json_collection_of_primitives_index_used_in_orderby(bool async) + { + await base.Json_collection_of_primitives_index_used_in_orderby(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +ORDER BY CAST(JSON_VALUE([j].[OwnedReferenceRoot], '$.Numbers[0]') AS int) +"""); + } + + public override async Task Json_collection_of_primitives_contains_in_predicate(bool async) + { + // TODO:SQLJSON (See JsonTypeToFunction.cs) + Assert.Equal( + "OpenJson support not yet supported for JSON native data type.", + (await Assert.ThrowsAsync( + () => base.Json_collection_of_primitives_contains_in_predicate(async))).Message); + + AssertSql( + """ +SELECT [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +WHERE N'e1_r1' IN ( + SELECT [n].[value] + FROM OPENJSON(JSON_QUERY([j].[OwnedReferenceRoot], '$.Names')) WITH ([value] nvarchar(max) '$') AS [n] +) +"""); + } + + [SqlServerCondition(SqlServerCondition.SupportsJsonPathExpressions)] + public override async Task Json_collection_index_with_parameter_Select_ElementAt(bool async) + { + await base.Json_collection_index_with_parameter_Select_ElementAt(async); + + AssertSql( + """ +@__prm_0='0' + +SELECT [j].[Id], ( + SELECT N'Foo' + FROM OPENJSON(CAST([j].[OwnedCollectionRoot] AS nvarchar(max)), '$[' + CAST(@__prm_0 AS nvarchar(max)) + '].OwnedCollectionBranch') AS [o] + ORDER BY CAST([o].[key] AS int) + OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY) AS [CollectionElement] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + [SqlServerCondition(SqlServerCondition.SupportsJsonPathExpressions)] + public override async Task Json_collection_index_with_expression_Select_ElementAt(bool async) + { + await base.Json_collection_index_with_expression_Select_ElementAt(async); + + AssertSql( + """ +@__prm_0='0' + +SELECT JSON_VALUE([j].[OwnedCollectionRoot], '$[' + CAST(@__prm_0 + [j].[Id] AS nvarchar(max)) + '].OwnedCollectionBranch[0].OwnedReferenceLeaf.SomethingSomething') +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Json_collection_Select_entity_collection_ElementAt(bool async) + { + await base.Json_collection_Select_entity_collection_ElementAt(async); + + AssertSql( + """ +SELECT JSON_QUERY([j].[OwnedCollectionRoot], '$[0].OwnedCollectionBranch'), [j].[Id] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Json_collection_Select_entity_ElementAt(bool async) + { + await base.Json_collection_Select_entity_ElementAt(async); + + AssertSql( + """ +SELECT JSON_QUERY([j].[OwnedCollectionRoot], '$[0].OwnedReferenceBranch'), [j].[Id] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Json_collection_Select_entity_in_anonymous_object_ElementAt(bool async) + { + await base.Json_collection_Select_entity_in_anonymous_object_ElementAt(async); + + AssertSql( + """ +SELECT [o0].[c], [o0].[Id], [o0].[c0] +FROM [JsonEntitiesBasic] AS [j] +OUTER APPLY ( + SELECT JSON_QUERY([o].[value], '$.OwnedReferenceBranch') AS [c], [j].[Id], 1 AS [c0] + FROM OPENJSON(CAST([j].[OwnedCollectionRoot] AS nvarchar(max)), '$') AS [o] + ORDER BY CAST([o].[key] AS int) + OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY +) AS [o0] +ORDER BY [j].[Id] +"""); + } + + public override async Task Json_collection_Select_entity_with_initializer_ElementAt(bool async) + { + await base.Json_collection_Select_entity_with_initializer_ElementAt(async); + + AssertSql( + """ +SELECT [o0].[Id], [o0].[c] +FROM [JsonEntitiesBasic] AS [j] +OUTER APPLY ( + SELECT [j].[Id], 1 AS [c] + FROM OPENJSON(CAST([j].[OwnedCollectionRoot] AS nvarchar(max)), '$') AS [o] + ORDER BY CAST([o].[key] AS int) + OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY +) AS [o0] +"""); + } + + public override async Task Json_projection_deduplication_with_collection_indexer_in_original(bool async) + { + await base.Json_projection_deduplication_with_collection_indexer_in_original(async); + + AssertSql( + """ +SELECT [j].[Id], JSON_QUERY([j].[OwnedCollectionRoot], '$[0].OwnedReferenceBranch'), JSON_QUERY([j].[OwnedCollectionRoot], '$[0]'), JSON_QUERY([j].[OwnedCollectionRoot], '$[0].OwnedReferenceBranch.OwnedCollectionLeaf') +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + [SqlServerCondition(SqlServerCondition.SupportsJsonPathExpressions)] + public override async Task Json_projection_deduplication_with_collection_indexer_in_target(bool async) + { + await base.Json_projection_deduplication_with_collection_indexer_in_target(async); + + AssertSql( + """ +@__prm_0='1' + +SELECT [j].[Id], JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedCollectionBranch[1]'), [j].[OwnedReferenceRoot], JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch.OwnedCollectionLeaf[' + CAST(@__prm_0 AS nvarchar(max)) + ']'), @__prm_0 +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + [SqlServerCondition(SqlServerCondition.SupportsJsonPathExpressions)] + public override async Task Json_projection_deduplication_with_collection_in_original_and_collection_indexer_in_target(bool async) + { + await base.Json_projection_deduplication_with_collection_in_original_and_collection_indexer_in_target(async); + + AssertSql( + """ +@__prm_0='1' + +SELECT JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedCollectionBranch[0].OwnedCollectionLeaf[' + CAST(@__prm_0 AS nvarchar(max)) + ']'), [j].[Id], @__prm_0, JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedCollectionBranch[' + CAST(@__prm_0 AS nvarchar(max)) + ']'), JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedCollectionBranch'), JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedCollectionBranch[0]') +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Json_collection_index_in_projection_using_constant_when_owner_is_present(bool async) + { + await base.Json_collection_index_in_projection_using_constant_when_owner_is_present(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot], JSON_QUERY([j].[OwnedCollectionRoot], '$[1]') +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Json_collection_index_in_projection_using_constant_when_owner_is_not_present(bool async) + { + await base.Json_collection_index_in_projection_using_constant_when_owner_is_not_present(async); + + AssertSql( + """ +SELECT [j].[Id], JSON_QUERY([j].[OwnedCollectionRoot], '$[1]') +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + [SqlServerCondition(SqlServerCondition.SupportsJsonPathExpressions)] + public override async Task Json_collection_index_in_projection_using_parameter_when_owner_is_present(bool async) + { + await base.Json_collection_index_in_projection_using_parameter_when_owner_is_present(async); + + AssertSql( + """ +@__prm_0='1' + +SELECT [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot], JSON_QUERY([j].[OwnedCollectionRoot], '$[' + CAST(@__prm_0 AS nvarchar(max)) + ']'), @__prm_0 +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + [SqlServerCondition(SqlServerCondition.SupportsJsonPathExpressions)] + public override async Task Json_collection_index_in_projection_using_parameter_when_owner_is_not_present(bool async) + { + await base.Json_collection_index_in_projection_using_parameter_when_owner_is_not_present(async); + + AssertSql( + """ +@__prm_0='1' + +SELECT [j].[Id], JSON_QUERY([j].[OwnedCollectionRoot], '$[' + CAST(@__prm_0 AS nvarchar(max)) + ']'), @__prm_0 +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Json_collection_after_collection_index_in_projection_using_constant_when_owner_is_present(bool async) + { + await base.Json_collection_after_collection_index_in_projection_using_constant_when_owner_is_present(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot], JSON_QUERY([j].[OwnedCollectionRoot], '$[1].OwnedCollectionBranch') +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Json_collection_after_collection_index_in_projection_using_constant_when_owner_is_not_present(bool async) + { + await base.Json_collection_after_collection_index_in_projection_using_constant_when_owner_is_not_present(async); + + AssertSql( + """ +SELECT [j].[Id], JSON_QUERY([j].[OwnedCollectionRoot], '$[1].OwnedCollectionBranch') +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + [SqlServerCondition(SqlServerCondition.SupportsJsonPathExpressions)] + public override async Task Json_collection_after_collection_index_in_projection_using_parameter_when_owner_is_present(bool async) + { + await base.Json_collection_after_collection_index_in_projection_using_parameter_when_owner_is_present(async); + + AssertSql( + """ +@__prm_0='1' + +SELECT [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot], JSON_QUERY([j].[OwnedCollectionRoot], '$[' + CAST(@__prm_0 AS nvarchar(max)) + '].OwnedCollectionBranch'), @__prm_0 +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + [SqlServerCondition(SqlServerCondition.SupportsJsonPathExpressions)] + public override async Task Json_collection_after_collection_index_in_projection_using_parameter_when_owner_is_not_present(bool async) + { + await base.Json_collection_after_collection_index_in_projection_using_parameter_when_owner_is_not_present(async); + + AssertSql( + """ +@__prm_0='1' + +SELECT [j].[Id], JSON_QUERY([j].[OwnedCollectionRoot], '$[' + CAST(@__prm_0 AS nvarchar(max)) + '].OwnedCollectionBranch'), @__prm_0 +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + [SqlServerCondition(SqlServerCondition.SupportsJsonPathExpressions)] + public override async Task Json_collection_index_in_projection_when_owner_is_present_misc1(bool async) + { + await base.Json_collection_index_in_projection_when_owner_is_present_misc1(async); + + AssertSql( + """ +@__prm_0='1' + +SELECT [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot], JSON_QUERY([j].[OwnedCollectionRoot], '$[1].OwnedCollectionBranch[' + CAST(@__prm_0 AS nvarchar(max)) + ']'), @__prm_0 +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + [SqlServerCondition(SqlServerCondition.SupportsJsonPathExpressions)] + public override async Task Json_collection_index_in_projection_when_owner_is_not_present_misc1(bool async) + { + await base.Json_collection_index_in_projection_when_owner_is_not_present_misc1(async); + + AssertSql( + """ +@__prm_0='1' + +SELECT [j].[Id], JSON_QUERY([j].[OwnedCollectionRoot], '$[1].OwnedCollectionBranch[' + CAST(@__prm_0 AS nvarchar(max)) + ']'), @__prm_0 +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Json_collection_index_in_projection_when_owner_is_present_misc2(bool async) + { + await base.Json_collection_index_in_projection_when_owner_is_present_misc2(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot], JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch.OwnedCollectionLeaf[1]') +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Json_collection_index_in_projection_when_owner_is_not_present_misc2(bool async) + { + await base.Json_collection_index_in_projection_when_owner_is_not_present_misc2(async); + + AssertSql( + """ +SELECT [j].[Id], JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch.OwnedCollectionLeaf[1]') +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + [SqlServerCondition(SqlServerCondition.SupportsJsonPathExpressions)] + public override async Task Json_collection_index_in_projection_when_owner_is_present_multiple(bool async) + { + await base.Json_collection_index_in_projection_when_owner_is_present_multiple(async); + + AssertSql( + """ +@__prm_0='1' + +SELECT [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot], JSON_QUERY([j].[OwnedCollectionRoot], '$[' + CAST(@__prm_0 AS nvarchar(max)) + '].OwnedCollectionBranch[1]'), @__prm_0, JSON_QUERY([j].[OwnedCollectionRoot], '$[1].OwnedCollectionBranch[1].OwnedReferenceLeaf'), JSON_QUERY([j].[OwnedCollectionRoot], '$[1].OwnedReferenceBranch'), JSON_QUERY([j].[OwnedCollectionRoot], '$[' + CAST(@__prm_0 AS nvarchar(max)) + '].OwnedReferenceBranch'), JSON_QUERY([j].[OwnedCollectionRoot], '$[' + CAST(@__prm_0 AS nvarchar(max)) + '].OwnedCollectionBranch[' + CAST([j].[Id] AS nvarchar(max)) + ']'), JSON_QUERY([j].[OwnedCollectionRoot], '$[' + CAST([j].[Id] AS nvarchar(max)) + '].OwnedCollectionBranch[1].OwnedReferenceLeaf'), JSON_QUERY([j].[OwnedCollectionRoot], '$[1].OwnedReferenceBranch'), JSON_QUERY([j].[OwnedCollectionRoot], '$[' + CAST([j].[Id] AS nvarchar(max)) + '].OwnedReferenceBranch'), JSON_QUERY([j].[OwnedCollectionRoot], '$[' + CAST([j].[Id] AS nvarchar(max)) + '].OwnedCollectionBranch[' + CAST([j].[Id] AS nvarchar(max)) + ']') +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + [SqlServerCondition(SqlServerCondition.SupportsJsonPathExpressions)] + public override async Task Json_collection_index_in_projection_when_owner_is_not_present_multiple(bool async) + { + await base.Json_collection_index_in_projection_when_owner_is_not_present_multiple(async); + + AssertSql( + """ +@__prm_0='1' + +SELECT [j].[Id], JSON_QUERY([j].[OwnedCollectionRoot], '$[' + CAST(@__prm_0 AS nvarchar(max)) + '].OwnedCollectionBranch[1]'), @__prm_0, JSON_QUERY([j].[OwnedCollectionRoot], '$[1].OwnedCollectionBranch[1].OwnedReferenceLeaf'), JSON_QUERY([j].[OwnedCollectionRoot], '$[1].OwnedReferenceBranch'), JSON_QUERY([j].[OwnedCollectionRoot], '$[' + CAST(@__prm_0 AS nvarchar(max)) + '].OwnedReferenceBranch'), JSON_QUERY([j].[OwnedCollectionRoot], '$[' + CAST(@__prm_0 AS nvarchar(max)) + '].OwnedCollectionBranch[' + CAST([j].[Id] AS nvarchar(max)) + ']'), JSON_QUERY([j].[OwnedCollectionRoot], '$[' + CAST([j].[Id] AS nvarchar(max)) + '].OwnedCollectionBranch[1].OwnedReferenceLeaf'), JSON_QUERY([j].[OwnedCollectionRoot], '$[1].OwnedReferenceBranch'), JSON_QUERY([j].[OwnedCollectionRoot], '$[' + CAST([j].[Id] AS nvarchar(max)) + '].OwnedReferenceBranch'), JSON_QUERY([j].[OwnedCollectionRoot], '$[' + CAST([j].[Id] AS nvarchar(max)) + '].OwnedCollectionBranch[' + CAST([j].[Id] AS nvarchar(max)) + ']') +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Json_scalar_required_null_semantics(bool async) + { + await base.Json_scalar_required_null_semantics(async); + + AssertSql( + """ +SELECT [j].[Name] +FROM [JsonEntitiesBasic] AS [j] +WHERE CAST(JSON_VALUE([j].[OwnedReferenceRoot], '$.Number') AS int) <> CAST(LEN(JSON_VALUE([j].[OwnedReferenceRoot], '$.Name')) AS int) OR JSON_VALUE([j].[OwnedReferenceRoot], '$.Name') IS NULL +"""); + } + + public override async Task Json_scalar_optional_null_semantics(bool async) + { + await base.Json_scalar_optional_null_semantics(async); + + AssertSql( + """ +SELECT [j].[Name] +FROM [JsonEntitiesBasic] AS [j] +WHERE (JSON_VALUE([j].[OwnedReferenceRoot], '$.Name') <> JSON_VALUE([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch.OwnedReferenceLeaf.SomethingSomething') OR JSON_VALUE([j].[OwnedReferenceRoot], '$.Name') IS NULL OR JSON_VALUE([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch.OwnedReferenceLeaf.SomethingSomething') IS NULL) AND (JSON_VALUE([j].[OwnedReferenceRoot], '$.Name') IS NOT NULL OR JSON_VALUE([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch.OwnedReferenceLeaf.SomethingSomething') IS NOT NULL) +"""); + } + + public override async Task Group_by_on_json_scalar(bool async) + { + await base.Group_by_on_json_scalar(async); + + AssertSql( + """ +SELECT [j0].[Key], COUNT(*) AS [Count] +FROM ( + SELECT JSON_VALUE([j].[OwnedReferenceRoot], '$.Name') AS [Key] + FROM [JsonEntitiesBasic] AS [j] +) AS [j0] +GROUP BY [j0].[Key] +"""); + } + + public override async Task Group_by_on_json_scalar_using_collection_indexer(bool async) + { + await base.Group_by_on_json_scalar_using_collection_indexer(async); + + AssertSql( + """ +SELECT [j0].[Key], COUNT(*) AS [Count] +FROM ( + SELECT JSON_VALUE([j].[OwnedCollectionRoot], '$[0].Name') AS [Key] + FROM [JsonEntitiesBasic] AS [j] +) AS [j0] +GROUP BY [j0].[Key] +"""); + } + + public override async Task Group_by_First_on_json_scalar(bool async) + { + await base.Group_by_First_on_json_scalar(async); + + AssertSql( + """ +SELECT [j5].[Id], [j5].[EntityBasicId], [j5].[Name], [j5].[c], [j5].[c0] +FROM ( + SELECT [j0].[Key] + FROM ( + SELECT JSON_VALUE([j].[OwnedReferenceRoot], '$.Name') AS [Key] + FROM [JsonEntitiesBasic] AS [j] + ) AS [j0] + GROUP BY [j0].[Key] +) AS [j3] +LEFT JOIN ( + SELECT [j4].[Id], [j4].[EntityBasicId], [j4].[Name], [j4].[c] AS [c], [j4].[c0] AS [c0], [j4].[Key] + FROM ( + SELECT [j1].[Id], [j1].[EntityBasicId], [j1].[Name], [j1].[c] AS [c], [j1].[c0] AS [c0], [j1].[Key], ROW_NUMBER() OVER(PARTITION BY [j1].[Key] ORDER BY [j1].[Id]) AS [row] + FROM ( + SELECT [j2].[Id], [j2].[EntityBasicId], [j2].[Name], [j2].[OwnedCollectionRoot] AS [c], [j2].[OwnedReferenceRoot] AS [c0], JSON_VALUE([j2].[OwnedReferenceRoot], '$.Name') AS [Key] + FROM [JsonEntitiesBasic] AS [j2] + ) AS [j1] + ) AS [j4] + WHERE [j4].[row] <= 1 +) AS [j5] ON [j3].[Key] = [j5].[Key] +"""); + } + + public override async Task Group_by_FirstOrDefault_on_json_scalar(bool async) + { + await base.Group_by_FirstOrDefault_on_json_scalar(async); + + AssertSql( + """ +SELECT [j5].[Id], [j5].[EntityBasicId], [j5].[Name], [j5].[c], [j5].[c0] +FROM ( + SELECT [j0].[Key] + FROM ( + SELECT JSON_VALUE([j].[OwnedReferenceRoot], '$.Name') AS [Key] + FROM [JsonEntitiesBasic] AS [j] + ) AS [j0] + GROUP BY [j0].[Key] +) AS [j3] +LEFT JOIN ( + SELECT [j4].[Id], [j4].[EntityBasicId], [j4].[Name], [j4].[c] AS [c], [j4].[c0] AS [c0], [j4].[Key] + FROM ( + SELECT [j1].[Id], [j1].[EntityBasicId], [j1].[Name], [j1].[c] AS [c], [j1].[c0] AS [c0], [j1].[Key], ROW_NUMBER() OVER(PARTITION BY [j1].[Key] ORDER BY [j1].[Id]) AS [row] + FROM ( + SELECT [j2].[Id], [j2].[EntityBasicId], [j2].[Name], [j2].[OwnedCollectionRoot] AS [c], [j2].[OwnedReferenceRoot] AS [c0], JSON_VALUE([j2].[OwnedReferenceRoot], '$.Name') AS [Key] + FROM [JsonEntitiesBasic] AS [j2] + ) AS [j1] + ) AS [j4] + WHERE [j4].[row] <= 1 +) AS [j5] ON [j3].[Key] = [j5].[Key] +"""); + } + + public override async Task Group_by_Skip_Take_on_json_scalar(bool async) + { + // TODO:SQLJSON Returns empty (invalid) JSON (See BadJson.cs) + if (async) + { + Assert.Equal( + "Unable to cast object of type 'System.DBNull' to type 'System.String'.", + (await Assert.ThrowsAsync(() => base.Group_by_Skip_Take_on_json_scalar(true))).Message); + } + else + { + await base.Group_by_Skip_Take_on_json_scalar(false); + } + + AssertSql( + """ +SELECT [j3].[Key], [j5].[Id], [j5].[EntityBasicId], [j5].[Name], [j5].[c], [j5].[c0] +FROM ( + SELECT [j0].[Key] + FROM ( + SELECT JSON_VALUE([j].[OwnedReferenceRoot], '$.Name') AS [Key] + FROM [JsonEntitiesBasic] AS [j] + ) AS [j0] + GROUP BY [j0].[Key] +) AS [j3] +LEFT JOIN ( + SELECT [j4].[Id], [j4].[EntityBasicId], [j4].[Name], [j4].[c], [j4].[c0], [j4].[Key] + FROM ( + SELECT [j1].[Id], [j1].[EntityBasicId], [j1].[Name], [j1].[c] AS [c], [j1].[c0] AS [c0], [j1].[Key], ROW_NUMBER() OVER(PARTITION BY [j1].[Key] ORDER BY [j1].[Id]) AS [row] + FROM ( + SELECT [j2].[Id], [j2].[EntityBasicId], [j2].[Name], [j2].[OwnedCollectionRoot] AS [c], [j2].[OwnedReferenceRoot] AS [c0], JSON_VALUE([j2].[OwnedReferenceRoot], '$.Name') AS [Key] + FROM [JsonEntitiesBasic] AS [j2] + ) AS [j1] + ) AS [j4] + WHERE 1 < [j4].[row] AND [j4].[row] <= 6 +) AS [j5] ON [j3].[Key] = [j5].[Key] +ORDER BY [j3].[Key], [j5].[Key], [j5].[Id] +"""); + } + + public override async Task Group_by_json_scalar_Orderby_json_scalar_FirstOrDefault(bool async) + { + await base.Group_by_json_scalar_Orderby_json_scalar_FirstOrDefault(async); + + AssertSql( + @""); + } + + public override async Task Group_by_json_scalar_Skip_First_project_json_scalar(bool async) + { + await base.Group_by_json_scalar_Skip_First_project_json_scalar(async); + + AssertSql( + """ +SELECT ( + SELECT TOP(1) CAST(JSON_VALUE([j1].[c0], '$.OwnedReferenceBranch.Enum') AS int) + FROM ( + SELECT [j2].[OwnedReferenceRoot] AS [c0], JSON_VALUE([j2].[OwnedReferenceRoot], '$.Name') AS [Key] + FROM [JsonEntitiesBasic] AS [j2] + ) AS [j1] + WHERE [j0].[Key] = [j1].[Key] OR ([j0].[Key] IS NULL AND [j1].[Key] IS NULL)) +FROM ( + SELECT JSON_VALUE([j].[OwnedReferenceRoot], '$.Name') AS [Key] + FROM [JsonEntitiesBasic] AS [j] +) AS [j0] +GROUP BY [j0].[Key] +"""); + } + + public override async Task Json_with_include_on_json_entity(bool async) + { + await base.Json_with_include_on_json_entity(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Json_with_include_on_entity_reference(bool async) + { + await base.Json_with_include_on_entity_reference(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot], [j0].[Id], [j0].[Name], [j0].[ParentId] +FROM [JsonEntitiesBasic] AS [j] +LEFT JOIN [JsonEntitiesBasicForReference] AS [j0] ON [j].[Id] = [j0].[ParentId] +"""); + } + + public override async Task Json_with_include_on_entity_collection(bool async) + { + await base.Json_with_include_on_entity_collection(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot], [j0].[Id], [j0].[Name], [j0].[ParentId] +FROM [JsonEntitiesBasic] AS [j] +LEFT JOIN [JsonEntitiesBasicForCollection] AS [j0] ON [j].[Id] = [j0].[ParentId] +ORDER BY [j].[Id] +"""); + } + + public override async Task Entity_including_collection_with_json(bool async) + { + await base.Entity_including_collection_with_json(async); + + AssertSql( + """ +SELECT [e].[Id], [e].[Name], [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [EntitiesBasic] AS [e] +LEFT JOIN [JsonEntitiesBasic] AS [j] ON [e].[Id] = [j].[EntityBasicId] +ORDER BY [e].[Id] +"""); + } + + public override async Task Json_with_include_on_entity_collection_and_reference(bool async) + { + await base.Json_with_include_on_entity_collection_and_reference(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot], [j0].[Id], [j0].[Name], [j0].[ParentId], [j1].[Id], [j1].[Name], [j1].[ParentId] +FROM [JsonEntitiesBasic] AS [j] +LEFT JOIN [JsonEntitiesBasicForReference] AS [j0] ON [j].[Id] = [j0].[ParentId] +LEFT JOIN [JsonEntitiesBasicForCollection] AS [j1] ON [j].[Id] = [j1].[ParentId] +ORDER BY [j].[Id], [j0].[Id] +"""); + } + + public override async Task Json_with_projection_of_json_reference_leaf_and_entity_collection(bool async) + { + await base.Json_with_projection_of_json_reference_leaf_and_entity_collection(async); + + AssertSql( + """ +SELECT JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch.OwnedReferenceLeaf'), [j].[Id], [j0].[Id], [j0].[Name], [j0].[ParentId] +FROM [JsonEntitiesBasic] AS [j] +LEFT JOIN [JsonEntitiesBasicForCollection] AS [j0] ON [j].[Id] = [j0].[ParentId] +ORDER BY [j].[Id] +"""); + } + + public override async Task Json_with_projection_of_json_reference_and_entity_collection(bool async) + { + await base.Json_with_projection_of_json_reference_and_entity_collection(async); + + AssertSql( + """ +SELECT [j].[OwnedReferenceRoot], [j].[Id], [j0].[Id], [j0].[Name], [j0].[ParentId] +FROM [JsonEntitiesBasic] AS [j] +LEFT JOIN [JsonEntitiesBasicForCollection] AS [j0] ON [j].[Id] = [j0].[ParentId] +ORDER BY [j].[Id] +"""); + } + + public override async Task Json_with_projection_of_multiple_json_references_and_entity_collection(bool async) + { + await base.Json_with_projection_of_multiple_json_references_and_entity_collection(async); + + AssertSql( + """ +SELECT [j].[OwnedReferenceRoot], [j].[Id], JSON_QUERY([j].[OwnedCollectionRoot], '$[0].OwnedReferenceBranch'), [j0].[Id], [j0].[Name], [j0].[ParentId], JSON_QUERY([j].[OwnedCollectionRoot], '$[1].OwnedReferenceBranch.OwnedReferenceLeaf'), JSON_QUERY([j].[OwnedCollectionRoot], '$[0].OwnedCollectionBranch[0].OwnedReferenceLeaf') +FROM [JsonEntitiesBasic] AS [j] +LEFT JOIN [JsonEntitiesBasicForCollection] AS [j0] ON [j].[Id] = [j0].[ParentId] +ORDER BY [j].[Id] +"""); + } + + public override async Task Json_with_projection_of_json_collection_leaf_and_entity_collection(bool async) + { + await base.Json_with_projection_of_json_collection_leaf_and_entity_collection(async); + + AssertSql( + """ +SELECT JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch.OwnedCollectionLeaf'), [j].[Id], [j0].[Id], [j0].[Name], [j0].[ParentId] +FROM [JsonEntitiesBasic] AS [j] +LEFT JOIN [JsonEntitiesBasicForCollection] AS [j0] ON [j].[Id] = [j0].[ParentId] +ORDER BY [j].[Id] +"""); + } + + public override async Task Json_with_projection_of_json_collection_and_entity_collection(bool async) + { + await base.Json_with_projection_of_json_collection_and_entity_collection(async); + + AssertSql( + """ +SELECT [j].[OwnedCollectionRoot], [j].[Id], [j0].[Id], [j0].[Name], [j0].[ParentId] +FROM [JsonEntitiesBasic] AS [j] +LEFT JOIN [JsonEntitiesBasicForCollection] AS [j0] ON [j].[Id] = [j0].[ParentId] +ORDER BY [j].[Id] +"""); + } + + public override async Task Json_with_projection_of_json_collection_element_and_entity_collection(bool async) + { + await base.Json_with_projection_of_json_collection_element_and_entity_collection(async); + + AssertSql( + """ +SELECT JSON_QUERY([j].[OwnedCollectionRoot], '$[0]'), [j].[Id], [j0].[Id], [j0].[Name], [j0].[ParentId], [j1].[Id], [j1].[Name], [j1].[ParentId] +FROM [JsonEntitiesBasic] AS [j] +LEFT JOIN [JsonEntitiesBasicForReference] AS [j0] ON [j].[Id] = [j0].[ParentId] +LEFT JOIN [JsonEntitiesBasicForCollection] AS [j1] ON [j].[Id] = [j1].[ParentId] +ORDER BY [j].[Id], [j0].[Id] +"""); + } + + public override async Task Json_with_projection_of_mix_of_json_collections_json_references_and_entity_collection(bool async) + { + await base.Json_with_projection_of_mix_of_json_collections_json_references_and_entity_collection(async); + + AssertSql( + """ +SELECT JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch.OwnedCollectionLeaf'), [j].[Id], [j0].[Id], [j0].[Name], [j0].[ParentId], JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch.OwnedReferenceLeaf'), [j1].[Id], [j1].[Name], [j1].[ParentId], JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch.OwnedCollectionLeaf[0]'), JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedCollectionBranch'), [j].[OwnedCollectionRoot], JSON_QUERY([j].[OwnedCollectionRoot], '$[0].OwnedReferenceBranch'), JSON_QUERY([j].[OwnedCollectionRoot], '$[0].OwnedCollectionBranch') +FROM [JsonEntitiesBasic] AS [j] +LEFT JOIN [JsonEntitiesBasicForReference] AS [j0] ON [j].[Id] = [j0].[ParentId] +LEFT JOIN [JsonEntitiesBasicForCollection] AS [j1] ON [j].[Id] = [j1].[ParentId] +ORDER BY [j].[Id], [j0].[Id] +"""); + +// AssertSql( +//""" +//SELECT JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch.OwnedCollectionLeaf'), [j].[Id], [j0].[Id], [j0].[Name], [j0].[ParentId], JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch.OwnedReferenceLeaf'), [j1].[Id], [j1].[Name], [j1].[ParentId], JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedCollectionBranch'), [j].[OwnedCollectionRoot] +//FROM [JsonEntitiesBasic] AS [j] +//LEFT JOIN [JsonEntitiesBasicForReference] AS [j0] ON [j].[Id] = [j0].[ParentId] +//LEFT JOIN [JsonEntitiesBasicForCollection] AS [j1] ON [j].[Id] = [j1].[ParentId] +//ORDER BY [j].[Id], [j0].[Id] +//"""); + } + + public override async Task Json_all_types_entity_projection(bool async) + { + await base.Json_all_types_entity_projection(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] +"""); + } + + public override async Task Json_all_types_projection_from_owned_entity_reference(bool async) + { + await base.Json_all_types_projection_from_owned_entity_reference(async); + + AssertSql( + """ +SELECT [j].[Reference], [j].[Id] +FROM [JsonEntitiesAllTypes] AS [j] +"""); + } + + public override async Task Json_all_types_projection_individual_properties(bool async) + { + await base.Json_all_types_projection_individual_properties(async); + + 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], '$.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] +"""); + } + + public override async Task Json_boolean_predicate(bool async) + { + await base.Json_boolean_predicate(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], '$.TestBoolean') AS bit) = CAST(1 AS bit) +"""); + } + + public override async Task Json_boolean_predicate_negated(bool async) + { + await base.Json_boolean_predicate_negated(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], '$.TestBoolean') AS bit) = CAST(0 AS bit) +"""); + } + + public override async Task Json_boolean_projection(bool async) + { + await base.Json_boolean_projection(async); + + AssertSql( + """ +SELECT CAST(JSON_VALUE([j].[Reference], '$.TestBoolean') AS bit) +FROM [JsonEntitiesAllTypes] AS [j] +"""); + } + + public override async Task Json_boolean_projection_negated(bool async) + { + await base.Json_boolean_projection_negated(async); + + AssertSql( + """ +SELECT ~CAST(JSON_VALUE([j].[Reference], '$.TestBoolean') AS bit) +FROM [JsonEntitiesAllTypes] AS [j] +"""); + } + + public override async Task Json_predicate_on_default_string(bool async) + { + await base.Json_predicate_on_default_string(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 JSON_VALUE([j].[Reference], '$.TestDefaultString') <> N'MyDefaultStringInReference1' OR JSON_VALUE([j].[Reference], '$.TestDefaultString') IS NULL +"""); + } + + public override async Task Json_predicate_on_max_length_string(bool async) + { + await base.Json_predicate_on_max_length_string(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 JSON_VALUE([j].[Reference], '$.TestMaxLengthString') <> N'Foo' OR JSON_VALUE([j].[Reference], '$.TestMaxLengthString') IS NULL +"""); + } + + public override async Task Json_predicate_on_string_condition(bool async) + { + await base.Json_predicate_on_string_condition(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 CASE + WHEN CAST(JSON_VALUE([j].[Reference], '$.TestBoolean') AS bit) = CAST(0 AS bit) THEN JSON_VALUE([j].[Reference], '$.TestMaxLengthString') + ELSE JSON_VALUE([j].[Reference], '$.TestDefaultString') +END = N'MyDefaultStringInReference1' +"""); + } + + public override async Task Json_predicate_on_byte(bool async) + { + await base.Json_predicate_on_byte(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], '$.TestByte') AS tinyint) <> CAST(3 AS tinyint) OR CAST(JSON_VALUE([j].[Reference], '$.TestByte') AS tinyint) IS NULL +"""); + } + + public override async Task Json_predicate_on_byte_array(bool async) + { + // TODO:SQLJSON (See JsonTypeToFunction.cs) + Assert.Equal( + "OpenJson support not yet supported for JSON native data type.", + (await Assert.ThrowsAsync( + () => base.Json_predicate_on_byte_array(async))).Message); + + 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] +OUTER APPLY OPENJSON([j].[Reference]) WITH ([TestByteArray] varbinary(max) '$.TestByteArray') AS [t] +WHERE [t].[TestByteArray] <> 0x010203 OR [t].[TestByteArray] IS NULL +"""); + } + + public override async Task Json_predicate_on_character(bool async) + { + await base.Json_predicate_on_character(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 JSON_VALUE([j].[Reference], '$.TestCharacter') <> N'z' OR JSON_VALUE([j].[Reference], '$.TestCharacter') IS NULL +"""); + } + + public override async Task Json_predicate_on_datetime(bool async) + { + await base.Json_predicate_on_datetime(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], '$.TestDateTime') AS datetime2) <> '2000-01-03T00:00:00.0000000' OR CAST(JSON_VALUE([j].[Reference], '$.TestDateTime') AS datetime2) IS NULL +"""); + } + + public override async Task Json_predicate_on_datetimeoffset(bool async) + { + await base.Json_predicate_on_datetimeoffset(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], '$.TestDateTimeOffset') AS datetimeoffset) <> '2000-01-04T00:00:00.0000000+03:02' OR CAST(JSON_VALUE([j].[Reference], '$.TestDateTimeOffset') AS datetimeoffset) IS NULL +"""); + } + + public override async Task Json_predicate_on_decimal(bool async) + { + await base.Json_predicate_on_decimal(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], '$.TestDecimal') AS decimal(18,3)) <> 1.35 OR CAST(JSON_VALUE([j].[Reference], '$.TestDecimal') AS decimal(18,3)) IS NULL +"""); + } + + public override async Task Json_predicate_on_double(bool async) + { + await base.Json_predicate_on_double(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], '$.TestDouble') AS float) <> 33.25E0 OR CAST(JSON_VALUE([j].[Reference], '$.TestDouble') AS float) IS NULL +"""); + } + + public override async Task Json_predicate_on_enum(bool async) + { + await base.Json_predicate_on_enum(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], '$.TestEnum') AS int) <> 2 OR CAST(JSON_VALUE([j].[Reference], '$.TestEnum') AS int) IS NULL +"""); + } + + public override async Task Json_predicate_on_enumwithintconverter(bool async) + { + await base.Json_predicate_on_enumwithintconverter(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], '$.TestEnumWithIntConverter') AS int) <> -3 OR CAST(JSON_VALUE([j].[Reference], '$.TestEnumWithIntConverter') AS int) IS NULL +"""); + } + + public override async Task Json_predicate_on_guid(bool async) + { + await base.Json_predicate_on_guid(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], '$.TestGuid') AS uniqueidentifier) <> '00000000-0000-0000-0000-000000000000' OR CAST(JSON_VALUE([j].[Reference], '$.TestGuid') AS uniqueidentifier) IS NULL +"""); + } + + public override async Task Json_predicate_on_int16(bool async) + { + await base.Json_predicate_on_int16(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], '$.TestInt16') AS smallint) <> CAST(3 AS smallint) OR CAST(JSON_VALUE([j].[Reference], '$.TestInt16') AS smallint) IS NULL +"""); + } + + public override async Task Json_predicate_on_int32(bool async) + { + await base.Json_predicate_on_int32(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], '$.TestInt32') AS int) <> 33 OR CAST(JSON_VALUE([j].[Reference], '$.TestInt32') AS int) IS NULL +"""); + } + + public override async Task Json_predicate_on_int64(bool async) + { + await base.Json_predicate_on_int64(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], '$.TestInt64') AS bigint) <> CAST(333 AS bigint) OR CAST(JSON_VALUE([j].[Reference], '$.TestInt64') AS bigint) IS NULL +"""); + } + + public override async Task Json_predicate_on_nullableenum1(bool async) + { + await base.Json_predicate_on_nullableenum1(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], '$.TestNullableEnum') AS int) <> -1 OR CAST(JSON_VALUE([j].[Reference], '$.TestNullableEnum') AS int) IS NULL +"""); + } + + public override async Task Json_predicate_on_nullableenum2(bool async) + { + await base.Json_predicate_on_nullableenum2(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], '$.TestNullableEnum') AS int) IS NOT NULL +"""); + } + + public override async Task Json_predicate_on_nullableenumwithconverter1(bool async) + { + await base.Json_predicate_on_nullableenumwithconverter1(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], '$.TestNullableEnumWithIntConverter') AS int) <> 2 OR CAST(JSON_VALUE([j].[Reference], '$.TestNullableEnumWithIntConverter') AS int) IS NULL +"""); + } + + public override async Task Json_predicate_on_nullableenumwithconverter2(bool async) + { + await base.Json_predicate_on_nullableenumwithconverter2(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], '$.TestNullableEnumWithIntConverter') AS int) IS NOT NULL +"""); + } + + public override async Task Json_predicate_on_nullableenumwithconverterthathandlesnulls1(bool async) + { + await base.Json_predicate_on_nullableenumwithconverterthathandlesnulls1(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 JSON_VALUE([j].[Reference], '$.TestNullableEnumWithConverterThatHandlesNulls') <> N'One' OR JSON_VALUE([j].[Reference], '$.TestNullableEnumWithConverterThatHandlesNulls') IS NULL +"""); + } + + public override async Task Json_predicate_on_nullableenumwithconverterthathandlesnulls2(bool async) + { + await base.Json_predicate_on_nullableenumwithconverterthathandlesnulls2(async); + + AssertSql( + """ +x +"""); + } + + public override async Task Json_predicate_on_nullableint321(bool async) + { + await base.Json_predicate_on_nullableint321(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], '$.TestNullableInt32') AS int) <> 100 OR CAST(JSON_VALUE([j].[Reference], '$.TestNullableInt32') AS int) IS NULL +"""); + } + + public override async Task Json_predicate_on_nullableint322(bool async) + { + await base.Json_predicate_on_nullableint322(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], '$.TestNullableInt32') AS int) IS NOT NULL +"""); + } + + public override async Task Json_predicate_on_signedbyte(bool async) + { + await base.Json_predicate_on_signedbyte(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], '$.TestSignedByte') AS smallint) <> CAST(100 AS smallint) OR CAST(JSON_VALUE([j].[Reference], '$.TestSignedByte') AS smallint) IS NULL +"""); + } + + public override async Task Json_predicate_on_single(bool async) + { + await base.Json_predicate_on_single(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], '$.TestSingle') AS real) <> CAST(10.4 AS real) OR CAST(JSON_VALUE([j].[Reference], '$.TestSingle') AS real) IS NULL +"""); + } + + public override async Task Json_predicate_on_timespan(bool async) + { + await base.Json_predicate_on_timespan(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], '$.TestTimeSpan') AS time) <> '03:02:00' OR CAST(JSON_VALUE([j].[Reference], '$.TestTimeSpan') AS time) IS NULL +"""); + } + + 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); + + 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], '$.TestUnsignedInt16') AS int) <> 100 OR CAST(JSON_VALUE([j].[Reference], '$.TestUnsignedInt16') AS int) IS NULL +"""); + } + + public override async Task Json_predicate_on_unsignedint32(bool async) + { + await base.Json_predicate_on_unsignedint32(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], '$.TestUnsignedInt32') AS bigint) <> CAST(1000 AS bigint) OR CAST(JSON_VALUE([j].[Reference], '$.TestUnsignedInt32') AS bigint) IS NULL +"""); + } + + public override async Task Json_predicate_on_unsignedint64(bool async) + { + await base.Json_predicate_on_unsignedint64(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], '$.TestUnsignedInt64') AS decimal(20,0)) <> 10000.0 OR CAST(JSON_VALUE([j].[Reference], '$.TestUnsignedInt64') AS decimal(20,0)) IS NULL +"""); + } + + public override async Task Json_predicate_on_bool_converted_to_int_zero_one(bool async) + { + await base.Json_predicate_on_bool_converted_to_int_zero_one(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[Reference] +FROM [JsonEntitiesConverters] AS [j] +WHERE CAST(JSON_VALUE([j].[Reference], '$.BoolConvertedToIntZeroOne') AS int) = 1 +"""); + } + + public override async Task Json_predicate_on_bool_converted_to_int_zero_one_with_explicit_comparison(bool async) + { + await base.Json_predicate_on_bool_converted_to_int_zero_one_with_explicit_comparison(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[Reference] +FROM [JsonEntitiesConverters] AS [j] +WHERE CAST(JSON_VALUE([j].[Reference], '$.BoolConvertedToIntZeroOne') AS int) = 0 +"""); + } + + public override async Task Json_predicate_on_bool_converted_to_string_True_False(bool async) + { + await base.Json_predicate_on_bool_converted_to_string_True_False(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[Reference] +FROM [JsonEntitiesConverters] AS [j] +WHERE JSON_VALUE([j].[Reference], '$.BoolConvertedToStringTrueFalse') = N'True' +"""); + } + + public override async Task Json_predicate_on_bool_converted_to_string_True_False_with_explicit_comparison(bool async) + { + await base.Json_predicate_on_bool_converted_to_string_True_False_with_explicit_comparison(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[Reference] +FROM [JsonEntitiesConverters] AS [j] +WHERE JSON_VALUE([j].[Reference], '$.BoolConvertedToStringTrueFalse') = N'True' +"""); + } + + public override async Task Json_predicate_on_bool_converted_to_string_Y_N(bool async) + { + await base.Json_predicate_on_bool_converted_to_string_Y_N(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[Reference] +FROM [JsonEntitiesConverters] AS [j] +WHERE JSON_VALUE([j].[Reference], '$.BoolConvertedToStringYN') = N'Y' +"""); + } + + public override async Task Json_predicate_on_bool_converted_to_string_Y_N_with_explicit_comparison(bool async) + { + await base.Json_predicate_on_bool_converted_to_string_Y_N_with_explicit_comparison(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[Reference] +FROM [JsonEntitiesConverters] AS [j] +WHERE JSON_VALUE([j].[Reference], '$.BoolConvertedToStringYN') = N'N' +"""); + } + + public override async Task Json_predicate_on_int_zero_one_converted_to_bool(bool async) + { + await base.Json_predicate_on_int_zero_one_converted_to_bool(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[Reference] +FROM [JsonEntitiesConverters] AS [j] +WHERE CAST(JSON_VALUE([j].[Reference], '$.IntZeroOneConvertedToBool') AS bit) = CAST(1 AS bit) +"""); + } + + public override async Task Json_predicate_on_string_True_False_converted_to_bool(bool async) + { + await base.Json_predicate_on_string_True_False_converted_to_bool(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[Reference] +FROM [JsonEntitiesConverters] AS [j] +WHERE CAST(JSON_VALUE([j].[Reference], '$.StringTrueFalseConvertedToBool') AS bit) = CAST(0 AS bit) +"""); + } + + public override async Task Json_predicate_on_string_Y_N_converted_to_bool(bool async) + { + await base.Json_predicate_on_string_Y_N_converted_to_bool(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[Reference] +FROM [JsonEntitiesConverters] AS [j] +WHERE CAST(JSON_VALUE([j].[Reference], '$.StringYNConvertedToBool') AS bit) = CAST(0 AS bit) +"""); + } + + public override async Task FromSql_on_entity_with_json_basic(bool async) + { + await base.FromSql_on_entity_with_json_basic(async); + + AssertSql( + """ +SELECT [m].[Id], [m].[EntityBasicId], [m].[Name], [m].[OwnedCollectionRoot], [m].[OwnedReferenceRoot] +FROM ( + SELECT * FROM "JsonEntitiesBasic" AS j +) AS [m] +"""); + } + + public virtual async Task FromSqlInterpolated_on_entity_with_json_with_predicate(bool async) + { + var parameter = new SqlParameter { ParameterName = "prm", Value = 1 }; + await AssertQuery( + async, + ss => ((DbSet)ss.Set()).FromSql( + Fixture.TestStore.NormalizeDelimitersInInterpolatedString( + $"SELECT * FROM [JsonEntitiesBasic] AS j WHERE [j].[Id] = {parameter}")), + ss => ss.Set()); + + AssertSql( + """ +prm='1' + +SELECT [m].[Id], [m].[EntityBasicId], [m].[Name], [m].[OwnedCollectionRoot], [m].[OwnedReferenceRoot] +FROM ( + SELECT * FROM "JsonEntitiesBasic" AS j WHERE "j"."Id" = @prm +) AS [m] +"""); + } + + public override async Task FromSql_on_entity_with_json_project_json_reference(bool async) + { + await base.FromSql_on_entity_with_json_project_json_reference(async); + + AssertSql( + """ +SELECT JSON_QUERY([m].[OwnedReferenceRoot], '$.OwnedReferenceBranch'), [m].[Id] +FROM ( + SELECT * FROM "JsonEntitiesBasic" AS j +) AS [m] +"""); + } + + public override async Task FromSql_on_entity_with_json_project_json_collection(bool async) + { + await base.FromSql_on_entity_with_json_project_json_collection(async); + + AssertSql( + """ +SELECT JSON_QUERY([m].[OwnedReferenceRoot], '$.OwnedCollectionBranch'), [m].[Id] +FROM ( + SELECT * FROM "JsonEntitiesBasic" AS j +) AS [m] +"""); + } + + public override async Task FromSql_on_entity_with_json_inheritance_on_base(bool async) + { + // TODO:SQLJSON Returns empty (invalid) JSON (See BadJson.cs) + if (async) + { + Assert.Equal( + "Unable to cast object of type 'System.DBNull' to type 'System.String'.", + (await Assert.ThrowsAsync(() => base.FromSql_on_entity_with_json_inheritance_on_base(true))).Message); + } + else + { + Assert.Equal( + RelationalStrings.JsonEmptyString, + (await Assert.ThrowsAsync(() => base.FromSql_on_entity_with_json_inheritance_on_base(false))) + .Message); + } + + AssertSql( + """ +SELECT [m].[Id], [m].[Discriminator], [m].[Name], [m].[Fraction], [m].[CollectionOnBase], [m].[ReferenceOnBase], [m].[CollectionOnDerived], [m].[ReferenceOnDerived] +FROM ( + SELECT * FROM "JsonEntitiesInheritance" AS j +) AS [m] +"""); + } + + public override async Task FromSql_on_entity_with_json_inheritance_on_derived(bool async) + { + await base.FromSql_on_entity_with_json_inheritance_on_derived(async); + + AssertSql( + """ +SELECT [m].[Id], [m].[Discriminator], [m].[Name], [m].[Fraction], [m].[CollectionOnBase], [m].[ReferenceOnBase], [m].[CollectionOnDerived], [m].[ReferenceOnDerived] +FROM ( + SELECT * FROM "JsonEntitiesInheritance" AS j +) AS [m] +WHERE [m].[Discriminator] = N'JsonEntityInheritanceDerived' +"""); + } + + public override async Task FromSql_on_entity_with_json_inheritance_project_reference_on_base(bool async) + { + await base.FromSql_on_entity_with_json_inheritance_project_reference_on_base(async); + + AssertSql( + """ +SELECT [m].[ReferenceOnBase], [m].[Id] +FROM ( + SELECT * FROM "JsonEntitiesInheritance" AS j +) AS [m] +ORDER BY [m].[Id] +"""); + } + + public override async Task FromSql_on_entity_with_json_inheritance_project_reference_on_derived(bool async) + { + await base.FromSql_on_entity_with_json_inheritance_project_reference_on_derived(async); + + AssertSql( + """ +SELECT [m].[CollectionOnDerived], [m].[Id] +FROM ( + SELECT * FROM "JsonEntitiesInheritance" AS j +) AS [m] +WHERE [m].[Discriminator] = N'JsonEntityInheritanceDerived' +ORDER BY [m].[Id] +"""); + } + + + public override async Task Json_projection_nothing_interesting_AsNoTrackingWithIdentityResolution(bool async) + { + await base.Json_projection_nothing_interesting_AsNoTrackingWithIdentityResolution(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[Name] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Json_projection_owner_entity_AsNoTrackingWithIdentityResolution(bool async) + { + await base.Json_projection_owner_entity_AsNoTrackingWithIdentityResolution(async); + + AssertSql( + """ +SELECT [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Json_nested_collection_anonymous_projection_of_primitives_in_projection_NoTrackingWithIdentityResolution(bool async) + { + await base.Json_nested_collection_anonymous_projection_of_primitives_in_projection_NoTrackingWithIdentityResolution(async); + + AssertSql( + """ +SELECT [j].[Id], [s].[key], [s].[c], [s].[c0], [s].[c1], [s].[c2], [s].[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], [o0].[key] AS [key0], CAST([o].[key] AS int) AS [c3], CAST([o0].[key] AS int) AS [c4] + FROM OPENJSON(CAST([j].[OwnedCollectionRoot] AS nvarchar(max)), '$') AS [o] + OUTER APPLY OPENJSON(CAST(JSON_QUERY([o].[value], '$.OwnedCollectionBranch') AS nvarchar(max)), '$') AS [o0] +) AS [s] +ORDER BY [j].[Id], [s].[c3], [s].[key], [s].[c4] +"""); + } + + public override async Task Json_projection_second_element_through_collection_element_constant_projected_after_owner_nested_AsNoTrackingWithIdentityResolution(bool async) + { + await base.Json_projection_second_element_through_collection_element_constant_projected_after_owner_nested_AsNoTrackingWithIdentityResolution(async); + + AssertSql( + """ +SELECT [j].[Id], JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedCollectionBranch[0].OwnedCollectionLeaf'), JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedCollectionBranch[0].OwnedCollectionLeaf[1]') +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Json_projection_reference_collection_and_collection_element_nested_AsNoTrackingWithIdentityResolution(bool async) + { + await base.Json_projection_reference_collection_and_collection_element_nested_AsNoTrackingWithIdentityResolution(async); + + AssertSql( + """ +SELECT [j].[Id], JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedCollectionBranch[0].OwnedReferenceLeaf'), JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedCollectionBranch[0].OwnedCollectionLeaf'), JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedCollectionBranch[0].OwnedCollectionLeaf[1]') +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + [SqlServerCondition(SqlServerCondition.SupportsJsonPathExpressions)] + public override async Task Json_projection_second_element_through_collection_element_parameter_correctly_projected_after_owner_nested_AsNoTrackingWithIdentityResolution(bool async) + { + await base.Json_projection_second_element_through_collection_element_parameter_correctly_projected_after_owner_nested_AsNoTrackingWithIdentityResolution(async); + + AssertSql( + """ +@__prm_0='1' + +SELECT [j].[Id], JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedCollectionBranch[0].OwnedCollectionLeaf'), JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedCollectionBranch[0].OwnedCollectionLeaf[' + CAST(@__prm_0 AS nvarchar(max)) + ']'), @__prm_0 +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Json_projection_only_second_element_through_collection_element_constant_projected_nested_AsNoTrackingWithIdentityResolution(bool async) + { + await base.Json_projection_only_second_element_through_collection_element_constant_projected_nested_AsNoTrackingWithIdentityResolution(async); + + AssertSql( + """ +SELECT [j].[Id], JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedCollectionBranch[0].OwnedCollectionLeaf[1]') +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + [SqlServerCondition(SqlServerCondition.SupportsJsonPathExpressions)] + public override async Task Json_projection_only_second_element_through_collection_element_parameter_projected_nested_AsNoTrackingWithIdentityResolution(bool async) + { + await base.Json_projection_only_second_element_through_collection_element_parameter_projected_nested_AsNoTrackingWithIdentityResolution(async); + + AssertSql( + """ +@__prm1_0='0' +@__prm2_1='1' + +SELECT [j].[Id], JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedCollectionBranch[' + CAST(@__prm1_0 AS nvarchar(max)) + '].OwnedCollectionLeaf[' + CAST(@__prm2_1 AS nvarchar(max)) + ']'), @__prm1_0, @__prm2_1 +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Json_projection_second_element_through_collection_element_constant_different_values_projected_before_owner_nested_AsNoTrackingWithIdentityResolution(bool async) + { + await base.Json_projection_second_element_through_collection_element_constant_different_values_projected_before_owner_nested_AsNoTrackingWithIdentityResolution(async); + + AssertSql( + """ +SELECT [j].[Id], JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedCollectionBranch[0].OwnedCollectionLeaf[1]'), JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedCollectionBranch[1].OwnedCollectionLeaf') +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Json_projection_nested_collection_and_element_correct_order_AsNoTrackingWithIdentityResolution(bool async) + { + await base.Json_projection_nested_collection_and_element_correct_order_AsNoTrackingWithIdentityResolution(async); + + AssertSql( + """ +SELECT [j].[Id], JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedCollectionBranch[0].OwnedCollectionLeaf'), JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedCollectionBranch[0].OwnedCollectionLeaf[1]') +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + [SqlServerCondition(SqlServerCondition.SupportsJsonPathExpressions)] + public override async Task Json_projection_nested_collection_element_using_parameter_and_the_owner_in_correct_order_AsNoTrackingWithIdentityResolution(bool async) + { + await base.Json_projection_nested_collection_element_using_parameter_and_the_owner_in_correct_order_AsNoTrackingWithIdentityResolution(async); + + AssertSql( + """ +@__prm_0='0' + +SELECT [j].[Id], [j].[OwnedReferenceRoot], JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedCollectionBranch[' + CAST(@__prm_0 AS nvarchar(max)) + '].OwnedCollectionLeaf[1]'), @__prm_0 +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Json_projection_second_element_projected_before_owner_as_well_as_root_AsNoTrackingWithIdentityResolution(bool async) + { + await base.Json_projection_second_element_projected_before_owner_as_well_as_root_AsNoTrackingWithIdentityResolution(async); + + AssertSql( + """ +SELECT [j].[Id], JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedCollectionBranch[1]'), [j].[OwnedReferenceRoot], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Json_projection_second_element_projected_before_owner_nested_as_well_as_root_AsNoTrackingWithIdentityResolution(bool async) + { + await base.Json_projection_second_element_projected_before_owner_nested_as_well_as_root_AsNoTrackingWithIdentityResolution(async); + + AssertSql( + """ +SELECT [j].[Id], JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch.OwnedCollectionLeaf[1]'), JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch.OwnedCollectionLeaf'), JSON_QUERY([j].[OwnedReferenceRoot], '$.OwnedReferenceBranch'), [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + private void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); +} diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/JsonQuerySqlServerFixture.cs b/test/EFCore.SqlServer.FunctionalTests/Query/JsonQuerySqlServerFixture.cs index 21d51690775..49e5c0660fc 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/JsonQuerySqlServerFixture.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/JsonQuerySqlServerFixture.cs @@ -12,6 +12,9 @@ public class JsonQuerySqlServerFixture : JsonQueryRelationalFixture protected override ITestStoreFactory TestStoreFactory => SqlServerTestStoreFactory.Instance; + public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder) + => base.AddOptions(builder).ConfigureWarnings(e => e.Log(SqlServerEventId.JsonTypeExperimental)); + protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context) { base.OnModelCreating(modelBuilder, context); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerJsonTypeTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerJsonTypeTest.cs new file mode 100644 index 00000000000..d83e60993a8 --- /dev/null +++ b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerJsonTypeTest.cs @@ -0,0 +1,2087 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Data.SqlClient; + +namespace Microsoft.EntityFrameworkCore.Query; + +[SqlServerCondition(SqlServerCondition.SupportsFunctions2022 | SqlServerCondition.SupportsJsonType)] +public class PrimitiveCollectionsQuerySqlServerJsonTypeTest : PrimitiveCollectionsQueryRelationalTestBase< + PrimitiveCollectionsQuerySqlServerJsonTypeTest.PrimitiveCollectionsQuerySqlServerFixture> +{ + public PrimitiveCollectionsQuerySqlServerJsonTypeTest(PrimitiveCollectionsQuerySqlServerFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); + + public override async Task Inline_collection_with_single_parameter_element_Contains(bool async) + { + await base.Inline_collection_with_single_parameter_element_Contains(async); + + AssertSql( + """ +@__i_0='2' + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE [p].[Id] = @__i_0 +"""); + } + + public override async Task Inline_collection_with_single_parameter_element_Count(bool async) + { + await base.Inline_collection_with_single_parameter_element_Count(async); + + AssertSql( + """ +@__i_0='2' + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT COUNT(*) + FROM (VALUES (CAST(@__i_0 AS int))) AS [v]([Value]) + WHERE [v].[Value] > [p].[Id]) = 1 +"""); + } + + public override async Task Parameter_collection_Contains_with_EF_Constant(bool async) + { + await base.Parameter_collection_Contains_with_EF_Constant(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE [p].[Id] IN (2, 999, 1000) +"""); + } + + public override async Task Parameter_collection_Where_with_EF_Constant_Where_Any(bool async) + { + await base.Parameter_collection_Where_with_EF_Constant_Where_Any(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE EXISTS ( + SELECT 1 + FROM (VALUES (2), (999), (1000)) AS [i]([Value]) + WHERE [i].[Value] > 0) +"""); + } + + public override async Task Parameter_collection_Count_with_column_predicate_with_EF_Constant(bool async) + { + await base.Parameter_collection_Count_with_column_predicate_with_EF_Constant(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT COUNT(*) + FROM (VALUES (2), (999), (1000)) AS [i]([Value]) + WHERE [i].[Value] > [p].[Id]) = 2 +"""); + } + + public override async Task Inline_collection_of_ints_Contains(bool async) + { + await base.Inline_collection_of_ints_Contains(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE [p].[Int] IN (10, 999) +"""); + } + + public override async Task Inline_collection_of_nullable_ints_Contains(bool async) + { + await base.Inline_collection_of_nullable_ints_Contains(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE [p].[NullableInt] IN (10, 999) +"""); + } + + public override async Task Inline_collection_of_nullable_ints_Contains_null(bool async) + { + await base.Inline_collection_of_nullable_ints_Contains_null(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE [p].[NullableInt] IS NULL OR [p].[NullableInt] = 999 +"""); + } + + public override async Task Inline_collection_Count_with_zero_values(bool async) + { + await base.Inline_collection_Count_with_zero_values(async); + + AssertSql(); + } + + public override async Task Inline_collection_Count_with_one_value(bool async) + { + await base.Inline_collection_Count_with_one_value(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT COUNT(*) + FROM (VALUES (CAST(2 AS int))) AS [v]([Value]) + WHERE [v].[Value] > [p].[Id]) = 1 +"""); + } + + public override async Task Inline_collection_Count_with_two_values(bool async) + { + await base.Inline_collection_Count_with_two_values(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT COUNT(*) + FROM (VALUES (CAST(2 AS int)), (999)) AS [v]([Value]) + WHERE [v].[Value] > [p].[Id]) = 1 +"""); + } + + public override async Task Inline_collection_Count_with_three_values(bool async) + { + await base.Inline_collection_Count_with_three_values(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT COUNT(*) + FROM (VALUES (CAST(2 AS int)), (999), (1000)) AS [v]([Value]) + WHERE [v].[Value] > [p].[Id]) = 2 +"""); + } + + public override async Task Inline_collection_Contains_with_zero_values(bool async) + { + await base.Inline_collection_Contains_with_zero_values(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE 0 = 1 +"""); + } + + public override async Task Inline_collection_Contains_with_one_value(bool async) + { + await base.Inline_collection_Contains_with_one_value(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE [p].[Id] = 2 +"""); + } + + public override async Task Inline_collection_Contains_with_two_values(bool async) + { + await base.Inline_collection_Contains_with_two_values(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE [p].[Id] IN (2, 999) +"""); + } + + public override async Task Inline_collection_Contains_with_three_values(bool async) + { + await base.Inline_collection_Contains_with_three_values(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE [p].[Id] IN (2, 999, 1000) +"""); + } + + public override async Task Inline_collection_Contains_with_all_parameters(bool async) + { + await base.Inline_collection_Contains_with_all_parameters(async); + + AssertSql( + """ +@__i_0='2' +@__j_1='999' + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE [p].[Id] IN (@__i_0, @__j_1) +"""); + } + + public override async Task Inline_collection_Contains_with_constant_and_parameter(bool async) + { + await base.Inline_collection_Contains_with_constant_and_parameter(async); + + AssertSql( + """ +@__j_0='999' + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE [p].[Id] IN (2, @__j_0) +"""); + } + + public override async Task Inline_collection_Contains_with_mixed_value_types(bool async) + { + await base.Inline_collection_Contains_with_mixed_value_types(async); + + AssertSql( + """ +@__i_0='11' + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE [p].[Int] IN (999, @__i_0, [p].[Id], [p].[Id] + [p].[Int]) +"""); + } + + public override async Task Inline_collection_List_Contains_with_mixed_value_types(bool async) + { + await base.Inline_collection_List_Contains_with_mixed_value_types(async); + + AssertSql( + """ +@__i_0='11' + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE [p].[Int] IN (999, @__i_0, [p].[Id], [p].[Id] + [p].[Int]) +"""); + } + + public override async Task Inline_collection_Contains_as_Any_with_predicate(bool async) + { + await base.Inline_collection_Contains_as_Any_with_predicate(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE [p].[Id] IN (2, 999) +"""); + } + + public override async Task Inline_collection_negated_Contains_as_All(bool async) + { + await base.Inline_collection_negated_Contains_as_All(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE [p].[Id] NOT IN (2, 999) +"""); + } + + public override async Task Inline_collection_Min_with_two_values(bool async) + { + await base.Inline_collection_Min_with_two_values(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE LEAST(30, [p].[Int]) = 30 +"""); + } + + public override async Task Inline_collection_List_Min_with_two_values(bool async) + { + await base.Inline_collection_List_Min_with_two_values(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE LEAST(30, [p].[Int]) = 30 +"""); + } + + public override async Task Inline_collection_Max_with_two_values(bool async) + { + await base.Inline_collection_Max_with_two_values(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE GREATEST(30, [p].[Int]) = 30 +"""); + } + + public override async Task Inline_collection_List_Max_with_two_values(bool async) + { + await base.Inline_collection_List_Max_with_two_values(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE GREATEST(30, [p].[Int]) = 30 +"""); + } + + public override async Task Inline_collection_Min_with_three_values(bool async) + { + await base.Inline_collection_Min_with_three_values(async); + + AssertSql( + """ +@__i_0='25' + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE LEAST(30, [p].[Int], @__i_0) = 25 +"""); + } + + public override async Task Inline_collection_List_Min_with_three_values(bool async) + { + await base.Inline_collection_List_Min_with_three_values(async); + + AssertSql( + """ +@__i_0='25' + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE LEAST(30, [p].[Int], @__i_0) = 25 +"""); + } + + public override async Task Inline_collection_Max_with_three_values(bool async) + { + await base.Inline_collection_Max_with_three_values(async); + + AssertSql( + """ +@__i_0='35' + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE GREATEST(30, [p].[Int], @__i_0) = 35 +"""); + } + + public override async Task Inline_collection_List_Max_with_three_values(bool async) + { + await base.Inline_collection_List_Max_with_three_values(async); + + AssertSql( + """ +@__i_0='35' + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE GREATEST(30, [p].[Int], @__i_0) = 35 +"""); + } + + public override async Task Inline_collection_of_nullable_value_type_Min(bool async) + { + await base.Inline_collection_of_nullable_value_type_Min(async); + + AssertSql( + """ +@__i_0='25' (Nullable = true) + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE LEAST(30, [p].[Int], @__i_0) = 25 +"""); + } + + public override async Task Inline_collection_of_nullable_value_type_Max(bool async) + { + await base.Inline_collection_of_nullable_value_type_Max(async); + + AssertSql( + """ +@__i_0='35' (Nullable = true) + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE GREATEST(30, [p].[Int], @__i_0) = 35 +"""); + } + + public override async Task Inline_collection_of_nullable_value_type_with_null_Min(bool async) + { + await base.Inline_collection_of_nullable_value_type_with_null_Min(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE LEAST(30, [p].[NullableInt], NULL) = 30 +"""); + } + + public override async Task Inline_collection_of_nullable_value_type_with_null_Max(bool async) + { + await base.Inline_collection_of_nullable_value_type_with_null_Max(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE GREATEST(30, [p].[NullableInt], NULL) = 30 +"""); + } + + public override async Task Parameter_collection_Count(bool async) + { + await base.Parameter_collection_Count(async); + + AssertSql( + """ +@__ids_0='[2,999]' (Size = 4000) + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT COUNT(*) + FROM OPENJSON(@__ids_0) WITH ([value] int '$') AS [i] + WHERE [i].[value] > [p].[Id]) = 1 +"""); + } + + public override async Task Parameter_collection_of_ints_Contains_int(bool async) + { + await base.Parameter_collection_of_ints_Contains_int(async); + + AssertSql( + """ +@__ints_0='[10,999]' (Size = 4000) + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE [p].[Int] IN ( + SELECT [i].[value] + FROM OPENJSON(@__ints_0) WITH ([value] int '$') AS [i] +) +""", + // + """ +@__ints_0='[10,999]' (Size = 4000) + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE [p].[Int] NOT IN ( + SELECT [i].[value] + FROM OPENJSON(@__ints_0) WITH ([value] int '$') AS [i] +) +"""); + } + + public override async Task Parameter_collection_HashSet_of_ints_Contains_int(bool async) + { + await base.Parameter_collection_HashSet_of_ints_Contains_int(async); + + AssertSql( + """ +@__ints_0='[10,999]' (Size = 4000) + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE [p].[Int] IN ( + SELECT [i].[value] + FROM OPENJSON(@__ints_0) WITH ([value] int '$') AS [i] +) +""", + // + """ +@__ints_0='[10,999]' (Size = 4000) + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE [p].[Int] NOT IN ( + SELECT [i].[value] + FROM OPENJSON(@__ints_0) WITH ([value] int '$') AS [i] +) +"""); + } + + public override async Task Parameter_collection_of_ints_Contains_nullable_int(bool async) + { + await base.Parameter_collection_of_ints_Contains_nullable_int(async); + + AssertSql( + """ +@__ints_0='[10,999]' (Size = 4000) + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE [p].[NullableInt] IN ( + SELECT [i].[value] + FROM OPENJSON(@__ints_0) WITH ([value] int '$') AS [i] +) +""", + // + """ +@__ints_0='[10,999]' (Size = 4000) + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE [p].[NullableInt] NOT IN ( + SELECT [i].[value] + FROM OPENJSON(@__ints_0) WITH ([value] int '$') AS [i] +) OR [p].[NullableInt] IS NULL +"""); + } + + public override async Task Parameter_collection_of_nullable_ints_Contains_int(bool async) + { + await base.Parameter_collection_of_nullable_ints_Contains_int(async); + + AssertSql( + """ +@__nullableInts_0='[10,999]' (Size = 4000) + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE [p].[Int] IN ( + SELECT [n].[value] + FROM OPENJSON(@__nullableInts_0) WITH ([value] int '$') AS [n] +) +""", + // + """ +@__nullableInts_0='[10,999]' (Size = 4000) + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE [p].[Int] NOT IN ( + SELECT [n].[value] + FROM OPENJSON(@__nullableInts_0) WITH ([value] int '$') AS [n] +) +"""); + } + + public override async Task Parameter_collection_of_nullable_ints_Contains_nullable_int(bool async) + { + await base.Parameter_collection_of_nullable_ints_Contains_nullable_int(async); + + AssertSql( + """ +@__nullableInts_0_without_nulls='[999]' (Size = 4000) + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE [p].[NullableInt] IN ( + SELECT [n].[value] + FROM OPENJSON(@__nullableInts_0_without_nulls) AS [n] +) OR [p].[NullableInt] IS NULL +""", + // + """ +@__nullableInts_0_without_nulls='[999]' (Size = 4000) + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE [p].[NullableInt] NOT IN ( + SELECT [n].[value] + FROM OPENJSON(@__nullableInts_0_without_nulls) AS [n] +) AND [p].[NullableInt] IS NOT NULL +"""); + } + + public override async Task Parameter_collection_of_strings_Contains_string(bool async) + { + await base.Parameter_collection_of_strings_Contains_string(async); + + AssertSql( + """ +@__strings_0='["10","999"]' (Size = 4000) + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE [p].[String] IN ( + SELECT [s].[value] + FROM OPENJSON(@__strings_0) WITH ([value] nvarchar(max) '$') AS [s] +) +""", + // + """ +@__strings_0='["10","999"]' (Size = 4000) + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE [p].[String] NOT IN ( + SELECT [s].[value] + FROM OPENJSON(@__strings_0) WITH ([value] nvarchar(max) '$') AS [s] +) +"""); + } + + public override async Task Parameter_collection_of_strings_Contains_nullable_string(bool async) + { + await base.Parameter_collection_of_strings_Contains_nullable_string(async); + + AssertSql( + """ +@__strings_0='["10","999"]' (Size = 4000) + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE [p].[NullableString] IN ( + SELECT [s].[value] + FROM OPENJSON(@__strings_0) WITH ([value] nvarchar(max) '$') AS [s] +) +""", + // + """ +@__strings_0='["10","999"]' (Size = 4000) + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE [p].[NullableString] NOT IN ( + SELECT [s].[value] + FROM OPENJSON(@__strings_0) WITH ([value] nvarchar(max) '$') AS [s] +) OR [p].[NullableString] IS NULL +"""); + } + + public override async Task Parameter_collection_of_nullable_strings_Contains_string(bool async) + { + await base.Parameter_collection_of_nullable_strings_Contains_string(async); + + AssertSql( + """ +@__strings_0='["10",null]' (Size = 4000) + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE [p].[String] IN ( + SELECT [s].[value] + FROM OPENJSON(@__strings_0) WITH ([value] nvarchar(max) '$') AS [s] +) +""", + // + """ +@__strings_0_without_nulls='["10"]' (Size = 4000) + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE [p].[String] NOT IN ( + SELECT [s].[value] + FROM OPENJSON(@__strings_0_without_nulls) AS [s] +) +"""); + } + + public override async Task Parameter_collection_of_nullable_strings_Contains_nullable_string(bool async) + { + await base.Parameter_collection_of_nullable_strings_Contains_nullable_string(async); + + AssertSql( + """ +@__strings_0_without_nulls='["999"]' (Size = 4000) + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE [p].[NullableString] IN ( + SELECT [s].[value] + FROM OPENJSON(@__strings_0_without_nulls) AS [s] +) OR [p].[NullableString] IS NULL +""", + // + """ +@__strings_0_without_nulls='["999"]' (Size = 4000) + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE [p].[NullableString] NOT IN ( + SELECT [s].[value] + FROM OPENJSON(@__strings_0_without_nulls) AS [s] +) AND [p].[NullableString] IS NOT NULL +"""); + } + + public override async Task Parameter_collection_of_DateTimes_Contains(bool async) + { + await base.Parameter_collection_of_DateTimes_Contains(async); + + AssertSql( + """ +@__dateTimes_0='["2020-01-10T12:30:00Z","9999-01-01T00:00:00Z"]' (Size = 4000) + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE [p].[DateTime] IN ( + SELECT [d].[value] + FROM OPENJSON(@__dateTimes_0) WITH ([value] datetime '$') AS [d] +) +"""); + } + + public override async Task Parameter_collection_of_bools_Contains(bool async) + { + await base.Parameter_collection_of_bools_Contains(async); + + AssertSql( + """ +@__bools_0='[true]' (Size = 4000) + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE [p].[Bool] IN ( + SELECT [b].[value] + FROM OPENJSON(@__bools_0) WITH ([value] bit '$') AS [b] +) +"""); + } + + public override async Task Parameter_collection_of_enums_Contains(bool async) + { + await base.Parameter_collection_of_enums_Contains(async); + + AssertSql( + """ +@__enums_0='[0,3]' (Size = 4000) + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE [p].[Enum] IN ( + SELECT [e].[value] + FROM OPENJSON(@__enums_0) WITH ([value] int '$') AS [e] +) +"""); + } + + public override async Task Parameter_collection_null_Contains(bool async) + { + await base.Parameter_collection_null_Contains(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE [p].[Int] IN ( + SELECT [i].[value] + FROM OPENJSON(NULL) AS [i] +) +"""); + } + + public override async Task Column_collection_of_ints_Contains(bool async) + { + await base.Column_collection_of_ints_Contains(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE 10 IN ( + SELECT [i].[value] + FROM OPENJSON(CAST([p].[Ints] AS nvarchar(max))) WITH ([value] int '$') AS [i] +) +"""); + } + + public override async Task Column_collection_of_nullable_ints_Contains(bool async) + { + await base.Column_collection_of_nullable_ints_Contains(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE 10 IN ( + SELECT [n].[value] + FROM OPENJSON(CAST([p].[NullableInts] AS nvarchar(max))) WITH ([value] int '$') AS [n] +) +"""); + } + + public override async Task Column_collection_of_nullable_ints_Contains_null(bool async) + { + await base.Column_collection_of_nullable_ints_Contains_null(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE EXISTS ( + SELECT 1 + FROM OPENJSON(CAST([p].[NullableInts] AS nvarchar(max))) WITH ([value] int '$') AS [n] + WHERE [n].[value] IS NULL) +"""); + } + + public override async Task Column_collection_of_strings_contains_null(bool async) + { + await base.Column_collection_of_strings_contains_null(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE 0 = 1 +"""); + } + + public override async Task Column_collection_of_nullable_strings_contains_null(bool async) + { + await base.Column_collection_of_nullable_strings_contains_null(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE EXISTS ( + SELECT 1 + FROM OPENJSON(CAST([p].[NullableStrings] AS nvarchar(max))) WITH ([value] nvarchar(max) '$') AS [n] + WHERE [n].[value] IS NULL) +"""); + } + + public override async Task Column_collection_of_bools_Contains(bool async) + { + await base.Column_collection_of_bools_Contains(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE CAST(1 AS bit) IN ( + SELECT [b].[value] + FROM OPENJSON(CAST([p].[Bools] AS nvarchar(max))) WITH ([value] bit '$') AS [b] +) +"""); + } + + [ConditionalFact] + public virtual async Task Json_representation_of_bool_array() + { + await using var context = CreateContext(); + + Assert.Equal( + "[true,false]", + await context.Database.SqlQuery($"SELECT [Bools] AS [Value] FROM [PrimitiveCollectionsEntity] WHERE [Id] = 1") + .SingleAsync()); + } + + public override async Task Column_collection_Count_method(bool async) + { + await base.Column_collection_Count_method(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT COUNT(*) + FROM OPENJSON(CAST([p].[Ints] AS nvarchar(max))) AS [i]) = 2 +"""); + } + + public override async Task Column_collection_Length(bool async) + { + await base.Column_collection_Length(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT COUNT(*) + FROM OPENJSON(CAST([p].[Ints] AS nvarchar(max))) AS [i]) = 2 +"""); + } + + public override async Task Column_collection_Count_with_predicate(bool async) + { + await base.Column_collection_Count_with_predicate(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT COUNT(*) + FROM OPENJSON(CAST([p].[Ints] AS nvarchar(max))) WITH ([value] int '$') AS [i] + WHERE [i].[value] > 1) = 2 +"""); + } + + public override async Task Column_collection_Where_Count(bool async) + { + await base.Column_collection_Where_Count(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT COUNT(*) + FROM OPENJSON(CAST([p].[Ints] AS nvarchar(max))) WITH ([value] int '$') AS [i] + WHERE [i].[value] > 1) = 2 +"""); + } + + public override async Task Column_collection_index_int(bool async) + { + await base.Column_collection_index_int(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE CAST(JSON_VALUE([p].[Ints], '$[1]') AS int) = 10 +"""); + } + + public override async Task Column_collection_index_string(bool async) + { + await base.Column_collection_index_string(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE JSON_VALUE([p].[Strings], '$[1]') = N'10' +"""); + } + + public override async Task Column_collection_index_datetime(bool async) + { + await base.Column_collection_index_datetime(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE CAST(JSON_VALUE([p].[DateTimes], '$[1]') AS datetime2) = '2020-01-10T12:30:00.0000000Z' +"""); + } + + public override async Task Column_collection_index_beyond_end(bool async) + { + await base.Column_collection_index_beyond_end(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE CAST(JSON_VALUE([p].[Ints], '$[999]') AS int) = 10 +"""); + } + + public override async Task Nullable_reference_column_collection_index_equals_nullable_column(bool async) + { + // TODO: This test is incorrect, see #33784 + await base.Nullable_reference_column_collection_index_equals_nullable_column(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE JSON_VALUE([p].[NullableStrings], '$[2]') = [p].[NullableString] OR (JSON_VALUE([p].[NullableStrings], '$[2]') IS NULL AND [p].[NullableString] IS NULL) +"""); + } + + public override async Task Non_nullable_reference_column_collection_index_equals_nullable_column(bool async) + { + await base.Non_nullable_reference_column_collection_index_equals_nullable_column(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE EXISTS ( + SELECT 1 + FROM OPENJSON(CAST([p].[Strings] AS nvarchar(max))) AS [s]) AND JSON_VALUE([p].[Strings], '$[1]') = [p].[NullableString] +"""); + } + + public override async Task Inline_collection_index_Column(bool async) + { + await base.Inline_collection_index_Column(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT [v].[Value] + FROM (VALUES (0, CAST(1 AS int)), (1, 2), (2, 3)) AS [v]([_ord], [Value]) + ORDER BY [v].[_ord] + OFFSET [p].[Int] ROWS FETCH NEXT 1 ROWS ONLY) = 1 +"""); + } + + public override async Task Inline_collection_value_index_Column(bool async) + { + await base.Inline_collection_value_index_Column(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT [v].[Value] + FROM (VALUES (0, CAST(1 AS int)), (1, [p].[Int]), (2, 3)) AS [v]([_ord], [Value]) + ORDER BY [v].[_ord] + OFFSET [p].[Int] ROWS FETCH NEXT 1 ROWS ONLY) = 1 +"""); + } + + public override async Task Inline_collection_List_value_index_Column(bool async) + { + await base.Inline_collection_List_value_index_Column(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT [v].[Value] + FROM (VALUES (0, CAST(1 AS int)), (1, [p].[Int]), (2, 3)) AS [v]([_ord], [Value]) + ORDER BY [v].[_ord] + OFFSET [p].[Int] ROWS FETCH NEXT 1 ROWS ONLY) = 1 +"""); + } + + [SqlServerCondition(SqlServerCondition.SupportsJsonPathExpressions)] + public override async Task Parameter_collection_index_Column_equal_Column(bool async) + { + await base.Parameter_collection_index_Column_equal_Column(async); + + AssertSql( + """ +@__ints_0='[0,2,3]' (Size = 4000) + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE CAST(JSON_VALUE(@__ints_0, '$[' + CAST([p].[Int] AS nvarchar(max)) + ']') AS int) = [p].[Int] +"""); + } + + [SqlServerCondition(SqlServerCondition.SupportsJsonPathExpressions)] + public override async Task Parameter_collection_index_Column_equal_constant(bool async) + { + await base.Parameter_collection_index_Column_equal_constant(async); + + AssertSql( + """ +@__ints_0='[1,2,3]' (Size = 4000) + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE CAST(JSON_VALUE(@__ints_0, '$[' + CAST([p].[Int] AS nvarchar(max)) + ']') AS int) = 1 +"""); + } + + public override async Task Column_collection_ElementAt(bool async) + { + await base.Column_collection_ElementAt(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE CAST(JSON_VALUE([p].[Ints], '$[1]') AS int) = 10 +"""); + } + + public override async Task Column_collection_First(bool async) + { + await base.Column_collection_First(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT TOP(1) CAST([i].[value] AS int) AS [value] + FROM OPENJSON(CAST([p].[Ints] AS nvarchar(max))) AS [i] + ORDER BY CAST([i].[key] AS int)) = 1 +"""); + } + + public override async Task Column_collection_FirstOrDefault(bool async) + { + await base.Column_collection_FirstOrDefault(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE COALESCE(( + SELECT TOP(1) CAST([i].[value] AS int) AS [value] + FROM OPENJSON(CAST([p].[Ints] AS nvarchar(max))) AS [i] + ORDER BY CAST([i].[key] AS int)), 0) = 1 +"""); + } + + public override async Task Column_collection_Single(bool async) + { + await base.Column_collection_Single(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT TOP(1) CAST([i].[value] AS int) AS [value] + FROM OPENJSON(CAST([p].[Ints] AS nvarchar(max))) AS [i] + ORDER BY CAST([i].[key] AS int)) = 1 +"""); + } + + public override async Task Column_collection_SingleOrDefault(bool async) + { + await base.Column_collection_SingleOrDefault(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE COALESCE(( + SELECT TOP(1) CAST([i].[value] AS int) AS [value] + FROM OPENJSON(CAST([p].[Ints] AS nvarchar(max))) AS [i] + ORDER BY CAST([i].[key] AS int)), 0) = 1 +"""); + } + + public override async Task Column_collection_Skip(bool async) + { + await base.Column_collection_Skip(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT COUNT(*) + FROM ( + SELECT 1 AS empty + FROM OPENJSON(CAST([p].[Ints] AS nvarchar(max))) AS [i] + ORDER BY CAST([i].[key] AS int) + OFFSET 1 ROWS + ) AS [i0]) = 2 +"""); + } + + public override async Task Column_collection_Take(bool async) + { + await base.Column_collection_Take(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE 11 IN ( + SELECT TOP(2) CAST([i].[value] AS int) AS [value] + FROM OPENJSON(CAST([p].[Ints] AS nvarchar(max))) AS [i] + ORDER BY CAST([i].[key] AS int) +) +"""); + } + + public override async Task Column_collection_Skip_Take(bool async) + { + await base.Column_collection_Skip_Take(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE 11 IN ( + SELECT CAST([i].[value] AS int) AS [value] + FROM OPENJSON(CAST([p].[Ints] AS nvarchar(max))) AS [i] + ORDER BY CAST([i].[key] AS int) + OFFSET 1 ROWS FETCH NEXT 2 ROWS ONLY +) +"""); + } + + public override async Task Column_collection_Where_Skip(bool async) + { + await base.Column_collection_Where_Skip(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT COUNT(*) + FROM ( + SELECT 1 AS empty + FROM OPENJSON(CAST([p].[Ints] AS nvarchar(max))) AS [i] + WHERE CAST([i].[value] AS int) > 1 + ORDER BY CAST([i].[key] AS int) + OFFSET 1 ROWS + ) AS [i0]) = 3 +"""); + } + + public override async Task Column_collection_Where_Take(bool async) + { + await base.Column_collection_Where_Take(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT COUNT(*) + FROM ( + SELECT TOP(2) 1 AS empty + FROM OPENJSON(CAST([p].[Ints] AS nvarchar(max))) AS [i] + WHERE CAST([i].[value] AS int) > 1 + ORDER BY CAST([i].[key] AS int) + ) AS [i0]) = 2 +"""); + } + + public override async Task Column_collection_Where_Skip_Take(bool async) + { + await base.Column_collection_Where_Skip_Take(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT COUNT(*) + FROM ( + SELECT 1 AS empty + FROM OPENJSON(CAST([p].[Ints] AS nvarchar(max))) AS [i] + WHERE CAST([i].[value] AS int) > 1 + ORDER BY CAST([i].[key] AS int) + OFFSET 1 ROWS FETCH NEXT 2 ROWS ONLY + ) AS [i0]) = 1 +"""); + } + + public override async Task Column_collection_Contains_over_subquery(bool async) + { + await base.Column_collection_Contains_over_subquery(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE 11 IN ( + SELECT [i].[value] + FROM OPENJSON(CAST([p].[Ints] AS nvarchar(max))) WITH ([value] int '$') AS [i] + WHERE [i].[value] > 1 +) +"""); + } + + public override async Task Column_collection_OrderByDescending_ElementAt(bool async) + { + await base.Column_collection_OrderByDescending_ElementAt(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT [i].[value] + FROM OPENJSON(CAST([p].[Ints] AS nvarchar(max))) WITH ([value] int '$') AS [i] + ORDER BY [i].[value] DESC + OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY) = 111 +"""); + } + + public override async Task Column_collection_Where_ElementAt(bool async) + { + await base.Column_collection_Where_ElementAt(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT CAST([i].[value] AS int) AS [value] + FROM OPENJSON(CAST([p].[Ints] AS nvarchar(max))) AS [i] + WHERE CAST([i].[value] AS int) > 1 + ORDER BY CAST([i].[key] AS int) + OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY) = 11 +"""); + } + + public override async Task Column_collection_Any(bool async) + { + await base.Column_collection_Any(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE EXISTS ( + SELECT 1 + FROM OPENJSON(CAST([p].[Ints] AS nvarchar(max))) AS [i]) +"""); + } + + public override async Task Column_collection_Distinct(bool async) + { + await base.Column_collection_Distinct(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT COUNT(*) + FROM ( + SELECT DISTINCT [i].[value] + FROM OPENJSON(CAST([p].[Ints] AS nvarchar(max))) WITH ([value] int '$') AS [i] + ) AS [i0]) = 3 +"""); + } + + public override async Task Column_collection_SelectMany(bool async) + { + await base.Column_collection_SelectMany(async); + + AssertSql( + """ +SELECT [i].[value] +FROM [PrimitiveCollectionsEntity] AS [p] +CROSS APPLY OPENJSON(CAST([p].[Ints] AS nvarchar(max))) WITH ([value] int '$') AS [i] +"""); + } + + public override async Task Column_collection_SelectMany_with_filter(bool async) + { + await base.Column_collection_SelectMany_with_filter(async); + + AssertSql( + """ +SELECT [i0].[value] +FROM [PrimitiveCollectionsEntity] AS [p] +CROSS APPLY ( + SELECT [i].[value] + FROM OPENJSON(CAST([p].[Ints] AS nvarchar(max))) WITH ([value] int '$') AS [i] + WHERE [i].[value] > 1 +) AS [i0] +"""); + } + + public override async Task Column_collection_SelectMany_with_Select_to_anonymous_type(bool async) + { + await base.Column_collection_SelectMany_with_Select_to_anonymous_type(async); + + AssertSql( + """ +SELECT [i].[value] AS [Original], [i].[value] + 1 AS [Incremented] +FROM [PrimitiveCollectionsEntity] AS [p] +CROSS APPLY OPENJSON(CAST([p].[Ints] AS nvarchar(max))) WITH ([value] int '$') AS [i] +"""); + } + + public override async Task Column_collection_projection_from_top_level(bool async) + { + await base.Column_collection_projection_from_top_level(async); + + AssertSql( + """ +SELECT [p].[Ints] +FROM [PrimitiveCollectionsEntity] AS [p] +ORDER BY [p].[Id] +"""); + } + + public override async Task Column_collection_Join_parameter_collection(bool async) + { + await base.Column_collection_Join_parameter_collection(async); + + AssertSql( + """ +@__ints_0='[11,111]' (Size = 4000) + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT COUNT(*) + FROM OPENJSON(CAST([p].[Ints] AS nvarchar(max))) WITH ([value] int '$') AS [i] + INNER JOIN OPENJSON(@__ints_0) WITH ([value] int '$') AS [i0] ON [i].[value] = [i0].[value]) = 2 +"""); + } + + public override async Task Inline_collection_Join_ordered_column_collection(bool async) + { + await base.Inline_collection_Join_ordered_column_collection(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT COUNT(*) + FROM (VALUES (CAST(11 AS int)), (111)) AS [v]([Value]) + INNER JOIN OPENJSON(CAST([p].[Ints] AS nvarchar(max))) WITH ([value] int '$') AS [i] ON [v].[Value] = [i].[value]) = 2 +"""); + } + + public override async Task Parameter_collection_Concat_column_collection(bool async) + { + await base.Parameter_collection_Concat_column_collection(async); + + AssertSql( + """ +@__ints_0='[11,111]' (Size = 4000) + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT COUNT(*) + FROM ( + SELECT 1 AS empty + FROM OPENJSON(@__ints_0) AS [i] + UNION ALL + SELECT 1 AS empty + FROM OPENJSON(CAST([p].[Ints] AS nvarchar(max))) AS [i0] + ) AS [u]) = 2 +"""); + } + + public override async Task Column_collection_Union_parameter_collection(bool async) + { + await base.Column_collection_Union_parameter_collection(async); + + AssertSql( + """ +@__ints_0='[11,111]' (Size = 4000) + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT COUNT(*) + FROM ( + SELECT [i].[value] + FROM OPENJSON(CAST([p].[Ints] AS nvarchar(max))) WITH ([value] int '$') AS [i] + UNION + SELECT [i0].[value] + FROM OPENJSON(@__ints_0) WITH ([value] int '$') AS [i0] + ) AS [u]) = 2 +"""); + } + + public override async Task Column_collection_Intersect_inline_collection(bool async) + { + await base.Column_collection_Intersect_inline_collection(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT COUNT(*) + FROM ( + SELECT [i].[value] + FROM OPENJSON(CAST([p].[Ints] AS nvarchar(max))) WITH ([value] int '$') AS [i] + INTERSECT + SELECT [v].[Value] AS [value] + FROM (VALUES (CAST(11 AS int)), (111)) AS [v]([Value]) + ) AS [i0]) = 2 +"""); + } + + public override async Task Inline_collection_Except_column_collection(bool async) + { + await base.Inline_collection_Except_column_collection(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT COUNT(*) + FROM ( + SELECT [v].[Value] + FROM (VALUES (CAST(11 AS int)), (111)) AS [v]([Value]) + EXCEPT + SELECT [i].[value] AS [Value] + FROM OPENJSON(CAST([p].[Ints] AS nvarchar(max))) WITH ([value] int '$') AS [i] + ) AS [e] + WHERE [e].[Value] % 2 = 1) = 2 +"""); + } + + public override async Task Column_collection_Where_Union(bool async) + { + await base.Column_collection_Where_Union(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT COUNT(*) + FROM ( + SELECT [i].[value] + FROM OPENJSON(CAST([p].[Ints] AS nvarchar(max))) WITH ([value] int '$') AS [i] + WHERE [i].[value] > 100 + UNION + SELECT [v].[Value] AS [value] + FROM (VALUES (CAST(50 AS int))) AS [v]([Value]) + ) AS [u]) = 2 +"""); + } + + public override async Task Column_collection_equality_parameter_collection(bool async) + { + // TODO:SQLJSON Json type is not comparable + Assert.Equal( + "The JSON data type cannot be compared or sorted, except when using the IS NULL operator.", + (await Assert.ThrowsAsync(() => base.Column_collection_equality_parameter_collection(async))).Message); + + AssertSql( + """ +@__ints_0='[1,10]' (Size = 8000) + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE [p].[Ints] = @__ints_0 +"""); + } + + public override async Task Column_collection_Concat_parameter_collection_equality_inline_collection(bool async) + { + await base.Column_collection_Concat_parameter_collection_equality_inline_collection(async); + + AssertSql(); + } + + public override async Task Column_collection_equality_inline_collection(bool async) + { + // TODO:SQLJSON Json type is not comparable + Assert.Equal( + "The data types json and varchar are incompatible in the equal to operator.", + (await Assert.ThrowsAsync(() => base.Column_collection_equality_inline_collection(async))).Message); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE [p].[Ints] = '[1,10]' +"""); + } + + public override async Task Column_collection_equality_inline_collection_with_parameters(bool async) + { + await base.Column_collection_equality_inline_collection_with_parameters(async); + + AssertSql(); + } + + public override async Task Column_collection_Where_equality_inline_collection(bool async) + { + await base.Column_collection_Where_equality_inline_collection(async); + + AssertSql(); + } + + public override async Task Parameter_collection_in_subquery_Union_column_collection_as_compiled_query(bool async) + { + await base.Parameter_collection_in_subquery_Union_column_collection_as_compiled_query(async); + + AssertSql( + """ +@__ints='[10,111]' (Size = 4000) + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT COUNT(*) + FROM ( + SELECT [i1].[value] + FROM ( + SELECT CAST([i].[value] AS int) AS [value] + FROM OPENJSON(@__ints) AS [i] + ORDER BY CAST([i].[key] AS int) + OFFSET 1 ROWS + ) AS [i1] + UNION + SELECT [i0].[value] + FROM OPENJSON(CAST([p].[Ints] AS nvarchar(max))) WITH ([value] int '$') AS [i0] + ) AS [u]) = 3 +"""); + } + + public override async Task Parameter_collection_in_subquery_Union_column_collection(bool async) + { + await base.Parameter_collection_in_subquery_Union_column_collection(async); + + AssertSql( + """ +@__Skip_0='[111]' (Size = 4000) + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT COUNT(*) + FROM ( + SELECT [s].[value] + FROM OPENJSON(@__Skip_0) WITH ([value] int '$') AS [s] + UNION + SELECT [i].[value] + FROM OPENJSON(CAST([p].[Ints] AS nvarchar(max))) WITH ([value] int '$') AS [i] + ) AS [u]) = 3 +"""); + } + + public override async Task Parameter_collection_in_subquery_Union_column_collection_nested(bool async) + { + await base.Parameter_collection_in_subquery_Union_column_collection_nested(async); + + AssertSql( + """ +@__Skip_0='[111]' (Size = 4000) + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT COUNT(*) + FROM ( + SELECT [s].[value] + FROM OPENJSON(@__Skip_0) WITH ([value] int '$') AS [s] + UNION + SELECT [i2].[value] + FROM ( + SELECT TOP(20) [i1].[value] + FROM ( + SELECT DISTINCT [i0].[value] + FROM ( + SELECT [i].[value] + FROM OPENJSON(CAST([p].[Ints] AS nvarchar(max))) WITH ([value] int '$') AS [i] + ORDER BY [i].[value] + OFFSET 1 ROWS + ) AS [i0] + ) AS [i1] + ORDER BY [i1].[value] DESC + ) AS [i2] + ) AS [u]) = 3 +"""); + } + + public override void Parameter_collection_in_subquery_and_Convert_as_compiled_query() + { + base.Parameter_collection_in_subquery_and_Convert_as_compiled_query(); + + AssertSql(); + } + + public override async Task Parameter_collection_in_subquery_Count_as_compiled_query(bool async) + { + await base.Parameter_collection_in_subquery_Count_as_compiled_query(async); + + // TODO: the subquery projection contains extra columns which we should remove + AssertSql( + """ +@__ints='[10,111]' (Size = 4000) + +SELECT COUNT(*) +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT COUNT(*) + FROM ( + SELECT CAST([i].[value] AS int) AS [value0] + FROM OPENJSON(@__ints) AS [i] + ORDER BY CAST([i].[key] AS int) + OFFSET 1 ROWS + ) AS [i0] + WHERE [i0].[value0] > [p].[Id]) = 1 +"""); + } + + public override async Task Parameter_collection_in_subquery_Union_another_parameter_collection_as_compiled_query(bool async) + { + await base.Parameter_collection_in_subquery_Union_another_parameter_collection_as_compiled_query(async); + + AssertSql(); + } + + public override async Task Column_collection_in_subquery_Union_parameter_collection(bool async) + { + await base.Column_collection_in_subquery_Union_parameter_collection(async); + + AssertSql( + """ +@__ints_0='[10,111]' (Size = 4000) + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT COUNT(*) + FROM ( + SELECT [i1].[value] + FROM ( + SELECT CAST([i].[value] AS int) AS [value] + FROM OPENJSON(CAST([p].[Ints] AS nvarchar(max))) AS [i] + ORDER BY CAST([i].[key] AS int) + OFFSET 1 ROWS + ) AS [i1] + UNION + SELECT [i0].[value] + FROM OPENJSON(@__ints_0) WITH ([value] int '$') AS [i0] + ) AS [u]) = 3 +"""); + } + + public override async Task Project_collection_of_ints_simple(bool async) + { + await base.Project_collection_of_ints_simple(async); + + AssertSql( + """ +SELECT [p].[Ints] +FROM [PrimitiveCollectionsEntity] AS [p] +ORDER BY [p].[Id] +"""); + } + + public override async Task Project_collection_of_ints_ordered(bool async) + { + await base.Project_collection_of_ints_ordered(async); + + AssertSql( + """ +SELECT [p].[Id], CAST([i].[value] AS int) AS [value], [i].[key] +FROM [PrimitiveCollectionsEntity] AS [p] +OUTER APPLY OPENJSON(CAST([p].[Ints] AS nvarchar(max))) AS [i] +ORDER BY [p].[Id], CAST([i].[value] AS int) DESC +"""); + } + + public override async Task Project_collection_of_datetimes_filtered(bool async) + { + await base.Project_collection_of_datetimes_filtered(async); + + AssertSql( + """ +SELECT [p].[Id], [d0].[value], [d0].[key] +FROM [PrimitiveCollectionsEntity] AS [p] +OUTER APPLY ( + SELECT CAST([d].[value] AS datetime2) AS [value], [d].[key], CAST([d].[key] AS int) AS [c] + FROM OPENJSON(CAST([p].[DateTimes] AS nvarchar(max))) AS [d] + WHERE DATEPART(day, CAST([d].[value] AS datetime2)) <> 1 +) AS [d0] +ORDER BY [p].[Id], [d0].[c] +"""); + } + + public override async Task Project_collection_of_nullable_ints_with_paging(bool async) + { + await base.Project_collection_of_nullable_ints_with_paging(async); + + AssertSql( + """ +SELECT [p].[Id], [n0].[value], [n0].[key] +FROM [PrimitiveCollectionsEntity] AS [p] +OUTER APPLY ( + SELECT TOP(20) CAST([n].[value] AS int) AS [value], [n].[key], CAST([n].[key] AS int) AS [c] + FROM OPENJSON(CAST([p].[NullableInts] AS nvarchar(max))) AS [n] + ORDER BY CAST([n].[key] AS int) +) AS [n0] +ORDER BY [p].[Id], [n0].[c] +"""); + } + + public override async Task Project_collection_of_nullable_ints_with_paging2(bool async) + { + await base.Project_collection_of_nullable_ints_with_paging2(async); + + AssertSql( + """ +SELECT [p].[Id], [n0].[value], [n0].[key] +FROM [PrimitiveCollectionsEntity] AS [p] +OUTER APPLY ( + SELECT CAST([n].[value] AS int) AS [value], [n].[key] + FROM OPENJSON(CAST([p].[NullableInts] AS nvarchar(max))) AS [n] + ORDER BY CAST([n].[value] AS int) + OFFSET 1 ROWS +) AS [n0] +ORDER BY [p].[Id], [n0].[value] +"""); + } + + public override async Task Project_collection_of_nullable_ints_with_paging3(bool async) + { + await base.Project_collection_of_nullable_ints_with_paging3(async); + + AssertSql( + """ +SELECT [p].[Id], [n0].[value], [n0].[key] +FROM [PrimitiveCollectionsEntity] AS [p] +OUTER APPLY ( + SELECT CAST([n].[value] AS int) AS [value], [n].[key], CAST([n].[key] AS int) AS [c] + FROM OPENJSON(CAST([p].[NullableInts] AS nvarchar(max))) AS [n] + ORDER BY CAST([n].[key] AS int) + OFFSET 2 ROWS +) AS [n0] +ORDER BY [p].[Id], [n0].[c] +"""); + } + + public override async Task Project_collection_of_ints_with_distinct(bool async) + { + await base.Project_collection_of_ints_with_distinct(async); + + AssertSql( + """ +SELECT [p].[Id], [i0].[value] +FROM [PrimitiveCollectionsEntity] AS [p] +OUTER APPLY ( + SELECT DISTINCT [i].[value] + FROM OPENJSON(CAST([p].[Ints] AS nvarchar(max))) WITH ([value] int '$') AS [i] +) AS [i0] +ORDER BY [p].[Id] +"""); + } + + public override async Task Project_collection_of_nullable_ints_with_distinct(bool async) + { + await base.Project_collection_of_nullable_ints_with_distinct(async); + + AssertSql(""); + } + + public override async Task Project_collection_of_ints_with_ToList_and_FirstOrDefault(bool async) + { + await base.Project_collection_of_ints_with_ToList_and_FirstOrDefault(async); + + AssertSql( + """ +SELECT [p0].[Id], CAST([i].[value] AS int) AS [value], [i].[key] +FROM ( + SELECT TOP(1) [p].[Id], [p].[Ints] + FROM [PrimitiveCollectionsEntity] AS [p] + ORDER BY [p].[Id] +) AS [p0] +OUTER APPLY OPENJSON(CAST([p0].[Ints] AS nvarchar(max))) AS [i] +ORDER BY [p0].[Id], CAST([i].[key] AS int) +"""); + } + + public override async Task Project_empty_collection_of_nullables_and_collection_only_containing_nulls(bool async) + { + await base.Project_empty_collection_of_nullables_and_collection_only_containing_nulls(async); + + AssertSql( + """ +SELECT [p].[Id], [n1].[value], [n1].[key], [n2].[value], [n2].[key] +FROM [PrimitiveCollectionsEntity] AS [p] +OUTER APPLY ( + SELECT CAST([n].[value] AS int) AS [value], [n].[key], CAST([n].[key] AS int) AS [c] + FROM OPENJSON(CAST([p].[NullableInts] AS nvarchar(max))) AS [n] + WHERE 0 = 1 +) AS [n1] +OUTER APPLY ( + SELECT CAST([n0].[value] AS int) AS [value], [n0].[key], CAST([n0].[key] AS int) AS [c] + FROM OPENJSON(CAST([p].[NullableInts] AS nvarchar(max))) AS [n0] + WHERE [n0].[value] IS NULL +) AS [n2] +ORDER BY [p].[Id], [n1].[c], [n1].[key], [n2].[c] +"""); + } + + public override async Task Project_multiple_collections(bool async) + { + await base.Project_multiple_collections(async); + + AssertSql( + """ +SELECT [p].[Id], CAST([i].[value] AS int) AS [value], [i].[key], CAST([i0].[value] AS int) AS [value], [i0].[key], [d1].[value], [d1].[key], [d2].[value], [d2].[key] +FROM [PrimitiveCollectionsEntity] AS [p] +OUTER APPLY OPENJSON(CAST([p].[Ints] AS nvarchar(max))) AS [i] +OUTER APPLY OPENJSON(CAST([p].[Ints] AS nvarchar(max))) AS [i0] +OUTER APPLY ( + SELECT CAST([d].[value] AS datetime2) AS [value], [d].[key], CAST([d].[key] AS int) AS [c] + FROM OPENJSON(CAST([p].[DateTimes] AS nvarchar(max))) AS [d] + WHERE DATEPART(day, CAST([d].[value] AS datetime2)) <> 1 +) AS [d1] +OUTER APPLY ( + SELECT CAST([d0].[value] AS datetime2) AS [value], [d0].[key], CAST([d0].[key] AS int) AS [c] + FROM OPENJSON(CAST([p].[DateTimes] AS nvarchar(max))) AS [d0] + WHERE CAST([d0].[value] AS datetime2) > '2000-01-01T00:00:00.0000000' +) AS [d2] +ORDER BY [p].[Id], CAST([i].[key] AS int), [i].[key], CAST([i0].[value] AS int) DESC, [i0].[key], [d1].[c], [d1].[key], [d2].[c] +"""); + } + + public override async Task Project_primitive_collections_element(bool async) + { + await base.Project_primitive_collections_element(async); + + AssertSql( + """ +SELECT CAST(JSON_VALUE([p].[Ints], '$[0]') AS int) AS [Indexer], CAST(JSON_VALUE([p].[DateTimes], '$[0]') AS datetime2) AS [EnumerableElementAt], JSON_VALUE([p].[Strings], '$[1]') AS [QueryableElementAt] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE [p].[Id] < 4 +ORDER BY [p].[Id] +"""); + } + + public override async Task Project_inline_collection(bool async) + { + await base.Project_inline_collection(async); + + AssertSql( + """ +SELECT [p].[String] +FROM [PrimitiveCollectionsEntity] AS [p] +"""); + } + + public override async Task Project_inline_collection_with_Union(bool async) + { + await base.Project_inline_collection_with_Union(async); + + AssertSql( + """ +SELECT [p].[Id], [u].[Value] +FROM [PrimitiveCollectionsEntity] AS [p] +OUTER APPLY ( + SELECT [v].[Value] + FROM (VALUES ([p].[String])) AS [v]([Value]) + UNION + SELECT [p0].[String] AS [Value] + FROM [PrimitiveCollectionsEntity] AS [p0] +) AS [u] +ORDER BY [p].[Id] +"""); + } + + public override async Task Project_inline_collection_with_Concat(bool async) + { + await base.Project_inline_collection_with_Concat(async); + + AssertSql(); + } + + public override async Task Nested_contains_with_Lists_and_no_inferred_type_mapping(bool async) + { + await base.Nested_contains_with_Lists_and_no_inferred_type_mapping(async); + + AssertSql( + """ +@__ints_0='[1,2,3]' (Size = 4000) +@__strings_1='["one","two","three"]' (Size = 4000) + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE CASE + WHEN [p].[Int] IN ( + SELECT [i].[value] + FROM OPENJSON(@__ints_0) WITH ([value] int '$') AS [i] + ) THEN N'one' + ELSE N'two' +END IN ( + SELECT [s].[value] + FROM OPENJSON(@__strings_1) WITH ([value] nvarchar(max) '$') AS [s] +) +"""); + } + + public override async Task Nested_contains_with_arrays_and_no_inferred_type_mapping(bool async) + { + await base.Nested_contains_with_arrays_and_no_inferred_type_mapping(async); + + AssertSql( + """ +@__ints_0='[1,2,3]' (Size = 4000) +@__strings_1='["one","two","three"]' (Size = 4000) + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE CASE + WHEN [p].[Int] IN ( + SELECT [i].[value] + FROM OPENJSON(@__ints_0) WITH ([value] int '$') AS [i] + ) THEN N'one' + ELSE N'two' +END IN ( + SELECT [s].[value] + FROM OPENJSON(@__strings_1) WITH ([value] nvarchar(max) '$') AS [s] +) +"""); + } + + private void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); + + private PrimitiveCollectionsContext CreateContext() + => Fixture.CreateContext(); + + public class PrimitiveCollectionsQuerySqlServerFixture : PrimitiveCollectionsQueryFixtureBase, ITestSqlLoggerFactory + { + protected override string StoreName + => "PrimitiveCollectionsJsonTypeTest"; + + public TestSqlLoggerFactory TestSqlLoggerFactory + => (TestSqlLoggerFactory)ListLoggerFactory; + + protected override ITestStoreFactory TestStoreFactory + => SqlServerTestStoreFactory.Instance; + + public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder) + => base.AddOptions(builder) + .UseSqlServer(b => b.UseCompatibilityLevel(160)) + .ConfigureWarnings(e => e.Log(SqlServerEventId.JsonTypeExperimental)); + + protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context) + { + base.OnModelCreating(modelBuilder, context); + + modelBuilder.Entity( + b => + { + // Map DateTime to non-default datetime instead of the default datetime2 to exercise type mapping inference + b.Property(p => p.DateTime).HasColumnType("datetime"); + b.PrimitiveCollection(e => e.Strings).HasColumnType("json"); + b.PrimitiveCollection(e => e.Ints).HasColumnType("json"); + b.PrimitiveCollection(e => e.DateTimes).HasColumnType("json"); + b.PrimitiveCollection(e => e.Bools).HasColumnType("json"); + b.PrimitiveCollection(e => e.Ints).HasColumnType("json"); + b.PrimitiveCollection(e => e.Enums).HasColumnType("json"); + b.PrimitiveCollection(e => e.NullableStrings).HasColumnType("json"); + b.PrimitiveCollection(e => e.NullableInts).HasColumnType("json"); + }); + } + } +} diff --git a/test/EFCore.SqlServer.FunctionalTests/Scaffolding/SqlServerDatabaseModelFactoryTest.cs b/test/EFCore.SqlServer.FunctionalTests/Scaffolding/SqlServerDatabaseModelFactoryTest.cs index e3d56a4901f..de336712544 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Scaffolding/SqlServerDatabaseModelFactoryTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Scaffolding/SqlServerDatabaseModelFactoryTest.cs @@ -2453,6 +2453,46 @@ nationalCharacterVaryingMaxColumn national char varying(max) NULL }, "DROP TABLE MaxColumns;"); + [SqlServerCondition(SqlServerCondition.SupportsJsonType)] + [ConditionalFact] + public void Handles_native_JSON_type() + => Test( + @" +CREATE TABLE JsonColumns ( + Id int, + jsonTypeColumn json NULL +);", + Enumerable.Empty(), + Enumerable.Empty(), + (dbModel, scaffoldingFactory) => + { + var columns = dbModel.Tables.Single().Columns; + + Assert.Equal("json", columns.Single(c => c.Name == "jsonTypeColumn").StoreType); + + var model = scaffoldingFactory.Create(dbModel, new()); + + Assert.Collection( + model.GetEntityTypes(), + e => + { + Assert.Equal("JsonColumn", e.Name); + Assert.Collection( + e.GetProperties(), + p => Assert.Equal("Id", p.Name), + p => + { + Assert.Equal("JsonTypeColumn", p.Name); + Assert.Same(typeof(string), p.ClrType); + Assert.Null(p.GetMaxLength()); + }); + Assert.Empty(e.GetForeignKeys()); + Assert.Empty(e.GetSkipNavigations()); + Assert.Empty(e.GetNavigations()); + }); + }, + "DROP TABLE JsonColumns;"); + [ConditionalFact] public void Specific_max_length_are_add_to_store_type() => Test( diff --git a/test/EFCore.SqlServer.FunctionalTests/SqlServerFixture.cs b/test/EFCore.SqlServer.FunctionalTests/SqlServerFixture.cs index 57f63ac3a54..27015232920 100644 --- a/test/EFCore.SqlServer.FunctionalTests/SqlServerFixture.cs +++ b/test/EFCore.SqlServer.FunctionalTests/SqlServerFixture.cs @@ -21,6 +21,7 @@ public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder build w => { w.Log(SqlServerEventId.ByteIdentityColumnWarning); + w.Log(SqlServerEventId.JsonTypeExperimental); w.Log(SqlServerEventId.DecimalTypeKeyWarning); }); } diff --git a/test/EFCore.SqlServer.FunctionalTests/TestUtilities/SqlServerCondition.cs b/test/EFCore.SqlServer.FunctionalTests/TestUtilities/SqlServerCondition.cs index 031ff85e9d6..2c3dd3d6104 100644 --- a/test/EFCore.SqlServer.FunctionalTests/TestUtilities/SqlServerCondition.cs +++ b/test/EFCore.SqlServer.FunctionalTests/TestUtilities/SqlServerCondition.cs @@ -21,4 +21,5 @@ public enum SqlServerCondition SupportsFunctions2017 = 1 << 12, SupportsFunctions2019 = 1 << 13, SupportsFunctions2022 = 1 << 14, + SupportsJsonType = 1 << 15, } diff --git a/test/EFCore.SqlServer.FunctionalTests/TestUtilities/SqlServerConditionAttribute.cs b/test/EFCore.SqlServer.FunctionalTests/TestUtilities/SqlServerConditionAttribute.cs index a28352fad6e..7f61d3a59d5 100644 --- a/test/EFCore.SqlServer.FunctionalTests/TestUtilities/SqlServerConditionAttribute.cs +++ b/test/EFCore.SqlServer.FunctionalTests/TestUtilities/SqlServerConditionAttribute.cs @@ -92,6 +92,11 @@ public ValueTask IsMetAsync() isMet &= TestEnvironment.IsFunctions2022Supported; } + if (Conditions.HasFlag(SqlServerCondition.SupportsJsonType)) + { + isMet &= TestEnvironment.IsJsonTypeSupported; + } + return ValueTask.FromResult(isMet); } diff --git a/test/EFCore.SqlServer.FunctionalTests/TestUtilities/TestEnvironment.cs b/test/EFCore.SqlServer.FunctionalTests/TestUtilities/TestEnvironment.cs index a93f0517a71..34e7e76ab28 100644 --- a/test/EFCore.SqlServer.FunctionalTests/TestUtilities/TestEnvironment.cs +++ b/test/EFCore.SqlServer.FunctionalTests/TestUtilities/TestEnvironment.cs @@ -398,6 +398,10 @@ public static bool IsFunctions2022Supported } } + // TODO:SQLJSON Issue #34414 + public static bool IsJsonTypeSupported + => false; + public static byte SqlServerMajorVersion => GetProductMajorVersion(); diff --git a/test/EFCore.SqlServer.FunctionalTests/Update/JsonUpdateJsonTypeSqlServerFixture.cs b/test/EFCore.SqlServer.FunctionalTests/Update/JsonUpdateJsonTypeSqlServerFixture.cs new file mode 100644 index 00000000000..840544363e4 --- /dev/null +++ b/test/EFCore.SqlServer.FunctionalTests/Update/JsonUpdateJsonTypeSqlServerFixture.cs @@ -0,0 +1,74 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable disable +using Microsoft.EntityFrameworkCore.TestModels.JsonQuery; + +namespace Microsoft.EntityFrameworkCore.Update; + +public class JsonUpdateJsonTypeSqlServerFixture : JsonUpdateSqlServerFixture +{ + protected override string StoreName + => "JsonUpdateJsonTypeTest"; + + protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context) + { + base.OnModelCreating(modelBuilder, context); + + modelBuilder.Entity( + b => + { + b.OwnsOne(x => x.OwnedReferenceRoot).ToJson().HasColumnType("json"); + b.OwnsMany(x => x.OwnedCollectionRoot).ToJson().HasColumnType("json"); + }); + + modelBuilder.Entity( + b => + { + b.OwnsOne(x => x.ReferenceOnBase).ToJson().HasColumnType("json"); + b.OwnsMany(x => x.CollectionOnBase).ToJson().HasColumnType("json"); + }); + + modelBuilder.Entity( + b => + { + b.HasBaseType(); + b.OwnsOne(x => x.ReferenceOnDerived).ToJson().HasColumnType("json"); + b.OwnsMany(x => x.CollectionOnDerived).ToJson().HasColumnType("json"); + }); + + modelBuilder.Entity( + b => + { + b.OwnsOne(x => x.Reference).ToJson().HasColumnType("json"); + b.OwnsMany(x => x.Collection).ToJson().HasColumnType("json"); + b.PrimitiveCollection(e => e.TestDefaultStringCollection).HasColumnType("json"); + b.PrimitiveCollection(e => e.TestMaxLengthStringCollection).HasColumnType("json"); + b.PrimitiveCollection(e => e.TestInt16Collection).HasColumnType("json"); + b.PrimitiveCollection(e => e.TestInt32Collection).HasColumnType("json"); + b.PrimitiveCollection(e => e.TestDecimalCollection).HasColumnType("json"); + b.PrimitiveCollection(e => e.TestDateTimeCollection).HasColumnType("json"); + b.PrimitiveCollection(e => e.TestDateTimeOffsetCollection).HasColumnType("json"); + b.PrimitiveCollection(e => e.TestTimeSpanCollection).HasColumnType("json"); + b.PrimitiveCollection(e => e.TestInt64Collection).HasColumnType("json"); + b.PrimitiveCollection(e => e.TestDoubleCollection).HasColumnType("json"); + b.PrimitiveCollection(e => e.TestSingleCollection).HasColumnType("json"); + b.PrimitiveCollection(e => e.TestBooleanCollection).HasColumnType("json"); + b.PrimitiveCollection(e => e.TestCharacterCollection).HasColumnType("json"); + b.PrimitiveCollection(e => e.TestByteCollection).HasColumnType("json"); + b.PrimitiveCollection(e => e.TestGuidCollection).HasColumnType("json"); + b.PrimitiveCollection(e => e.TestUnsignedInt16Collection).HasColumnType("json"); + b.PrimitiveCollection(e => e.TestUnsignedInt32Collection).HasColumnType("json"); + b.PrimitiveCollection(e => e.TestUnsignedInt64Collection).HasColumnType("json"); + b.PrimitiveCollection(e => e.TestSignedByteCollection).HasColumnType("json"); + b.PrimitiveCollection(e => e.TestNullableInt32Collection).HasColumnType("json"); + b.PrimitiveCollection(e => e.TestEnumCollection).HasColumnType("json"); + b.PrimitiveCollection(e => e.TestEnumWithIntConverterCollection).HasColumnType("json"); + b.PrimitiveCollection(e => e.TestNullableEnumCollection).HasColumnType("json"); + b.PrimitiveCollection(e => e.TestNullableEnumWithIntConverterCollection).HasColumnType("json"); + b.PrimitiveCollection(e => e.TestNullableEnumWithConverterThatHandlesNullsCollection).HasColumnType("json"); + }); + + modelBuilder.Entity().OwnsOne(x => x.Reference).ToJson().HasColumnType("json"); + } +} diff --git a/test/EFCore.SqlServer.FunctionalTests/Update/JsonUpdateJsonTypeSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Update/JsonUpdateJsonTypeSqlServerTest.cs new file mode 100644 index 00000000000..3fcc3c283f9 --- /dev/null +++ b/test/EFCore.SqlServer.FunctionalTests/Update/JsonUpdateJsonTypeSqlServerTest.cs @@ -0,0 +1,2963 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable disable +using Xunit.Sdk; + +namespace Microsoft.EntityFrameworkCore.Update; + +[SqlServerCondition(SqlServerCondition.SupportsJsonType)] +public class JsonUpdateJsonTypeSqlServerTest : JsonUpdateTestBase +{ + public JsonUpdateJsonTypeSqlServerTest(JsonUpdateJsonTypeSqlServerFixture fixture) + : base(fixture) + { + ClearLog(); + } + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); + + public override async Task Add_element_to_json_collection_branch() + { + // TODO:SQLJSON (See JsonTypeToFunction.cs) + Assert.Equal( + "Argument data type json is invalid for argument 3 of json_modify function.", + (await Assert.ThrowsAsync( + () => base.Add_element_to_json_collection_branch())).InnerException?.Message); + + AssertSql( + """ +@p0='[{"Date":"2101-01-01T00:00:00","Enum":2,"Enums":[-1,-1,2],"Fraction":10.1,"NullableEnum":-1,"NullableEnums":[null,-1,2],"OwnedCollectionLeaf":[{"SomethingSomething":"e1_r_c1_c1"},{"SomethingSomething":"e1_r_c1_c2"}],"OwnedReferenceLeaf":{"SomethingSomething":"e1_r_c1_r"}},{"Date":"2102-01-01T00:00:00","Enum":-3,"Enums":[-1,-1,2],"Fraction":10.2,"NullableEnum":2,"NullableEnums":[null,-1,2],"OwnedCollectionLeaf":[{"SomethingSomething":"e1_r_c2_c1"},{"SomethingSomething":"e1_r_c2_c2"}],"OwnedReferenceLeaf":{"SomethingSomething":"e1_r_c2_r"}},{"Date":"2010-10-10T00:00:00","Enum":-3,"Enums":null,"Fraction":42.42,"NullableEnum":null,"NullableEnums":null,"OwnedCollectionLeaf":[{"SomethingSomething":"ss1"},{"SomethingSomething":"ss2"}],"OwnedReferenceLeaf":{"SomethingSomething":"ss3"}}]' (Nullable = false) (Size = 789) +@p1='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesBasic] SET [OwnedReferenceRoot] = JSON_MODIFY([OwnedReferenceRoot], 'strict $.OwnedCollectionBranch', JSON_QUERY(@p0)) +OUTPUT 1 +WHERE [Id] = @p1; +"""); + } + + public override async Task Add_element_to_json_collection_leaf() + { + // TODO:SQLJSON (See JsonTypeToFunction.cs) + Assert.Equal( + "Argument data type json is invalid for argument 3 of json_modify function.", + (await Assert.ThrowsAsync( + () => base.Add_element_to_json_collection_leaf())).InnerException?.Message); + + AssertSql( + """ +@p0='[{"SomethingSomething":"e1_r_r_c1"},{"SomethingSomething":"e1_r_r_c2"},{"SomethingSomething":"ss1"}]' (Nullable = false) (Size = 100) +@p1='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesBasic] SET [OwnedReferenceRoot] = JSON_MODIFY([OwnedReferenceRoot], 'strict $.OwnedReferenceBranch.OwnedCollectionLeaf', JSON_QUERY(@p0)) +OUTPUT 1 +WHERE [Id] = @p1; +"""); + } + + public override async Task Add_element_to_json_collection_on_derived() + { + await base.Add_element_to_json_collection_on_derived(); + + AssertSql( + """ +@p0='[{"Date":"2221-01-01T00:00:00","Enum":2,"Enums":[-1,-1,2],"Fraction":221.1,"NullableEnum":-1,"NullableEnums":[null,-1,2],"OwnedCollectionLeaf":[{"SomethingSomething":"d2_r_c1"},{"SomethingSomething":"d2_r_c2"}],"OwnedReferenceLeaf":{"SomethingSomething":"d2_r_r"}},{"Date":"2222-01-01T00:00:00","Enum":-3,"Enums":[-1,-1,2],"Fraction":222.1,"NullableEnum":2,"NullableEnums":[null,-1,2],"OwnedCollectionLeaf":[{"SomethingSomething":"d2_r_c1"},{"SomethingSomething":"d2_r_c2"}],"OwnedReferenceLeaf":{"SomethingSomething":"d2_r_r"}},{"Date":"2010-10-10T00:00:00","Enum":-3,"Enums":null,"Fraction":42.42,"NullableEnum":null,"NullableEnums":null,"OwnedCollectionLeaf":[{"SomethingSomething":"ss1"},{"SomethingSomething":"ss2"}],"OwnedReferenceLeaf":{"SomethingSomething":"ss3"}}]' (Nullable = false) (Size = 773) +@p1='2' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesInheritance] SET [CollectionOnDerived] = @p0 +OUTPUT 1 +WHERE [Id] = @p1; +""", + // + """ +SELECT TOP(2) [j].[Id], [j].[Discriminator], [j].[Name], [j].[Fraction], [j].[CollectionOnBase], [j].[ReferenceOnBase], [j].[CollectionOnDerived], [j].[ReferenceOnDerived] +FROM [JsonEntitiesInheritance] AS [j] +WHERE [j].[Discriminator] = N'JsonEntityInheritanceDerived' +"""); + } + + public override async Task Add_element_to_json_collection_root() + { + await base.Add_element_to_json_collection_root(); + + AssertSql( + """ +@p0='[{"Name":"e1_c1","Names":["e1_c11","e1_c12"],"Number":11,"Numbers":[-1000,0,1000],"OwnedCollectionBranch":[{"Date":"2111-01-01T00:00:00","Enum":2,"Enums":[-1,-1,2],"Fraction":11.1,"NullableEnum":-1,"NullableEnums":[null,-1,2],"OwnedCollectionLeaf":[{"SomethingSomething":"e1_c1_c1_c1"},{"SomethingSomething":"e1_c1_c1_c2"}],"OwnedReferenceLeaf":{"SomethingSomething":"e1_c1_c1_r"}},{"Date":"2112-01-01T00:00:00","Enum":-3,"Enums":[-1,-1,2],"Fraction":11.2,"NullableEnum":2,"NullableEnums":[null,-1,2],"OwnedCollectionLeaf":[{"SomethingSomething":"e1_c1_c2_c1"},{"SomethingSomething":"e1_c1_c2_c2"}],"OwnedReferenceLeaf":{"SomethingSomething":"e1_c1_c2_r"}}],"OwnedReferenceBranch":{"Date":"2110-01-01T00:00:00","Enum":-1,"Enums":[-1,-1,2],"Fraction":11.0,"NullableEnum":null,"NullableEnums":[null,-1,2],"OwnedCollectionLeaf":[{"SomethingSomething":"e1_c1_r_c1"},{"SomethingSomething":"e1_c1_r_c2"}],"OwnedReferenceLeaf":{"SomethingSomething":"e1_c1_r_r"}}},{"Name":"e1_c2","Names":["e1_c21","e1_c22"],"Number":12,"Numbers":[-1001,0,1001],"OwnedCollectionBranch":[{"Date":"2121-01-01T00:00:00","Enum":2,"Enums":[-1,-1,2],"Fraction":12.1,"NullableEnum":-1,"NullableEnums":[null,-1,2],"OwnedCollectionLeaf":[{"SomethingSomething":"e1_c2_c1_c1"},{"SomethingSomething":"e1_c2_c1_c2"}],"OwnedReferenceLeaf":{"SomethingSomething":"e1_c2_c1_r"}},{"Date":"2122-01-01T00:00:00","Enum":-1,"Enums":[-1,-1,2],"Fraction":12.2,"NullableEnum":null,"NullableEnums":[null,-1,2],"OwnedCollectionLeaf":[{"SomethingSomething":"e1_c2_c2_c1"},{"SomethingSomething":"e1_c2_c2_c2"}],"OwnedReferenceLeaf":{"SomethingSomething":"e1_c2_c2_r"}}],"OwnedReferenceBranch":{"Date":"2120-01-01T00:00:00","Enum":-3,"Enums":[-1,-1,2],"Fraction":12.0,"NullableEnum":2,"NullableEnums":[null,-1,2],"OwnedCollectionLeaf":[{"SomethingSomething":"e1_c2_r_c1"},{"SomethingSomething":"e1_c2_r_c2"}],"OwnedReferenceLeaf":{"SomethingSomething":"e1_c2_r_r"}}},{"Name":"new Name","Names":null,"Number":142,"Numbers":null,"OwnedCollectionBranch":[],"OwnedReferenceBranch":{"Date":"2010-10-10T00:00:00","Enum":-3,"Enums":null,"Fraction":42.42,"NullableEnum":null,"NullableEnums":null,"OwnedCollectionLeaf":[{"SomethingSomething":"ss1"},{"SomethingSomething":"ss2"}],"OwnedReferenceLeaf":{"SomethingSomething":"ss3"}}}]' (Nullable = false) (Size = 2268) +@p1='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesBasic] SET [OwnedCollectionRoot] = @p0 +OUTPUT 1 +WHERE [Id] = @p1; +""", + // + """ +SELECT TOP(2) [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Add_element_to_json_collection_root_null_navigations() + { + await base.Add_element_to_json_collection_root_null_navigations(); + + AssertSql( + """ +@p0='[{"Name":"e1_c1","Names":["e1_c11","e1_c12"],"Number":11,"Numbers":[-1000,0,1000],"OwnedCollectionBranch":[{"Date":"2111-01-01T00:00:00","Enum":2,"Enums":[-1,-1,2],"Fraction":11.1,"NullableEnum":-1,"NullableEnums":[null,-1,2],"OwnedCollectionLeaf":[{"SomethingSomething":"e1_c1_c1_c1"},{"SomethingSomething":"e1_c1_c1_c2"}],"OwnedReferenceLeaf":{"SomethingSomething":"e1_c1_c1_r"}},{"Date":"2112-01-01T00:00:00","Enum":-3,"Enums":[-1,-1,2],"Fraction":11.2,"NullableEnum":2,"NullableEnums":[null,-1,2],"OwnedCollectionLeaf":[{"SomethingSomething":"e1_c1_c2_c1"},{"SomethingSomething":"e1_c1_c2_c2"}],"OwnedReferenceLeaf":{"SomethingSomething":"e1_c1_c2_r"}}],"OwnedReferenceBranch":{"Date":"2110-01-01T00:00:00","Enum":-1,"Enums":[-1,-1,2],"Fraction":11.0,"NullableEnum":null,"NullableEnums":[null,-1,2],"OwnedCollectionLeaf":[{"SomethingSomething":"e1_c1_r_c1"},{"SomethingSomething":"e1_c1_r_c2"}],"OwnedReferenceLeaf":{"SomethingSomething":"e1_c1_r_r"}}},{"Name":"e1_c2","Names":["e1_c21","e1_c22"],"Number":12,"Numbers":[-1001,0,1001],"OwnedCollectionBranch":[{"Date":"2121-01-01T00:00:00","Enum":2,"Enums":[-1,-1,2],"Fraction":12.1,"NullableEnum":-1,"NullableEnums":[null,-1,2],"OwnedCollectionLeaf":[{"SomethingSomething":"e1_c2_c1_c1"},{"SomethingSomething":"e1_c2_c1_c2"}],"OwnedReferenceLeaf":{"SomethingSomething":"e1_c2_c1_r"}},{"Date":"2122-01-01T00:00:00","Enum":-1,"Enums":[-1,-1,2],"Fraction":12.2,"NullableEnum":null,"NullableEnums":[null,-1,2],"OwnedCollectionLeaf":[{"SomethingSomething":"e1_c2_c2_c1"},{"SomethingSomething":"e1_c2_c2_c2"}],"OwnedReferenceLeaf":{"SomethingSomething":"e1_c2_c2_r"}}],"OwnedReferenceBranch":{"Date":"2120-01-01T00:00:00","Enum":-3,"Enums":[-1,-1,2],"Fraction":12.0,"NullableEnum":2,"NullableEnums":[null,-1,2],"OwnedCollectionLeaf":[{"SomethingSomething":"e1_c2_r_c1"},{"SomethingSomething":"e1_c2_r_c2"}],"OwnedReferenceLeaf":{"SomethingSomething":"e1_c2_r_r"}}},{"Name":"new Name","Names":null,"Number":142,"Numbers":null,"OwnedCollectionBranch":null,"OwnedReferenceBranch":{"Date":"2010-10-10T00:00:00","Enum":-3,"Enums":null,"Fraction":42.42,"NullableEnum":null,"NullableEnums":null,"OwnedCollectionLeaf":null,"OwnedReferenceLeaf":null}}]' (Nullable = false) (Size = 2191) +@p1='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesBasic] SET [OwnedCollectionRoot] = @p0 +OUTPUT 1 +WHERE [Id] = @p1; +""", + // + """ +SELECT TOP(2) [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Add_entity_with_json() + { + await base.Add_entity_with_json(); + + AssertSql( + """ +@p0='{"Name":"RootName","Names":null,"Number":42,"Numbers":null,"OwnedCollectionBranch":[],"OwnedReferenceBranch":{"Date":"2010-10-10T00:00:00","Enum":-3,"Enums":null,"Fraction":42.42,"NullableEnum":null,"NullableEnums":null,"OwnedCollectionLeaf":[{"SomethingSomething":"ss1"},{"SomethingSomething":"ss2"}],"OwnedReferenceLeaf":{"SomethingSomething":"ss3"}}}' (Nullable = false) (Size = 353) +@p1='[]' (Nullable = false) (Size = 2) +@p2='2' +@p3=NULL (DbType = Int32) +@p4='NewEntity' (Size = 4000) + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +INSERT INTO [JsonEntitiesBasic] ([OwnedReferenceRoot], [OwnedCollectionRoot], [Id], [EntityBasicId], [Name]) +VALUES (@p0, @p1, @p2, @p3, @p4); +""", + // + """ +SELECT [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Add_entity_with_json_null_navigations() + { + // TODO:SQLJSON Updates to null fail (See UpdateToNull.cs) + await Assert.ThrowsAsync( + () => base.Add_entity_with_json_null_navigations()); + + AssertSql( + """ +@p0='{"Name":"RootName","Names":null,"Number":42,"Numbers":null,"OwnedCollectionBranch":null,"OwnedReferenceBranch":{"Date":"2010-10-10T00:00:00","Enum":-3,"Enums":null,"Fraction":42.42,"NullableEnum":null,"NullableEnums":null,"OwnedCollectionLeaf":[{"SomethingSomething":"ss1"},{"SomethingSomething":"ss2"}],"OwnedReferenceLeaf":null}}' (Nullable = false) (Size = 331) +@p1='2' +@p2=NULL (DbType = Int32) +@p3='NewEntity' (Size = 4000) + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +INSERT INTO [JsonEntitiesBasic] ([OwnedReferenceRoot], [Id], [EntityBasicId], [Name]) +VALUES (@p0, @p1, @p2, @p3); +""", + // + """ +SELECT [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Add_json_reference_leaf() + { + // TODO:SQLJSON (See JsonTypeToFunction.cs) + Assert.Equal( + "Argument data type json is invalid for argument 3 of json_modify function.", + (await Assert.ThrowsAsync( + () => base.Add_json_reference_leaf())).InnerException?.Message); + + AssertSql( + """ +SELECT [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +""", + // + """ +@p0=NULL (Nullable = false) +@p1='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesBasic] SET [OwnedReferenceRoot] = JSON_MODIFY([OwnedReferenceRoot], 'strict $.OwnedCollectionBranch[0].OwnedReferenceLeaf', JSON_QUERY(@p0)) +OUTPUT 1 +WHERE [Id] = @p1; +"""); + } + + public override async Task Add_json_reference_root() + { + // TODO:SQLJSON Updates to null fail (See UpdateToNull.cs) + await Assert.ThrowsAsync( + () => base.Add_json_reference_root()); + + AssertSql( + """ +SELECT [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +""", + // + """ +@p0=NULL (Nullable = false) +@p1='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesBasic] SET [OwnedReferenceRoot] = @p0 +OUTPUT 1 +WHERE [Id] = @p1; +""", + // + """ +SELECT [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Delete_entity_with_json() + { + await base.Delete_entity_with_json(); + + AssertSql( + """ +@p0='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +DELETE FROM [JsonEntitiesBasic] +OUTPUT 1 +WHERE [Id] = @p0; +""", + // + """ +SELECT COUNT(*) +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Delete_json_collection_branch() + { + // TODO:SQLJSON (See JsonTypeToFunction.cs) + Assert.Equal( + "Argument data type json is invalid for argument 3 of json_modify function.", + (await Assert.ThrowsAsync( + () => base.Delete_json_collection_branch())).InnerException?.Message); + + AssertSql( + """ +@p0=NULL (Nullable = false) +@p1='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesBasic] SET [OwnedReferenceRoot] = JSON_MODIFY([OwnedReferenceRoot], 'strict $.OwnedCollectionBranch', JSON_QUERY(@p0)) +OUTPUT 1 +WHERE [Id] = @p1; +"""); + } + + public override async Task Delete_json_collection_root() + { + // TODO:SQLJSON Updates to null fail (See UpdateToNull.cs) + await Assert.ThrowsAsync( + () => base.Delete_json_collection_root()); + + AssertSql( + """ +@p0=NULL (Nullable = false) +@p1='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesBasic] SET [OwnedCollectionRoot] = @p0 +OUTPUT 1 +WHERE [Id] = @p1; +""", + // + """ +SELECT TOP(2) [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Delete_json_reference_leaf() + { + // TODO:SQLJSON (See JsonTypeToFunction.cs) + Assert.Equal( + "Argument data type json is invalid for argument 3 of json_modify function.", + (await Assert.ThrowsAsync( + () => base.Delete_json_reference_leaf())).InnerException?.Message); + + AssertSql( + """ +@p0=NULL (Nullable = false) +@p1='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesBasic] SET [OwnedReferenceRoot] = JSON_MODIFY([OwnedReferenceRoot], 'strict $.OwnedReferenceBranch.OwnedReferenceLeaf', JSON_QUERY(@p0)) +OUTPUT 1 +WHERE [Id] = @p1; +"""); + } + + public override async Task Delete_json_reference_root() + { + // TODO:SQLJSON Updates to null fail (See UpdateToNull.cs) + await Assert.ThrowsAsync( + () => base.Delete_json_reference_root()); + + AssertSql( + """ +@p0=NULL (Nullable = false) +@p1='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesBasic] SET [OwnedReferenceRoot] = @p0 +OUTPUT 1 +WHERE [Id] = @p1; +""", + // + """ +SELECT TOP(2) [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Edit_element_in_json_collection_branch() + { + await base.Edit_element_in_json_collection_branch(); + + AssertSql( + """ +@p0='2111-11-11T00:00:00' (Nullable = false) (Size = 4000) +@p1='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesBasic] SET [OwnedCollectionRoot] = JSON_MODIFY([OwnedCollectionRoot], 'strict $[0].OwnedCollectionBranch[0].Date', @p0) +OUTPUT 1 +WHERE [Id] = @p1; +""", + // + """ +SELECT TOP(2) [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Edit_element_in_json_collection_root1() + { + await base.Edit_element_in_json_collection_root1(); + + AssertSql( + """ +@p0='Modified' (Nullable = false) (Size = 4000) +@p1='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesBasic] SET [OwnedCollectionRoot] = JSON_MODIFY([OwnedCollectionRoot], 'strict $[0].Name', @p0) +OUTPUT 1 +WHERE [Id] = @p1; +""", + // + """ +SELECT TOP(2) [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Edit_element_in_json_collection_root2() + { + await base.Edit_element_in_json_collection_root2(); + + AssertSql( + """ +@p0='Modified' (Nullable = false) (Size = 4000) +@p1='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesBasic] SET [OwnedCollectionRoot] = JSON_MODIFY([OwnedCollectionRoot], 'strict $[1].Name', @p0) +OUTPUT 1 +WHERE [Id] = @p1; +""", + // + """ +SELECT TOP(2) [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Edit_element_in_json_multiple_levels_partial_update() + { + // TODO:SQLJSON (See JsonTypeToFunction.cs) + Assert.Equal( + "Argument data type json is invalid for argument 3 of json_modify function.", + (await Assert.ThrowsAsync( + () => base.Edit_element_in_json_multiple_levels_partial_update())).InnerException?.Message); + + AssertSql( + """ +@p0='[{"Date":"2111-01-01T00:00:00","Enum":2,"Enums":[-1,-1,2],"Fraction":11.1,"NullableEnum":-1,"NullableEnums":[null,-1,2],"OwnedCollectionLeaf":[{"SomethingSomething":"...and another"},{"SomethingSomething":"e1_c1_c1_c2"}],"OwnedReferenceLeaf":{"SomethingSomething":"e1_c1_c1_r"}},{"Date":"2112-01-01T00:00:00","Enum":-3,"Enums":[-1,-1,2],"Fraction":11.2,"NullableEnum":2,"NullableEnums":[null,-1,2],"OwnedCollectionLeaf":[{"SomethingSomething":"yet another change"},{"SomethingSomething":"and another"}],"OwnedReferenceLeaf":{"SomethingSomething":"e1_c1_c2_r"}}]' (Nullable = false) (Size = 561) +@p1='{"Name":"edit","Names":["e1_r1","e1_r2"],"Number":10,"Numbers":[-2147483648,-1,0,1,2147483647],"OwnedCollectionBranch":[{"Date":"2101-01-01T00:00:00","Enum":2,"Enums":[-1,-1,2],"Fraction":10.1,"NullableEnum":-1,"NullableEnums":[null,-1,2],"OwnedCollectionLeaf":[{"SomethingSomething":"e1_r_c1_c1"},{"SomethingSomething":"e1_r_c1_c2"}],"OwnedReferenceLeaf":{"SomethingSomething":"e1_r_c1_r"}},{"Date":"2102-01-01T00:00:00","Enum":-3,"Enums":[-1,-1,2],"Fraction":10.2,"NullableEnum":2,"NullableEnums":[null,-1,2],"OwnedCollectionLeaf":[{"SomethingSomething":"e1_r_c2_c1"},{"SomethingSomething":"e1_r_c2_c2"}],"OwnedReferenceLeaf":{"SomethingSomething":"e1_r_c2_r"}}],"OwnedReferenceBranch":{"Date":"2111-11-11T00:00:00","Enum":-1,"Enums":[-1,-1,2],"Fraction":10.0,"NullableEnum":null,"NullableEnums":[null,-1,2],"OwnedCollectionLeaf":[{"SomethingSomething":"e1_r_r_c1"},{"SomethingSomething":"e1_r_r_c2"}],"OwnedReferenceLeaf":{"SomethingSomething":"e1_r_r_r"}}}' (Nullable = false) (Size = 960) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesBasic] SET [OwnedCollectionRoot] = JSON_MODIFY([OwnedCollectionRoot], 'strict $[0].OwnedCollectionBranch', JSON_QUERY(@p0)), [OwnedReferenceRoot] = @p1 +OUTPUT 1 +WHERE [Id] = @p2; +"""); + } + + public override async Task Edit_element_in_json_branch_collection_and_add_element_to_the_same_collection() + { + // TODO:SQLJSON (See JsonTypeToFunction.cs) + Assert.Equal( + "Argument data type json is invalid for argument 3 of json_modify function.", + (await Assert.ThrowsAsync( + () => base.Edit_element_in_json_branch_collection_and_add_element_to_the_same_collection())).InnerException?.Message); + + AssertSql( + """ +@p0='[{"Date":"2101-01-01T00:00:00","Enum":2,"Enums":[-1,-1,2],"Fraction":4321.3,"NullableEnum":-1,"NullableEnums":[null,-1,2],"OwnedCollectionLeaf":[{"SomethingSomething":"e1_r_c1_c1"},{"SomethingSomething":"e1_r_c1_c2"}],"OwnedReferenceLeaf":{"SomethingSomething":"e1_r_c1_r"}},{"Date":"2102-01-01T00:00:00","Enum":-3,"Enums":[-1,-1,2],"Fraction":10.2,"NullableEnum":2,"NullableEnums":[null,-1,2],"OwnedCollectionLeaf":[{"SomethingSomething":"e1_r_c2_c1"},{"SomethingSomething":"e1_r_c2_c2"}],"OwnedReferenceLeaf":{"SomethingSomething":"e1_r_c2_r"}},{"Date":"2222-11-11T00:00:00","Enum":-3,"Enums":null,"Fraction":45.32,"NullableEnum":null,"NullableEnums":null,"OwnedCollectionLeaf":null,"OwnedReferenceLeaf":{"SomethingSomething":"cc"}}]' (Nullable = false) (Size = 735) +@p1='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesBasic] SET [OwnedReferenceRoot] = JSON_MODIFY([OwnedReferenceRoot], 'strict $.OwnedCollectionBranch', JSON_QUERY(@p0)) +OUTPUT 1 +WHERE [Id] = @p1; +"""); + } + + public override async Task Edit_two_elements_in_the_same_json_collection() + { + // TODO:SQLJSON (See JsonTypeToFunction.cs) + Assert.Equal( + "Argument data type json is invalid for argument 3 of json_modify function.", + (await Assert.ThrowsAsync( + () => base.Edit_two_elements_in_the_same_json_collection())).InnerException?.Message); + + AssertSql( + """ +@p0='[{"SomethingSomething":"edit1"},{"SomethingSomething":"edit2"}]' (Nullable = false) (Size = 63) +@p1='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesBasic] SET [OwnedReferenceRoot] = JSON_MODIFY([OwnedReferenceRoot], 'strict $.OwnedCollectionBranch[0].OwnedCollectionLeaf', JSON_QUERY(@p0)) +OUTPUT 1 +WHERE [Id] = @p1; +"""); + } + + public override async Task Edit_two_elements_in_the_same_json_collection_at_the_root() + { + await base.Edit_two_elements_in_the_same_json_collection_at_the_root(); + + AssertSql( + """ +@p0='[{"Name":"edit1","Names":["e1_c11","e1_c12"],"Number":11,"Numbers":[-1000,0,1000],"OwnedCollectionBranch":[{"Date":"2111-01-01T00:00:00","Enum":2,"Enums":[-1,-1,2],"Fraction":11.1,"NullableEnum":-1,"NullableEnums":[null,-1,2],"OwnedCollectionLeaf":[{"SomethingSomething":"e1_c1_c1_c1"},{"SomethingSomething":"e1_c1_c1_c2"}],"OwnedReferenceLeaf":{"SomethingSomething":"e1_c1_c1_r"}},{"Date":"2112-01-01T00:00:00","Enum":-3,"Enums":[-1,-1,2],"Fraction":11.2,"NullableEnum":2,"NullableEnums":[null,-1,2],"OwnedCollectionLeaf":[{"SomethingSomething":"e1_c1_c2_c1"},{"SomethingSomething":"e1_c1_c2_c2"}],"OwnedReferenceLeaf":{"SomethingSomething":"e1_c1_c2_r"}}],"OwnedReferenceBranch":{"Date":"2110-01-01T00:00:00","Enum":-1,"Enums":[-1,-1,2],"Fraction":11.0,"NullableEnum":null,"NullableEnums":[null,-1,2],"OwnedCollectionLeaf":[{"SomethingSomething":"e1_c1_r_c1"},{"SomethingSomething":"e1_c1_r_c2"}],"OwnedReferenceLeaf":{"SomethingSomething":"e1_c1_r_r"}}},{"Name":"edit2","Names":["e1_c21","e1_c22"],"Number":12,"Numbers":[-1001,0,1001],"OwnedCollectionBranch":[{"Date":"2121-01-01T00:00:00","Enum":2,"Enums":[-1,-1,2],"Fraction":12.1,"NullableEnum":-1,"NullableEnums":[null,-1,2],"OwnedCollectionLeaf":[{"SomethingSomething":"e1_c2_c1_c1"},{"SomethingSomething":"e1_c2_c1_c2"}],"OwnedReferenceLeaf":{"SomethingSomething":"e1_c2_c1_r"}},{"Date":"2122-01-01T00:00:00","Enum":-1,"Enums":[-1,-1,2],"Fraction":12.2,"NullableEnum":null,"NullableEnums":[null,-1,2],"OwnedCollectionLeaf":[{"SomethingSomething":"e1_c2_c2_c1"},{"SomethingSomething":"e1_c2_c2_c2"}],"OwnedReferenceLeaf":{"SomethingSomething":"e1_c2_c2_r"}}],"OwnedReferenceBranch":{"Date":"2120-01-01T00:00:00","Enum":-3,"Enums":[-1,-1,2],"Fraction":12.0,"NullableEnum":2,"NullableEnums":[null,-1,2],"OwnedCollectionLeaf":[{"SomethingSomething":"e1_c2_r_c1"},{"SomethingSomething":"e1_c2_r_c2"}],"OwnedReferenceLeaf":{"SomethingSomething":"e1_c2_r_r"}}}]' (Nullable = false) (Size = 1913) +@p1='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesBasic] SET [OwnedCollectionRoot] = @p0 +OUTPUT 1 +WHERE [Id] = @p1; +""", + // + """ +SELECT TOP(2) [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Edit_collection_element_and_reference_at_once() + { + // TODO:SQLJSON (See JsonTypeToFunction.cs) + Assert.Equal( + "Argument data type json is invalid for argument 3 of json_modify function.", + (await Assert.ThrowsAsync( + () => base.Edit_collection_element_and_reference_at_once())).InnerException?.Message); + + AssertSql( + """ +@p0='{"Date":"2102-01-01T00:00:00","Enum":-3,"Enums":[-1,-1,2],"Fraction":10.2,"NullableEnum":2,"NullableEnums":[null,-1,2],"OwnedCollectionLeaf":[{"SomethingSomething":"edit1"},{"SomethingSomething":"e1_r_c2_c2"}],"OwnedReferenceLeaf":{"SomethingSomething":"edit2"}}' (Nullable = false) (Size = 262) +@p1='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesBasic] SET [OwnedReferenceRoot] = JSON_MODIFY([OwnedReferenceRoot], 'strict $.OwnedCollectionBranch[1]', JSON_QUERY(@p0)) +OUTPUT 1 +WHERE [Id] = @p1; +"""); + } + + public override async Task Edit_single_enum_property() + { + await base.Edit_single_enum_property(); + + AssertSql( + """ +@p0='2' +@p1='2' +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesBasic] SET [OwnedCollectionRoot] = JSON_MODIFY([OwnedCollectionRoot], 'strict $[1].OwnedCollectionBranch[1].Enum', @p0), [OwnedReferenceRoot] = JSON_MODIFY([OwnedReferenceRoot], 'strict $.OwnedReferenceBranch.Enum', @p1) +OUTPUT 1 +WHERE [Id] = @p2; +""", + // + """ +SELECT TOP(2) [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Edit_single_numeric_property() + { + await base.Edit_single_numeric_property(); + + AssertSql( + """ +@p0='1024' +@p1='999' +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesBasic] SET [OwnedCollectionRoot] = JSON_MODIFY([OwnedCollectionRoot], 'strict $[1].Number', @p0), [OwnedReferenceRoot] = JSON_MODIFY([OwnedReferenceRoot], 'strict $.Number', @p1) +OUTPUT 1 +WHERE [Id] = @p2; +""", + // + """ +SELECT TOP(2) [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +"""); + } + + public override async Task Edit_single_property_bool() + { + await base.Edit_single_property_bool(); + + AssertSql( + """ +@p0='True' +@p1='False' +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestBoolean', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestBoolean', @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_byte() + { + await base.Edit_single_property_byte(); + + AssertSql( + """ +@p0='14' (Size = 1) +@p1='25' (Size = 1) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestByte', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestByte', @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_char() + { + await base.Edit_single_property_char(); + + AssertSql( + """ +@p0='t' (Nullable = false) (Size = 4000) +@p1='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Reference] = JSON_MODIFY([Reference], 'strict $.TestCharacter', @p0) +OUTPUT 1 +WHERE [Id] = @p1; +""", + // + """ +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_datetime() + { + await base.Edit_single_property_datetime(); + + AssertSql( + """ +@p0='3000-01-01T12:34:56' (Nullable = false) (Size = 4000) +@p1='3000-01-01T12:34:56' (Nullable = false) (Size = 4000) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestDateTime', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestDateTime', @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_datetimeoffset() + { + await base.Edit_single_property_datetimeoffset(); + + AssertSql( + """ +@p0='3000-01-01T12:34:56-04:00' (Nullable = false) (Size = 4000) +@p1='3000-01-01T12:34:56-04:00' (Nullable = false) (Size = 4000) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestDateTimeOffset', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestDateTimeOffset', @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_decimal() + { + // TODO:SQLJSON Cannot insert decimal (See DecimalParameters.cs) + await Assert.ThrowsAsync(() => base.Edit_single_property_decimal()); + + AssertSql( + """ +@p0='-13579.01' (Precision = 18) (Scale = 3) +@p1='-13579.01' (Precision = 18) (Scale = 3) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestDecimal', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestDecimal', @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_double() + { + await base.Edit_single_property_double(); + + AssertSql( + """ +@p0='-1.23579' +@p1='-1.23579' +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestDouble', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestDouble', @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_guid() + { + await base.Edit_single_property_guid(); + + AssertSql( + """ +@p0='12345678-1234-4321-5555-987654321000' (Nullable = false) (Size = 4000) +@p1='12345678-1234-4321-5555-987654321000' (Nullable = false) (Size = 4000) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestGuid', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestGuid', @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_int16() + { + await base.Edit_single_property_int16(); + + AssertSql( + """ +@p0='-3234' +@p1='-3234' +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestInt16', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestInt16', @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_int32() + { + await base.Edit_single_property_int32(); + + AssertSql( + """ +@p0='-3234' +@p1='-3234' +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestInt32', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestInt32', @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_int64() + { + await base.Edit_single_property_int64(); + + AssertSql( + """ +@p0='-3234' +@p1='-3234' +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestInt64', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestInt64', @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_signed_byte() + { + await base.Edit_single_property_signed_byte(); + + AssertSql( + """ +@p0='-108' +@p1='-108' +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestSignedByte', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestSignedByte', @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_single() + { + await base.Edit_single_property_single(); + + AssertSql( + """ +@p0='-7.234' +@p1='-7.234' +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestSingle', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestSingle', @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_timespan() + { + await base.Edit_single_property_timespan(); + + AssertSql( + """ +@p0='10:01:01.007' (Nullable = false) (Size = 4000) +@p1='10:01:01.007' (Nullable = false) (Size = 4000) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestTimeSpan', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestTimeSpan', @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_uint16() + { + await base.Edit_single_property_uint16(); + + AssertSql( + """ +@p0='1534' +@p1='1534' +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestUnsignedInt16', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestUnsignedInt16', @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_uint32() + { + await base.Edit_single_property_uint32(); + + AssertSql( + """ +@p0='1237775789' +@p1='1237775789' +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestUnsignedInt32', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestUnsignedInt32', @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_uint64() + { + // TODO:SQLJSON Cannot insert decimal (See DecimalParameters.cs) + await Assert.ThrowsAsync(() => base.Edit_single_property_uint64()); + + AssertSql( + """ +@p0='1234555555123456789' (Precision = 20) +@p1='1234555555123456789' (Precision = 20) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestUnsignedInt64', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestUnsignedInt64', @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_dateonly() + { + await base.Edit_single_property_dateonly(); + + AssertSql( + """ +@p0='2000-02-04' (Nullable = false) (Size = 4000) +@p1='1023-01-01' (Nullable = false) (Size = 4000) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestDateOnly', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestDateOnly', @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_string() + { + // TODO:SQLJSON (See JsonTypeToFunction.cs) + Assert.Equal( + "Argument data type json is invalid for argument 3 of json_modify function.", + (await Assert.ThrowsAsync( + () => base.Edit_single_property_collection_of_string())).InnerException?.Message); + + AssertSql( + """ +@p0='["1024","2048"]' (Nullable = false) (Size = 15) +@p1='["999","997"]' (Nullable = false) (Size = 13) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesBasic] SET [OwnedCollectionRoot] = JSON_MODIFY([OwnedCollectionRoot], 'strict $[1].Names', JSON_QUERY(@p0)), [OwnedReferenceRoot] = JSON_MODIFY([OwnedReferenceRoot], 'strict $.Names', JSON_QUERY(@p1)) +OUTPUT 1 +WHERE [Id] = @p2; +"""); + } + + public override async Task Edit_single_property_nullable_int32() + { + await base.Edit_single_property_nullable_int32(); + + AssertSql( + """ +@p0='122354' +@p1='64528' +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestNullableInt32', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestNullableInt32', @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_nullable_int32_set_to_null() + { + await base.Edit_single_property_nullable_int32_set_to_null(); + + AssertSql( + """ +@p0=NULL (Nullable = false) (DbType = Int32) +@p1=NULL (Nullable = false) (DbType = Int32) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestNullableInt32', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestNullableInt32', @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_enum() + { + await base.Edit_single_property_enum(); + + AssertSql( + """ +@p0='-3' +@p1='-3' +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestEnum', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestEnum', @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_enum_with_int_converter() + { + await base.Edit_single_property_enum_with_int_converter(); + + AssertSql( + """ +@p0='-3' +@p1='-3' +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestEnumWithIntConverter', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestEnumWithIntConverter', @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_nullable_enum() + { + await base.Edit_single_property_nullable_enum(); + + AssertSql( + """ +@p0='-3' +@p1='-3' +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestEnum', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestEnum', @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_nullable_enum_set_to_null() + { + await base.Edit_single_property_nullable_enum_set_to_null(); + + AssertSql( + """ +@p0=NULL (Nullable = false) (DbType = Int32) +@p1=NULL (Nullable = false) (DbType = Int32) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestNullableEnum', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestNullableEnum', @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_nullable_enum_with_int_converter() + { + await base.Edit_single_property_nullable_enum_with_int_converter(); + + AssertSql( + """ +@p0='-1' +@p1='-3' +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestNullableEnumWithIntConverter', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestNullableEnumWithIntConverter', @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_nullable_enum_with_int_converter_set_to_null() + { + await base.Edit_single_property_nullable_enum_with_int_converter_set_to_null(); + + AssertSql( + """ +@p0=NULL (Nullable = false) (DbType = Int32) +@p1=NULL (Nullable = false) (DbType = Int32) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestNullableEnumWithIntConverter', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestNullableEnumWithIntConverter', @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_nullable_enum_with_converter_that_handles_nulls() + { + await base.Edit_single_property_nullable_enum_with_converter_that_handles_nulls(); + + AssertSql( + """ +@p0='Three' (Nullable = false) (Size = 4000) +@p1='One' (Nullable = false) (Size = 4000) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestNullableEnumWithConverterThatHandlesNulls', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestNullableEnumWithConverterThatHandlesNulls', @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_nullable_enum_with_converter_that_handles_nulls_set_to_null() + { + await base.Edit_single_property_nullable_enum_with_converter_that_handles_nulls_set_to_null(); + + AssertSql( + """ +@p0='Null' (Nullable = false) (Size = 4000) +@p1='Null' (Nullable = false) (Size = 4000) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestNullableEnumWithConverterThatHandlesNulls', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestNullableEnumWithConverterThatHandlesNulls', @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_two_properties_on_same_entity_updates_the_entire_entity() + { + // TODO:SQLJSON (See JsonTypeToFunction.cs) + Assert.Equal( + "Argument data type json is invalid for argument 3 of json_modify function.", + (await Assert.ThrowsAsync( + () => base.Edit_two_properties_on_same_entity_updates_the_entire_entity())).InnerException?.Message); + + AssertSql( + """ +@p0='{"TestBoolean":false,"TestBooleanCollection":[true,false],"TestByte":25,"TestByteArray":"","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":-1,"TestEnumCollection":[-1,-3,-7],"TestEnumWithIntConverter":2,"TestEnumWithIntConverterCollection":[-1,-3,-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":-1,"TestNullableEnumCollection":[-1,null,-3,-7],"TestNullableEnumWithConverterThatHandlesNulls":"Two","TestNullableEnumWithConverterThatHandlesNullsCollection":[-1,null,-7],"TestNullableEnumWithIntConverter":-3,"TestNullableEnumWithIntConverterCollection":[-1,null,-3,-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 = 2158) +@p1='{"TestBoolean":true,"TestBooleanCollection":[true,false],"TestByte":255,"TestByteArray":"AQID","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":-1,"TestEnumCollection":[-1,-3,-7],"TestEnumWithIntConverter":2,"TestEnumWithIntConverterCollection":[-1,-3,-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":-1,"TestNullableEnumCollection":[-1,null,-3,-7],"TestNullableEnumWithConverterThatHandlesNulls":"Three","TestNullableEnumWithConverterThatHandlesNullsCollection":[-1,null,-7],"TestNullableEnumWithIntConverter":2,"TestNullableEnumWithIntConverterCollection":[-1,null,-3,-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 = 2192) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0]', JSON_QUERY(@p0)), [Reference] = @p1 +OUTPUT 1 +WHERE [Id] = @p2; +"""); + } + + public override async Task Edit_a_scalar_property_and_reference_navigation_on_the_same_entity() + { + // TODO:SQLJSON (See JsonTypeToFunction.cs) + Assert.Equal( + "Argument data type json is invalid for argument 3 of json_modify function.", + (await Assert.ThrowsAsync( + () => base.Edit_a_scalar_property_and_reference_navigation_on_the_same_entity())).InnerException?.Message); + + AssertSql( + """ +SELECT [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +""", + // + """ +@p0='{"Date":"2100-01-01T00:00:00","Enum":-1,"Enums":[-1,-1,2],"Fraction":123.532,"NullableEnum":null,"NullableEnums":[null,-1,2],"OwnedCollectionLeaf":[{"SomethingSomething":"e1_r_r_c1"},{"SomethingSomething":"e1_r_r_c2"}],"OwnedReferenceLeaf":null}' (Nullable = false) (Size = 245) +@p1='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesBasic] SET [OwnedReferenceRoot] = JSON_MODIFY([OwnedReferenceRoot], 'strict $.OwnedReferenceBranch', JSON_QUERY(@p0)) +OUTPUT 1 +WHERE [Id] = @p1; +"""); + } + + public override async Task Edit_a_scalar_property_and_collection_navigation_on_the_same_entity() + { + // TODO:SQLJSON (See JsonTypeToFunction.cs) + Assert.Equal( + "Argument data type json is invalid for argument 3 of json_modify function.", + (await Assert.ThrowsAsync( + () => base.Edit_a_scalar_property_and_collection_navigation_on_the_same_entity())).InnerException?.Message); + + AssertSql( + """ +SELECT [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +""", + // + """ +@p0='{"Date":"2100-01-01T00:00:00","Enum":-1,"Enums":[-1,-1,2],"Fraction":123.532,"NullableEnum":null,"NullableEnums":[null,-1,2],"OwnedCollectionLeaf":null,"OwnedReferenceLeaf":{"SomethingSomething":"e1_r_r_r"}}' (Nullable = false) (Size = 207) +@p1='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesBasic] SET [OwnedReferenceRoot] = JSON_MODIFY([OwnedReferenceRoot], 'strict $.OwnedReferenceBranch', JSON_QUERY(@p0)) +OUTPUT 1 +WHERE [Id] = @p1; +"""); + } + + public override async Task Edit_a_scalar_property_and_another_property_behind_reference_navigation_on_the_same_entity() + { + // TODO:SQLJSON (See JsonTypeToFunction.cs) + Assert.Equal( + "Argument data type json is invalid for argument 3 of json_modify function.", + (await Assert.ThrowsAsync( + () => base.Edit_a_scalar_property_and_another_property_behind_reference_navigation_on_the_same_entity())) + .InnerException?.Message); + + AssertSql( + """ +@p0='{"Date":"2100-01-01T00:00:00","Enum":-1,"Enums":[-1,-1,2],"Fraction":523.532,"NullableEnum":null,"NullableEnums":[null,-1,2],"OwnedCollectionLeaf":[{"SomethingSomething":"e1_r_r_c1"},{"SomethingSomething":"e1_r_r_c2"}],"OwnedReferenceLeaf":{"SomethingSomething":"edit"}}' (Nullable = false) (Size = 270) +@p1='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesBasic] SET [OwnedReferenceRoot] = JSON_MODIFY([OwnedReferenceRoot], 'strict $.OwnedReferenceBranch', JSON_QUERY(@p0)) +OUTPUT 1 +WHERE [Id] = @p1; +"""); + } + + public override async Task Edit_single_property_with_converter_bool_to_int_zero_one() + { + await base.Edit_single_property_with_converter_bool_to_int_zero_one(); + + AssertSql( + """ +@p0='0' +@p1='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesConverters] SET [Reference] = JSON_MODIFY([Reference], 'strict $.BoolConvertedToIntZeroOne', @p0) +OUTPUT 1 +WHERE [Id] = @p1; +""", + // + """ +SELECT TOP(2) [j].[Id], [j].[Reference] +FROM [JsonEntitiesConverters] AS [j] +WHERE [j].[Id] = 1 +"""); + } + + public override async Task Edit_single_property_with_converter_bool_to_string_True_False() + { + await base.Edit_single_property_with_converter_bool_to_string_True_False(); + + AssertSql( + """ +@p0='True' (Nullable = false) (Size = 4000) +@p1='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesConverters] SET [Reference] = JSON_MODIFY([Reference], 'strict $.BoolConvertedToStringTrueFalse', @p0) +OUTPUT 1 +WHERE [Id] = @p1; +""", + // + """ +SELECT TOP(2) [j].[Id], [j].[Reference] +FROM [JsonEntitiesConverters] AS [j] +WHERE [j].[Id] = 1 +"""); + } + + public override async Task Edit_single_property_with_converter_bool_to_string_Y_N() + { + await base.Edit_single_property_with_converter_bool_to_string_Y_N(); + + AssertSql( + """ +@p0='N' (Nullable = false) (Size = 4000) +@p1='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesConverters] SET [Reference] = JSON_MODIFY([Reference], 'strict $.BoolConvertedToStringYN', @p0) +OUTPUT 1 +WHERE [Id] = @p1; +""", + // + """ +SELECT TOP(2) [j].[Id], [j].[Reference] +FROM [JsonEntitiesConverters] AS [j] +WHERE [j].[Id] = 1 +"""); + } + + public override async Task Edit_single_property_with_converter_int_zero_one_to_bool() + { + await base.Edit_single_property_with_converter_int_zero_one_to_bool(); + + AssertSql( + """ +@p0='True' +@p1='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesConverters] SET [Reference] = JSON_MODIFY([Reference], 'strict $.IntZeroOneConvertedToBool', @p0) +OUTPUT 1 +WHERE [Id] = @p1; +""", + // + """ +SELECT TOP(2) [j].[Id], [j].[Reference] +FROM [JsonEntitiesConverters] AS [j] +WHERE [j].[Id] = 1 +"""); + } + + [ConditionalFact] + public override async Task Edit_single_property_with_converter_string_True_False_to_bool() + { + await base.Edit_single_property_with_converter_string_True_False_to_bool(); + + AssertSql( + """ +@p0='False' +@p1='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesConverters] SET [Reference] = JSON_MODIFY([Reference], 'strict $.StringTrueFalseConvertedToBool', @p0) +OUTPUT 1 +WHERE [Id] = @p1; +""", + // + """ +SELECT TOP(2) [j].[Id], [j].[Reference] +FROM [JsonEntitiesConverters] AS [j] +WHERE [j].[Id] = 1 +"""); + } + + [ConditionalFact] + public override async Task Edit_single_property_with_converter_string_Y_N_to_bool() + { + await base.Edit_single_property_with_converter_string_Y_N_to_bool(); + + AssertSql( + """ +@p0='True' +@p1='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesConverters] SET [Reference] = JSON_MODIFY([Reference], 'strict $.StringYNConvertedToBool', @p0) +OUTPUT 1 +WHERE [Id] = @p1; +""", + // + """ +SELECT TOP(2) [j].[Id], [j].[Reference] +FROM [JsonEntitiesConverters] AS [j] +WHERE [j].[Id] = 1 +"""); + } + + public override async Task Edit_single_property_collection_of_numeric() + { + // TODO:SQLJSON (See JsonTypeToFunction.cs) + Assert.Equal( + "Argument data type json is invalid for argument 3 of json_modify function.", + (await Assert.ThrowsAsync( + () => base.Edit_single_property_collection_of_numeric())).InnerException?.Message); + + AssertSql( + """ +@p0='[1024,2048]' (Nullable = false) (Size = 11) +@p1='[999,997]' (Nullable = false) (Size = 9) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesBasic] SET [OwnedCollectionRoot] = JSON_MODIFY([OwnedCollectionRoot], 'strict $[1].Numbers', JSON_QUERY(@p0)), [OwnedReferenceRoot] = JSON_MODIFY([OwnedReferenceRoot], 'strict $.Numbers', JSON_QUERY(@p1)) +OUTPUT 1 +WHERE [Id] = @p2; +"""); + } + + public override async Task Edit_single_property_collection_of_bool() + { + // TODO:SQLJSON (See JsonTypeToFunction.cs) + Assert.Equal( + "Argument data type json is invalid for argument 3 of json_modify function.", + (await Assert.ThrowsAsync( + () => base.Edit_single_property_collection_of_bool())).InnerException?.Message); + + AssertSql( + """ +@p0='[true,true,true,false]' (Nullable = false) (Size = 22) +@p1='[true,true,false]' (Nullable = false) (Size = 17) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestBooleanCollection', JSON_QUERY(@p0)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestBooleanCollection', JSON_QUERY(@p1)) +OUTPUT 1 +WHERE [Id] = @p2; +"""); + } + + public override async Task Edit_single_property_collection_of_byte() + { + await base.Edit_single_property_collection_of_byte(); + + AssertSql( + """ +@p0='Dg==' (Nullable = false) (Size = 4000) +@p1='GRo=' (Nullable = false) (Size = 4000) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestByteCollection', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestByteCollection', @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 +"""); + } + + [ConditionalFact(Skip = "TODO:SQLJSON Hangs (See InsertsHang.cs")] + public override async Task Edit_single_property_collection_of_char() + { + await base.Edit_single_property_collection_of_char(); + + AssertSql( + """ +@p0='["A","B","\u0022","\u0000"]' (Nullable = false) (Size = 4000) +@p1='["E","F","C","\u00F6","r","E","\u0022","\\"]' (Nullable = false) (Size = 4000) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestCharacterCollection', JSON_QUERY(@p0)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestCharacterCollection', 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_datetime() + { + // TODO:SQLJSON (See JsonTypeToFunction.cs) + Assert.Equal( + "Argument data type json is invalid for argument 3 of json_modify function.", + (await Assert.ThrowsAsync( + () => base.Edit_single_property_collection_of_datetime())).InnerException?.Message); + + AssertSql( + """ +@p0='["2000-01-01T12:34:56","3000-01-01T12:34:56","3000-01-01T12:34:56"]' (Nullable = false) (Size = 67) +@p1='["2000-01-01T12:34:56","3000-01-01T12:34:56","3000-01-01T12:34:56"]' (Nullable = false) (Size = 67) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestDateTimeCollection', JSON_QUERY(@p0)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestDateTimeCollection', JSON_QUERY(@p1)) +OUTPUT 1 +WHERE [Id] = @p2; +"""); + } + + public override async Task Edit_single_property_collection_of_datetimeoffset() + { + // TODO:SQLJSON (See JsonTypeToFunction.cs) + Assert.Equal( + "Argument data type json is invalid for argument 3 of json_modify function.", + (await Assert.ThrowsAsync( + () => base.Edit_single_property_collection_of_datetimeoffset())).InnerException?.Message); + + AssertSql( + """ +@p0='["3000-01-01T12:34:56-04:00"]' (Nullable = false) (Size = 29) +@p1='["3000-01-01T12:34:56-04:00"]' (Nullable = false) (Size = 29) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestDateTimeOffsetCollection', JSON_QUERY(@p0)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestDateTimeOffsetCollection', JSON_QUERY(@p1)) +OUTPUT 1 +WHERE [Id] = @p2; +"""); + } + + public override async Task Edit_single_property_collection_of_decimal() + { + // TODO:SQLJSON (See JsonTypeToFunction.cs) + Assert.Equal( + "Argument data type json is invalid for argument 3 of json_modify function.", + (await Assert.ThrowsAsync( + () => base.Edit_single_property_collection_of_decimal())).InnerException?.Message); + + AssertSql( + """ +@p0='[-13579.01]' (Nullable = false) (Size = 11) +@p1='[-13579.01]' (Nullable = false) (Size = 11) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestDecimalCollection', JSON_QUERY(@p0)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestDecimalCollection', JSON_QUERY(@p1)) +OUTPUT 1 +WHERE [Id] = @p2; +"""); + } + + public override async Task Edit_single_property_collection_of_double() + { + // TODO:SQLJSON (See JsonTypeToFunction.cs) + Assert.Equal( + "Argument data type json is invalid for argument 3 of json_modify function.", + (await Assert.ThrowsAsync( + () => base.Edit_single_property_collection_of_double())).InnerException?.Message); + + AssertSql( + """ +@p0='[-1.23456789,1.23456789,0,-1.23579]' (Nullable = false) (Size = 35) +@p1='[-1.23456789,1.23456789,0,-1.23579]' (Nullable = false) (Size = 35) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestDoubleCollection', JSON_QUERY(@p0)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestDoubleCollection', JSON_QUERY(@p1)) +OUTPUT 1 +WHERE [Id] = @p2; +"""); + } + + public override async Task Edit_single_property_collection_of_guid() + { + // TODO:SQLJSON (See JsonTypeToFunction.cs) + Assert.Equal( + "Argument data type json is invalid for argument 3 of json_modify function.", + (await Assert.ThrowsAsync( + () => base.Edit_single_property_collection_of_guid())).InnerException?.Message); + + AssertSql( + """ +@p0='["12345678-1234-4321-5555-987654321000"]' (Nullable = false) (Size = 40) +@p1='["12345678-1234-4321-5555-987654321000"]' (Nullable = false) (Size = 40) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestGuidCollection', JSON_QUERY(@p0)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestGuidCollection', JSON_QUERY(@p1)) +OUTPUT 1 +WHERE [Id] = @p2; +"""); + } + + public override async Task Edit_single_property_collection_of_int16() + { + // TODO:SQLJSON (See JsonTypeToFunction.cs) + Assert.Equal( + "Argument data type json is invalid for argument 3 of json_modify function.", + (await Assert.ThrowsAsync( + () => base.Edit_single_property_collection_of_int16())).InnerException?.Message); + + AssertSql( + """ +@p0='[-3234]' (Nullable = false) (Size = 7) +@p1='[-3234]' (Nullable = false) (Size = 7) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestInt16Collection', JSON_QUERY(@p0)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestInt16Collection', JSON_QUERY(@p1)) +OUTPUT 1 +WHERE [Id] = @p2; +"""); + } + + public override async Task Edit_single_property_collection_of_int32() + { + // TODO:SQLJSON (See JsonTypeToFunction.cs) + Assert.Equal( + "Argument data type json is invalid for argument 3 of json_modify function.", + (await Assert.ThrowsAsync( + () => base.Edit_single_property_collection_of_int32())).InnerException?.Message); + + AssertSql( + """ +@p0='[-3234]' (Nullable = false) (Size = 7) +@p1='[-3234]' (Nullable = false) (Size = 7) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestInt32Collection', JSON_QUERY(@p0)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestInt32Collection', JSON_QUERY(@p1)) +OUTPUT 1 +WHERE [Id] = @p2; +"""); + } + + public override async Task Edit_single_property_collection_of_int64() + { + // TODO:SQLJSON (See JsonTypeToFunction.cs) + Assert.Equal( + "Argument data type json is invalid for argument 3 of json_modify function.", + (await Assert.ThrowsAsync( + () => base.Edit_single_property_collection_of_int64())).InnerException?.Message); + + AssertSql( + """ +@p0='[]' (Nullable = false) (Size = 2) +@p1='[]' (Nullable = false) (Size = 2) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestInt64Collection', JSON_QUERY(@p0)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestInt64Collection', JSON_QUERY(@p1)) +OUTPUT 1 +WHERE [Id] = @p2; +"""); + } + + public override async Task Edit_single_property_collection_of_signed_byte() + { + // TODO:SQLJSON (See JsonTypeToFunction.cs) + Assert.Equal( + "Argument data type json is invalid for argument 3 of json_modify function.", + (await Assert.ThrowsAsync( + () => base.Edit_single_property_collection_of_signed_byte())).InnerException?.Message); + + AssertSql( + """ +@p0='[-108]' (Nullable = false) (Size = 6) +@p1='[-108]' (Nullable = false) (Size = 6) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestSignedByteCollection', JSON_QUERY(@p0)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestSignedByteCollection', JSON_QUERY(@p1)) +OUTPUT 1 +WHERE [Id] = @p2; +"""); + } + + public override async Task Edit_single_property_collection_of_single() + { + // TODO:SQLJSON (See JsonTypeToFunction.cs) + Assert.Equal( + "Argument data type json is invalid for argument 3 of json_modify function.", + (await Assert.ThrowsAsync( + () => base.Edit_single_property_collection_of_single())).InnerException?.Message); + + AssertSql( + """ +@p0='[-1.234,-1.234]' (Nullable = false) (Size = 15) +@p1='[0,-1.234]' (Nullable = false) (Size = 10) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestSingleCollection', JSON_QUERY(@p0)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestSingleCollection', JSON_QUERY(@p1)) +OUTPUT 1 +WHERE [Id] = @p2; +"""); + } + + public override async Task Edit_single_property_collection_of_timespan() + { + // TODO:SQLJSON (See JsonTypeToFunction.cs) + Assert.Equal( + "Argument data type json is invalid for argument 3 of json_modify function.", + (await Assert.ThrowsAsync( + () => base.Edit_single_property_collection_of_timespan())).InnerException?.Message); + + AssertSql( + """ +@p0='["10:09:08.007","10:01:01.007"]' (Nullable = false) (Size = 31) +@p1='["10:01:01.007","-9:50:51.993"]' (Nullable = false) (Size = 31) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestTimeSpanCollection', JSON_QUERY(@p0)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestTimeSpanCollection', JSON_QUERY(@p1)) +OUTPUT 1 +WHERE [Id] = @p2; +"""); + } + + public override async Task Edit_single_property_collection_of_dateonly() + { + // TODO:SQLJSON (See JsonTypeToFunction.cs) + Assert.Equal( + "Argument data type json is invalid for argument 3 of json_modify function.", + (await Assert.ThrowsAsync( + () => base.Edit_single_property_collection_of_dateonly())).InnerException?.Message); + + 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' + +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; +"""); + } + + public override async Task Edit_single_property_collection_of_timeonly() + { + // TODO:SQLJSON (See JsonTypeToFunction.cs) + Assert.Equal( + "Argument data type json is invalid for argument 3 of json_modify function.", + (await Assert.ThrowsAsync( + () => base.Edit_single_property_collection_of_timeonly())).InnerException?.Message); + + 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' + +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; +"""); + } + + public override async Task Edit_single_property_collection_of_uint16() + { + // TODO:SQLJSON (See JsonTypeToFunction.cs) + Assert.Equal( + "Argument data type json is invalid for argument 3 of json_modify function.", + (await Assert.ThrowsAsync( + () => base.Edit_single_property_collection_of_uint16())).InnerException?.Message); + + AssertSql( + """ +@p0='[1534]' (Nullable = false) (Size = 6) +@p1='[1534]' (Nullable = false) (Size = 6) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestUnsignedInt16Collection', JSON_QUERY(@p0)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestUnsignedInt16Collection', JSON_QUERY(@p1)) +OUTPUT 1 +WHERE [Id] = @p2; +"""); + } + + public override async Task Edit_single_property_collection_of_uint32() + { + // TODO:SQLJSON (See JsonTypeToFunction.cs) + Assert.Equal( + "Argument data type json is invalid for argument 3 of json_modify function.", + (await Assert.ThrowsAsync( + () => base.Edit_single_property_collection_of_uint32())).InnerException?.Message); + + AssertSql( + """ +@p0='[1237775789]' (Nullable = false) (Size = 12) +@p1='[1237775789]' (Nullable = false) (Size = 12) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestUnsignedInt32Collection', JSON_QUERY(@p0)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestUnsignedInt32Collection', JSON_QUERY(@p1)) +OUTPUT 1 +WHERE [Id] = @p2; +"""); + } + + public override async Task Edit_single_property_collection_of_uint64() + { + // TODO:SQLJSON (See JsonTypeToFunction.cs) + Assert.Equal( + "Argument data type json is invalid for argument 3 of json_modify function.", + (await Assert.ThrowsAsync( + () => base.Edit_single_property_collection_of_uint64())).InnerException?.Message); + + AssertSql( + """ +@p0='[1234555555123456789]' (Nullable = false) (Size = 21) +@p1='[1234555555123456789]' (Nullable = false) (Size = 21) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestUnsignedInt64Collection', JSON_QUERY(@p0)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestUnsignedInt64Collection', JSON_QUERY(@p1)) +OUTPUT 1 +WHERE [Id] = @p2; +"""); + } + + public override async Task Edit_single_property_collection_of_nullable_int32() + { + // TODO:SQLJSON (See JsonTypeToFunction.cs) + Assert.Equal( + "Argument data type json is invalid for argument 3 of json_modify function.", + (await Assert.ThrowsAsync( + () => base.Edit_single_property_collection_of_nullable_int32())).InnerException?.Message); + + AssertSql( + """ +@p0='[null,77]' (Nullable = false) (Size = 9) +@p1='[null,-2147483648,0,null,2147483647,null,77,null]' (Nullable = false) (Size = 49) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestNullableInt32Collection', JSON_QUERY(@p0)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestNullableInt32Collection', JSON_QUERY(@p1)) +OUTPUT 1 +WHERE [Id] = @p2; +"""); + } + + public override async Task Edit_single_property_collection_of_nullable_int32_set_to_null() + { + await base.Edit_single_property_collection_of_nullable_int32_set_to_null(); + + AssertSql( + """ +@p0=NULL (Nullable = false) (Size = 4000) +@p1=NULL (Nullable = false) (Size = 4000) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestNullableInt32Collection', JSON_QUERY(@p0)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestNullableInt32Collection', 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_enum() + { + // TODO:SQLJSON (See JsonTypeToFunction.cs) + Assert.Equal( + "Argument data type json is invalid for argument 3 of json_modify function.", + (await Assert.ThrowsAsync( + () => base.Edit_single_property_collection_of_enum())).InnerException?.Message); + + AssertSql( + """ +@p0='[-3]' (Nullable = false) (Size = 4) +@p1='[-3]' (Nullable = false) (Size = 4) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestEnumCollection', JSON_QUERY(@p0)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestEnumCollection', JSON_QUERY(@p1)) +OUTPUT 1 +WHERE [Id] = @p2; +"""); + } + + public override async Task Edit_single_property_collection_of_enum_with_int_converter() + { + // TODO:SQLJSON (See JsonTypeToFunction.cs) + Assert.Equal( + "Argument data type json is invalid for argument 3 of json_modify function.", + (await Assert.ThrowsAsync( + () => base.Edit_single_property_collection_of_enum_with_int_converter())).InnerException?.Message); + + AssertSql( + """ +@p0='[-3]' (Nullable = false) (Size = 4) +@p1='[-3]' (Nullable = false) (Size = 4) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestEnumWithIntConverterCollection', JSON_QUERY(@p0)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestEnumWithIntConverterCollection', JSON_QUERY(@p1)) +OUTPUT 1 +WHERE [Id] = @p2; +"""); + } + + public override async Task Edit_single_property_collection_of_nullable_enum() + { + // TODO:SQLJSON (See JsonTypeToFunction.cs) + Assert.Equal( + "Argument data type json is invalid for argument 3 of json_modify function.", + (await Assert.ThrowsAsync( + () => base.Edit_single_property_collection_of_nullable_enum())).InnerException?.Message); + + AssertSql( + """ +@p0='[-3]' (Nullable = false) (Size = 4) +@p1='[-3]' (Nullable = false) (Size = 4) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestEnumCollection', JSON_QUERY(@p0)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestEnumCollection', JSON_QUERY(@p1)) +OUTPUT 1 +WHERE [Id] = @p2; +"""); + } + + public override async Task Edit_single_property_collection_of_nullable_enum_set_to_null() + { + await base.Edit_single_property_collection_of_nullable_enum_set_to_null(); + + AssertSql( + """ +@p0=NULL (Nullable = false) (Size = 4000) +@p1=NULL (Nullable = false) (Size = 4000) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestNullableEnumCollection', JSON_QUERY(@p0)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestNullableEnumCollection', 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_nullable_enum_with_int_converter() + { + // TODO:SQLJSON (See JsonTypeToFunction.cs) + Assert.Equal( + "Argument data type json is invalid for argument 3 of json_modify function.", + (await Assert.ThrowsAsync( + () => base.Edit_single_property_collection_of_nullable_enum_with_int_converter())).InnerException?.Message); + + AssertSql( + """ +@p0='[-1,null,-7,2]' (Nullable = false) (Size = 14) +@p1='[-1,-3,-7,2]' (Nullable = false) (Size = 12) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestNullableEnumWithIntConverterCollection', JSON_QUERY(@p0)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestNullableEnumWithIntConverterCollection', JSON_QUERY(@p1)) +OUTPUT 1 +WHERE [Id] = @p2; +"""); + } + + public override async Task Edit_single_property_collection_of_nullable_enum_with_int_converter_set_to_null() + { + await base.Edit_single_property_collection_of_nullable_enum_with_int_converter_set_to_null(); + + AssertSql( + """ +@p0=NULL (Nullable = false) (Size = 4000) +@p1=NULL (Nullable = false) (Size = 4000) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestNullableEnumWithIntConverterCollection', JSON_QUERY(@p0)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestNullableEnumWithIntConverterCollection', 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_nullable_enum_with_converter_that_handles_nulls() + { + // TODO:SQLJSON (See JsonTypeToFunction.cs) + Assert.Equal( + "Argument data type json is invalid for argument 3 of json_modify function.", + (await Assert.ThrowsAsync( + () => base.Edit_single_property_collection_of_nullable_enum_with_converter_that_handles_nulls())).InnerException?.Message); + + AssertSql( + """ +@p0='[-3]' (Nullable = false) (Size = 4) +@p1='[-1]' (Nullable = false) (Size = 4) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestNullableEnumWithConverterThatHandlesNullsCollection', JSON_QUERY(@p0)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestNullableEnumWithConverterThatHandlesNullsCollection', JSON_QUERY(@p1)) +OUTPUT 1 +WHERE [Id] = @p2; +"""); + } + + public override async Task Edit_single_property_collection_of_nullable_enum_with_converter_that_handles_nulls_set_to_null() + { + await base.Edit_single_property_collection_of_nullable_enum_with_converter_that_handles_nulls_set_to_null(); + + AssertSql( + """ +@p0=NULL (Nullable = false) (Size = 4000) +@p1=NULL (Nullable = false) (Size = 4000) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestNullableEnumWithConverterThatHandlesNullsCollection', JSON_QUERY(@p0)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestNullableEnumWithConverterThatHandlesNullsCollection', 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 Task Add_and_update_nested_optional_owned_collection_to_JSON(bool? value) + // TODO:SQLJSON Updates to null fail (See UpdateToNull.cs) + => Assert.ThrowsAsync( + () => base.Add_and_update_nested_optional_owned_collection_to_JSON(value)); + + public override Task Add_and_update_top_level_optional_owned_collection_to_JSON(bool? value) + // TODO:SQLJSON Updates to null fail (See UpdateToNull.cs) + => Assert.ThrowsAsync( + () => base.Add_and_update_top_level_optional_owned_collection_to_JSON(value)); + + [ConditionalTheory(Skip = "TODO:SQLJSON Hangs (See InsertsHang.cs")] + public override async Task Add_and_update_nested_optional_primitive_collection(bool? value) + { + await base.Add_and_update_nested_optional_primitive_collection(value); + + var characterCollection = value switch + { + true => "[\"A\"]", + false => "[]", + _ => "null" + }; + + var parameterSize = value switch + { + true => "1558", + false => "1555", + _ => "1557" + }; + + var updateParameter = value switch + { + true => "NULL", + false => "'[\"Z\"]'", + _ => "'[]'" + }; + + AssertSql( + @"@p0='[{""TestBoolean"":false,""TestBooleanCollection"":[],""TestByte"":0,""TestByteArray"":null,""TestByteCollection"":null,""TestCharacter"":""\u0000"",""TestCharacterCollection"":" + + characterCollection + + @",""TestDateOnly"":""0001-01-01"",""TestDateOnlyCollection"":[],""TestDateTime"":""0001-01-01T00:00:00"",""TestDateTimeCollection"":[],""TestDateTimeOffset"":""0001-01-01T00:00:00+00:00"",""TestDateTimeOffsetCollection"":[],""TestDecimal"":0,""TestDecimalCollection"":[],""TestDefaultString"":null,""TestDefaultStringCollection"":[],""TestDouble"":0,""TestDoubleCollection"":[],""TestEnum"":0,""TestEnumCollection"":[],""TestEnumWithIntConverter"":0,""TestEnumWithIntConverterCollection"":[],""TestGuid"":""00000000-0000-0000-0000-000000000000"",""TestGuidCollection"":[],""TestInt16"":0,""TestInt16Collection"":[],""TestInt32"":0,""TestInt32Collection"":[],""TestInt64"":0,""TestInt64Collection"":[],""TestMaxLengthString"":null,""TestMaxLengthStringCollection"":[],""TestNullableEnum"":null,""TestNullableEnumCollection"":[],""TestNullableEnumWithConverterThatHandlesNulls"":null,""TestNullableEnumWithConverterThatHandlesNullsCollection"":[],""TestNullableEnumWithIntConverter"":null,""TestNullableEnumWithIntConverterCollection"":[],""TestNullableInt32"":null,""TestNullableInt32Collection"":[],""TestSignedByte"":0,""TestSignedByteCollection"":[],""TestSingle"":0,""TestSingleCollection"":[],""TestTimeOnly"":""00:00:00.0000000"",""TestTimeOnlyCollection"":[],""TestTimeSpan"":""0:00:00"",""TestTimeSpanCollection"":[],""TestUnsignedInt16"":0,""TestUnsignedInt16Collection"":[],""TestUnsignedInt32"":0,""TestUnsignedInt32Collection"":[],""TestUnsignedInt64"":0,""TestUnsignedInt64Collection"":[]}]' (Nullable = false) (Size = " + + parameterSize + + @") +@p1='7624' +@p2='[]' (Size = 4000) +@p3=NULL (Size = 8000) (DbType = Binary) +@p4='[]' (Size = 4000) +@p5='[]' (Size = 4000) +@p6='[]' (Size = 4000) +@p7='[]' (Size = 4000) +@p8='[]' (Size = 4000) +@p9='[]' (Size = 4000) +@p10='[]' (Size = 4000) +@p11='[]' (Size = 4000) +@p12='[]' (Nullable = false) (Size = 4000) +@p13='[]' (Size = 4000) +@p14='[]' (Size = 4000) +@p15='[]' (Size = 4000) +@p16='[]' (Size = 4000) +@p17='[]' (Size = 4000) +@p18=NULL (Size = 4000) +@p19='[]' (Size = 4000) +@p20='[]' (Size = 4000) +@p21='[]' (Size = 4000) +@p22='[]' (Size = 4000) +@p23='[]' (Size = 4000) +@p24='[]' (Size = 4000) +@p25='[]' (Size = 4000) +@p26='[]' (Size = 4000) + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +INSERT INTO [JsonEntitiesAllTypes] ([Collection], [Id], [TestBooleanCollection], [TestByteCollection], [TestCharacterCollection], [TestDateTimeCollection], [TestDateTimeOffsetCollection], [TestDecimalCollection], [TestDefaultStringCollection], [TestDoubleCollection], [TestEnumCollection], [TestEnumWithIntConverterCollection], [TestGuidCollection], [TestInt16Collection], [TestInt32Collection], [TestInt64Collection], [TestMaxLengthStringCollection], [TestNullableEnumCollection], [TestNullableEnumWithConverterThatHandlesNullsCollection], [TestNullableEnumWithIntConverterCollection], [TestNullableInt32Collection], [TestSignedByteCollection], [TestSingleCollection], [TestTimeSpanCollection], [TestUnsignedInt16Collection], [TestUnsignedInt32Collection], [TestUnsignedInt64Collection]) +VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9, @p10, @p11, @p12, @p13, @p14, @p15, @p16, @p17, @p18, @p19, @p20, @p21, @p22, @p23, @p24, @p25, @p26);", + // + """ +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] = 7624 +""", + // + "@p0=" + + updateParameter + + @" (Nullable = false) (Size = 4000) +@p1='7624' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestCharacterCollection', JSON_QUERY(@p0)) +OUTPUT 1 +WHERE [Id] = @p1;", + // + """ +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] = 7624 +"""); + } + + // Nested collections are not mapped in the relational model, so there is no data stored in the document for them + public override Task Edit_single_property_collection_of_collection_of_bool() + => Assert.ThrowsAsync(base.Edit_single_property_collection_of_collection_of_bool); + + // Nested collections are not mapped in the relational model, so there is no data stored in the document for them + public override Task Edit_single_property_collection_of_collection_of_char() + => Assert.ThrowsAsync(base.Edit_single_property_collection_of_collection_of_char); + + // Nested collections are not mapped in the relational model, so there is no data stored in the document for them + public override Task Edit_single_property_collection_of_collection_of_double() + => Assert.ThrowsAsync(base.Edit_single_property_collection_of_collection_of_double); + + // Nested collections are not mapped in the relational model, so there is no data stored in the document for them + public override Task Edit_single_property_collection_of_collection_of_int16() + => Assert.ThrowsAsync(base.Edit_single_property_collection_of_collection_of_int16); + + // Nested collections are not mapped in the relational model, so there is no data stored in the document for them + public override Task Edit_single_property_collection_of_collection_of_int32() + => Assert.ThrowsAsync(base.Edit_single_property_collection_of_collection_of_int32); + + // Nested collections are not mapped in the relational model, so there is no data stored in the document for them + public override Task Edit_single_property_collection_of_collection_of_nullable_enum_set_to_null() + => Assert.ThrowsAsync(base.Edit_single_property_collection_of_collection_of_nullable_enum_set_to_null); + + // Nested collections are not mapped in the relational model, so there is no data stored in the document for them + public override Task Edit_single_property_collection_of_collection_of_nullable_enum_with_int_converter() + => Assert.ThrowsAsync( + base.Edit_single_property_collection_of_collection_of_nullable_enum_with_int_converter); + + // Nested collections are not mapped in the relational model, so there is no data stored in the document for them + public override Task Edit_single_property_collection_of_collection_of_nullable_int32() + => Assert.ThrowsAsync(base.Edit_single_property_collection_of_collection_of_nullable_int32); + + // Nested collections are not mapped in the relational model, so there is no data stored in the document for them + public override Task Edit_single_property_collection_of_collection_of_nullable_int32_set_to_null() + => Assert.ThrowsAsync(base.Edit_single_property_collection_of_collection_of_nullable_int32_set_to_null); + + // Nested collections are not mapped in the relational model, so there is no data stored in the document for them + public override Task Edit_single_property_collection_of_collection_of_single() + => Assert.ThrowsAsync(base.Edit_single_property_collection_of_collection_of_single); + + public override async Task Edit_single_property_timeonly() + { + await base.Edit_single_property_timeonly(); + + AssertSql( + """ +@p0='01:01:07.0000000' (Nullable = false) (Size = 4000) +@p1='01:01:07.0000000' (Nullable = false) (Size = 4000) +@p2='1' + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestTimeOnly', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestTimeOnly', @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_relational_collection_of_bool() + { + await base.Edit_single_property_relational_collection_of_bool(); + + AssertSql( + """ +@p1='1' +@p0='[true,true,false]' (Size = 8000) + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [TestBooleanCollection] = @p0 +OUTPUT 1 +WHERE [Id] = @p1; +""", + // + """ +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_relational_collection_of_byte() + { + await base.Edit_single_property_relational_collection_of_byte(); + + AssertSql( + """ +@p1='1' +@p0='[25,26]' (Size = 8000) + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [TestByteCollection] = @p0 +OUTPUT 1 +WHERE [Id] = @p1; +""", + // + """ +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 +"""); + } + + [ConditionalFact(Skip = "TODO:SQLJSON Investigate")] + public override async Task Edit_single_property_relational_collection_of_char() + { + await base.Edit_single_property_relational_collection_of_char(); + + AssertSql(); + } + + public override async Task Edit_single_property_relational_collection_of_datetime() + { + await base.Edit_single_property_relational_collection_of_datetime(); + + AssertSql( + """ +@p1='1' +@p0='["2000-01-01T12:34:56","3000-01-01T12:34:56","3000-01-01T12:34:56"]' (Size = 8000) + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [TestDateTimeCollection] = @p0 +OUTPUT 1 +WHERE [Id] = @p1; +""", + // + """ +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_relational_collection_of_datetimeoffset() + { + await base.Edit_single_property_relational_collection_of_datetimeoffset(); + + AssertSql( + """ +@p1='1' +@p0='["3000-01-01T12:34:56-04:00"]' (Size = 8000) + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [TestDateTimeOffsetCollection] = @p0 +OUTPUT 1 +WHERE [Id] = @p1; +""", + // + """ +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_relational_collection_of_decimal() + { + await base.Edit_single_property_relational_collection_of_decimal(); + + AssertSql( + """ +@p1='1' +@p0='[-13579.01]' (Size = 8000) + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [TestDecimalCollection] = @p0 +OUTPUT 1 +WHERE [Id] = @p1; +""", + // + """ +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_relational_collection_of_double() + { + await base.Edit_single_property_relational_collection_of_double(); + + AssertSql( + """ +@p1='1' +@p0='[-1.23456789,1.23456789,0,-1.23579]' (Size = 8000) + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [TestDoubleCollection] = @p0 +OUTPUT 1 +WHERE [Id] = @p1; +""", + // + """ +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_relational_collection_of_guid() + { + await base.Edit_single_property_relational_collection_of_guid(); + + AssertSql( + """ +@p1='1' +@p0='["12345678-1234-4321-5555-987654321000"]' (Nullable = false) (Size = 8000) + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [TestGuidCollection] = @p0 +OUTPUT 1 +WHERE [Id] = @p1; +""", + // + """ +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_relational_collection_of_int16() + { + await base.Edit_single_property_relational_collection_of_int16(); + + AssertSql( + """ +@p1='1' +@p0='[-3234]' (Size = 8000) + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [TestInt16Collection] = @p0 +OUTPUT 1 +WHERE [Id] = @p1; +""", + // + """ +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_relational_collection_of_int32() + { + await base.Edit_single_property_relational_collection_of_int32(); + + AssertSql( + """ +@p1='1' +@p0='[-3234]' (Size = 8000) + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [TestInt32Collection] = @p0 +OUTPUT 1 +WHERE [Id] = @p1; +""", + // + """ +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_relational_collection_of_int64() + { + await base.Edit_single_property_relational_collection_of_int64(); + + AssertSql( + """ +@p1='1' +@p0='[]' (Size = 8000) + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [TestInt64Collection] = @p0 +OUTPUT 1 +WHERE [Id] = @p1; +""", + // + """ +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_relational_collection_of_signed_byte() + { + await base.Edit_single_property_relational_collection_of_signed_byte(); + + AssertSql( + """ +@p1='1' +@p0='[-108]' (Size = 8000) + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [TestSignedByteCollection] = @p0 +OUTPUT 1 +WHERE [Id] = @p1; +""", + // + """ +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_relational_collection_of_single() + { + await base.Edit_single_property_relational_collection_of_single(); + + AssertSql( + """ +@p1='1' +@p0='[0,-1.234]' (Size = 8000) + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [TestSingleCollection] = @p0 +OUTPUT 1 +WHERE [Id] = @p1; +""", + // + """ +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_relational_collection_of_timespan() + { + await base.Edit_single_property_relational_collection_of_timespan(); + + AssertSql( + """ +@p1='1' +@p0='["10:01:01.007","7:09:08.007"]' (Size = 8000) + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [TestTimeSpanCollection] = @p0 +OUTPUT 1 +WHERE [Id] = @p1; +""", + // + """ +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_relational_collection_of_uint16() + { + await base.Edit_single_property_relational_collection_of_uint16(); + + AssertSql( + """ +@p1='1' +@p0='[1534]' (Size = 8000) + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [TestUnsignedInt16Collection] = @p0 +OUTPUT 1 +WHERE [Id] = @p1; +""", + // + """ +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_relational_collection_of_uint32() + { + await base.Edit_single_property_relational_collection_of_uint32(); + + AssertSql( + """ +@p1='1' +@p0='[1237775789]' (Size = 8000) + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [TestUnsignedInt32Collection] = @p0 +OUTPUT 1 +WHERE [Id] = @p1; +""", + // + """ +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_relational_collection_of_uint64() + { + await base.Edit_single_property_relational_collection_of_uint64(); + + AssertSql( + """ +@p1='1' +@p0='[1234555555123456789]' (Size = 8000) + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [TestUnsignedInt64Collection] = @p0 +OUTPUT 1 +WHERE [Id] = @p1; +""", + // + """ +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_relational_collection_of_nullable_int32() + { + await base.Edit_single_property_relational_collection_of_nullable_int32(); + + AssertSql( + """ +@p1='1' +@p0='[null,-2147483648,0,null,2147483647,null,77,null]' (Size = 8000) + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [TestNullableInt32Collection] = @p0 +OUTPUT 1 +WHERE [Id] = @p1; +""", + // + """ +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_relational_collection_of_nullable_int32_set_to_null() + { + // TODO:SQLJSON Updates to null fail (See UpdateToNull.cs) + await Assert.ThrowsAsync( + () => base.Edit_single_property_relational_collection_of_nullable_int32_set_to_null()); + + AssertSql( + """ +@p1='1' +@p0=NULL (Size = 8000) + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [TestNullableInt32Collection] = @p0 +OUTPUT 1 +WHERE [Id] = @p1; +""", + // + """ +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_relational_collection_of_enum() + { + await base.Edit_single_property_relational_collection_of_enum(); + + AssertSql( + """ +@p1='1' +@p0='[-3]' (Size = 8000) + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [TestEnumCollection] = @p0 +OUTPUT 1 +WHERE [Id] = @p1; +""", + // + """ +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_relational_collection_of_enum_with_int_converter() + { + await base.Edit_single_property_relational_collection_of_enum_with_int_converter(); + + AssertSql( + """ +@p1='1' +@p0='[-3]' (Size = 8000) + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [TestEnumWithIntConverterCollection] = @p0 +OUTPUT 1 +WHERE [Id] = @p1; +""", + // + """ +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_relational_collection_of_nullable_enum() + { + await base.Edit_single_property_relational_collection_of_nullable_enum(); + + AssertSql( + """ +@p1='1' +@p0='[-3]' (Size = 8000) + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [TestEnumCollection] = @p0 +OUTPUT 1 +WHERE [Id] = @p1; +""", + // + """ +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_relational_collection_of_nullable_enum_set_to_null() + { + // TODO:SQLJSON Updates to null fail (See UpdateToNull.cs) + await Assert.ThrowsAsync( + () => base.Edit_single_property_relational_collection_of_nullable_enum_set_to_null()); + + AssertSql( + """ +@p1='1' +@p0=NULL (Size = 8000) + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [TestNullableEnumCollection] = @p0 +OUTPUT 1 +WHERE [Id] = @p1; +""", + // + """ +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_relational_collection_of_nullable_enum_with_int_converter() + { + await base.Edit_single_property_relational_collection_of_nullable_enum_with_int_converter(); + + AssertSql( + """ +@p1='1' +@p0='[-1,-3,-7,2]' (Size = 8000) + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [TestNullableEnumWithIntConverterCollection] = @p0 +OUTPUT 1 +WHERE [Id] = @p1; +""", + // + """ +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_relational_collection_of_nullable_enum_with_int_converter_set_to_null() + { + // TODO:SQLJSON Updates to null fail (See UpdateToNull.cs) + await Assert.ThrowsAsync( + () => base.Edit_single_property_relational_collection_of_nullable_enum_with_int_converter_set_to_null()); + + AssertSql( + """ +@p1='1' +@p0=NULL (Size = 8000) + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [TestNullableEnumWithIntConverterCollection] = @p0 +OUTPUT 1 +WHERE [Id] = @p1; +""", + // + """ +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_relational_collection_of_nullable_enum_with_converter_that_handles_nulls() + { + await base.Edit_single_property_relational_collection_of_nullable_enum_with_converter_that_handles_nulls(); + + AssertSql( + """ +@p1='1' +@p0='[-1]' (Size = 8000) + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [TestNullableEnumWithConverterThatHandlesNullsCollection] = @p0 +OUTPUT 1 +WHERE [Id] = @p1; +""", + // + """ +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_relational_collection_of_nullable_enum_with_converter_that_handles_nulls_set_to_null() + { + // TODO:SQLJSON Updates to null fail (See UpdateToNull.cs) + await Assert.ThrowsAsync( + () => base.Edit_single_property_relational_collection_of_nullable_enum_with_converter_that_handles_nulls_set_to_null()); + + AssertSql( + """ +@p1='1' +@p0=NULL (Size = 8000) + +SET IMPLICIT_TRANSACTIONS OFF; +SET NOCOUNT ON; +UPDATE [JsonEntitiesAllTypes] SET [TestNullableEnumWithConverterThatHandlesNullsCollection] = @p0 +OUTPUT 1 +WHERE [Id] = @p1; +""", + // + """ +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_collection_of_int64() + { + await base.Edit_single_property_collection_of_collection_of_int64(); + + AssertSql( + """ +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 +"""); + } + + protected override void ClearLog() + => Fixture.TestSqlLoggerFactory.Clear(); + + private void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); +} diff --git a/test/EFCore.SqlServer.FunctionalTests/Update/JsonUpdateSqlServerFixture.cs b/test/EFCore.SqlServer.FunctionalTests/Update/JsonUpdateSqlServerFixture.cs index 719cbced154..fb12560e0c8 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Update/JsonUpdateSqlServerFixture.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Update/JsonUpdateSqlServerFixture.cs @@ -12,6 +12,9 @@ public class JsonUpdateSqlServerFixture : JsonUpdateFixtureBase protected override ITestStoreFactory TestStoreFactory => SqlServerTestStoreFactory.Instance; + public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder) + => base.AddOptions(builder).ConfigureWarnings(e => e.Log(SqlServerEventId.JsonTypeExperimental)); + protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context) { base.OnModelCreating(modelBuilder, context); diff --git a/test/EFCore.SqlServer.FunctionalTests/Update/JsonUpdateSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Update/JsonUpdateSqlServerTest.cs index 05ce6fb7ce2..ca8b6aa0500 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Update/JsonUpdateSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Update/JsonUpdateSqlServerTest.cs @@ -332,7 +332,7 @@ public override async Task Edit_element_in_json_collection_branch() AssertSql( """ -@p0='2111-11-11T00:00:00' (Nullable = false) (Size = 19) +@p0='2111-11-11T00:00:00' (Nullable = false) (Size = 4000) @p1='1' SET IMPLICIT_TRANSACTIONS OFF; @@ -603,7 +603,7 @@ public override async Task Edit_single_property_char() AssertSql( """ -@p0='t' (Nullable = false) (Size = 1) +@p0='t' (Nullable = false) (Size = 4000) @p1='1' SET IMPLICIT_TRANSACTIONS OFF; @@ -626,8 +626,8 @@ public override async Task Edit_single_property_datetime() AssertSql( """ -@p0='3000-01-01T12:34:56' (Nullable = false) (Size = 19) -@p1='3000-01-01T12:34:56' (Nullable = false) (Size = 19) +@p0='3000-01-01T12:34:56' (Nullable = false) (Size = 4000) +@p1='3000-01-01T12:34:56' (Nullable = false) (Size = 4000) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; @@ -650,8 +650,8 @@ public override async Task Edit_single_property_datetimeoffset() AssertSql( """ -@p0='3000-01-01T12:34:56-04:00' (Nullable = false) (Size = 25) -@p1='3000-01-01T12:34:56-04:00' (Nullable = false) (Size = 25) +@p0='3000-01-01T12:34:56-04:00' (Nullable = false) (Size = 4000) +@p1='3000-01-01T12:34:56-04:00' (Nullable = false) (Size = 4000) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; @@ -722,8 +722,8 @@ public override async Task Edit_single_property_guid() AssertSql( """ -@p0='12345678-1234-4321-5555-987654321000' (Nullable = false) (Size = 36) -@p1='12345678-1234-4321-5555-987654321000' (Nullable = false) (Size = 36) +@p0='12345678-1234-4321-5555-987654321000' (Nullable = false) (Size = 4000) +@p1='12345678-1234-4321-5555-987654321000' (Nullable = false) (Size = 4000) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; @@ -866,8 +866,8 @@ public override async Task Edit_single_property_timespan() AssertSql( """ -@p0='10:01:01.007' (Nullable = false) (Size = 12) -@p1='10:01:01.007' (Nullable = false) (Size = 12) +@p0='10:01:01.007' (Nullable = false) (Size = 4000) +@p1='10:01:01.007' (Nullable = false) (Size = 4000) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; @@ -1315,7 +1315,7 @@ public override async Task Edit_single_property_with_converter_bool_to_string_Tr AssertSql( """ -@p0='True' (Nullable = false) (Size = 5) +@p0='True' (Nullable = false) (Size = 4000) @p1='1' SET IMPLICIT_TRANSACTIONS OFF; @@ -1338,7 +1338,7 @@ public override async Task Edit_single_property_with_converter_bool_to_string_Y_ AssertSql( """ -@p0='N' (Nullable = false) (Size = 1) +@p0='N' (Nullable = false) (Size = 4000) @p1='1' SET IMPLICIT_TRANSACTIONS OFF; @@ -1432,8 +1432,8 @@ public override async Task Edit_single_property_collection_of_numeric() AssertSql( """ -@p0='[1024,2048]' (Nullable = false) (Size = 4000) -@p1='[999,997]' (Nullable = false) (Size = 4000) +@p0='[1024,2048]' (Nullable = false) (Size = 11) +@p1='[999,997]' (Nullable = false) (Size = 9) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; @@ -1455,8 +1455,8 @@ public override async Task Edit_single_property_collection_of_bool() AssertSql( """ -@p0='[true,true,true,false]' (Nullable = false) (Size = 4000) -@p1='[true,true,false]' (Nullable = false) (Size = 4000) +@p0='[true,true,true,false]' (Nullable = false) (Size = 22) +@p1='[true,true,false]' (Nullable = false) (Size = 17) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; @@ -1479,8 +1479,8 @@ public override async Task Edit_single_property_collection_of_byte() AssertSql( """ -@p0='Dg==' (Nullable = false) (Size = 4) -@p1='GRo=' (Nullable = false) (Size = 4) +@p0='Dg==' (Nullable = false) (Size = 4000) +@p1='GRo=' (Nullable = false) (Size = 4000) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; @@ -1503,8 +1503,8 @@ public override async Task Edit_single_property_collection_of_char() AssertSql( """ -@p0='["A","B","\u0022","\u0000"]' (Nullable = false) (Size = 4000) -@p1='["E","F","C","\u00F6","r","E","\u0022","\\"]' (Nullable = false) (Size = 4000) +@p0='["A","B","\u0022","\u0000"]' (Nullable = false) (Size = 27) +@p1='["E","F","C","\u00F6","r","E","\u0022","\\"]' (Nullable = false) (Size = 44) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; @@ -1527,8 +1527,8 @@ public override async Task Edit_single_property_collection_of_datetime() AssertSql( """ -@p0='["2000-01-01T12:34:56","3000-01-01T12:34:56","3000-01-01T12:34:56"]' (Nullable = false) (Size = 4000) -@p1='["2000-01-01T12:34:56","3000-01-01T12:34:56","3000-01-01T12:34:56"]' (Nullable = false) (Size = 4000) +@p0='["2000-01-01T12:34:56","3000-01-01T12:34:56","3000-01-01T12:34:56"]' (Nullable = false) (Size = 67) +@p1='["2000-01-01T12:34:56","3000-01-01T12:34:56","3000-01-01T12:34:56"]' (Nullable = false) (Size = 67) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; @@ -1551,8 +1551,8 @@ public override async Task Edit_single_property_collection_of_datetimeoffset() AssertSql( """ -@p0='["3000-01-01T12:34:56-04:00"]' (Nullable = false) (Size = 4000) -@p1='["3000-01-01T12:34:56-04:00"]' (Nullable = false) (Size = 4000) +@p0='["3000-01-01T12:34:56-04:00"]' (Nullable = false) (Size = 29) +@p1='["3000-01-01T12:34:56-04:00"]' (Nullable = false) (Size = 29) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; @@ -1575,8 +1575,8 @@ public override async Task Edit_single_property_collection_of_decimal() AssertSql( """ -@p0='[-13579.01]' (Nullable = false) (Size = 4000) -@p1='[-13579.01]' (Nullable = false) (Size = 4000) +@p0='[-13579.01]' (Nullable = false) (Size = 11) +@p1='[-13579.01]' (Nullable = false) (Size = 11) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; @@ -1599,8 +1599,8 @@ public override async Task Edit_single_property_collection_of_double() AssertSql( """ -@p0='[-1.23456789,1.23456789,0,-1.23579]' (Nullable = false) (Size = 4000) -@p1='[-1.23456789,1.23456789,0,-1.23579]' (Nullable = false) (Size = 4000) +@p0='[-1.23456789,1.23456789,0,-1.23579]' (Nullable = false) (Size = 35) +@p1='[-1.23456789,1.23456789,0,-1.23579]' (Nullable = false) (Size = 35) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; @@ -1623,8 +1623,8 @@ public override async Task Edit_single_property_collection_of_guid() AssertSql( """ -@p0='["12345678-1234-4321-5555-987654321000"]' (Nullable = false) (Size = 4000) -@p1='["12345678-1234-4321-5555-987654321000"]' (Nullable = false) (Size = 4000) +@p0='["12345678-1234-4321-5555-987654321000"]' (Nullable = false) (Size = 40) +@p1='["12345678-1234-4321-5555-987654321000"]' (Nullable = false) (Size = 40) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; @@ -1647,8 +1647,8 @@ public override async Task Edit_single_property_collection_of_int16() AssertSql( """ -@p0='[-3234]' (Nullable = false) (Size = 4000) -@p1='[-3234]' (Nullable = false) (Size = 4000) +@p0='[-3234]' (Nullable = false) (Size = 7) +@p1='[-3234]' (Nullable = false) (Size = 7) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; @@ -1671,8 +1671,8 @@ public override async Task Edit_single_property_collection_of_int32() AssertSql( """ -@p0='[-3234]' (Nullable = false) (Size = 4000) -@p1='[-3234]' (Nullable = false) (Size = 4000) +@p0='[-3234]' (Nullable = false) (Size = 7) +@p1='[-3234]' (Nullable = false) (Size = 7) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; @@ -1695,8 +1695,8 @@ public override async Task Edit_single_property_collection_of_int64() AssertSql( """ -@p0='[]' (Nullable = false) (Size = 4000) -@p1='[]' (Nullable = false) (Size = 4000) +@p0='[]' (Nullable = false) (Size = 2) +@p1='[]' (Nullable = false) (Size = 2) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; @@ -1719,8 +1719,8 @@ public override async Task Edit_single_property_collection_of_signed_byte() AssertSql( """ -@p0='[-108]' (Nullable = false) (Size = 4000) -@p1='[-108]' (Nullable = false) (Size = 4000) +@p0='[-108]' (Nullable = false) (Size = 6) +@p1='[-108]' (Nullable = false) (Size = 6) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; @@ -1743,8 +1743,8 @@ public override async Task Edit_single_property_collection_of_single() AssertSql( """ -@p0='[-1.234,-1.234]' (Nullable = false) (Size = 4000) -@p1='[0,-1.234]' (Nullable = false) (Size = 4000) +@p0='[-1.234,-1.234]' (Nullable = false) (Size = 15) +@p1='[0,-1.234]' (Nullable = false) (Size = 10) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; @@ -1767,8 +1767,8 @@ public override async Task Edit_single_property_collection_of_timespan() AssertSql( """ -@p0='["10:09:08.007","10:01:01.007"]' (Nullable = false) (Size = 4000) -@p1='["10:01:01.007","-9:50:51.993"]' (Nullable = false) (Size = 4000) +@p0='["10:09:08.007","10:01:01.007"]' (Nullable = false) (Size = 31) +@p1='["10:01:01.007","-9:50:51.993"]' (Nullable = false) (Size = 31) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; @@ -1791,8 +1791,8 @@ public override async Task 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) +@p0='["3234-01-23","0001-01-07"]' (Nullable = false) (Size = 27) +@p1='["0001-01-07","4321-01-21"]' (Nullable = false) (Size = 27) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; @@ -1815,8 +1815,8 @@ public override async Task 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) +@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' SET IMPLICIT_TRANSACTIONS OFF; @@ -1839,8 +1839,8 @@ public override async Task Edit_single_property_collection_of_uint16() AssertSql( """ -@p0='[1534]' (Nullable = false) (Size = 4000) -@p1='[1534]' (Nullable = false) (Size = 4000) +@p0='[1534]' (Nullable = false) (Size = 6) +@p1='[1534]' (Nullable = false) (Size = 6) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; @@ -1863,8 +1863,8 @@ public override async Task Edit_single_property_collection_of_uint32() AssertSql( """ -@p0='[1237775789]' (Nullable = false) (Size = 4000) -@p1='[1237775789]' (Nullable = false) (Size = 4000) +@p0='[1237775789]' (Nullable = false) (Size = 12) +@p1='[1237775789]' (Nullable = false) (Size = 12) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; @@ -1887,8 +1887,8 @@ public override async Task Edit_single_property_collection_of_uint64() AssertSql( """ -@p0='[1234555555123456789]' (Nullable = false) (Size = 4000) -@p1='[1234555555123456789]' (Nullable = false) (Size = 4000) +@p0='[1234555555123456789]' (Nullable = false) (Size = 21) +@p1='[1234555555123456789]' (Nullable = false) (Size = 21) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; @@ -1911,8 +1911,8 @@ public override async Task Edit_single_property_collection_of_nullable_int32() AssertSql( """ -@p0='[null,77]' (Nullable = false) (Size = 4000) -@p1='[null,-2147483648,0,null,2147483647,null,77,null]' (Nullable = false) (Size = 4000) +@p0='[null,77]' (Nullable = false) (Size = 9) +@p1='[null,-2147483648,0,null,2147483647,null,77,null]' (Nullable = false) (Size = 49) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; @@ -1959,8 +1959,8 @@ public override async Task Edit_single_property_collection_of_enum() AssertSql( """ -@p0='[-3]' (Nullable = false) (Size = 4000) -@p1='[-3]' (Nullable = false) (Size = 4000) +@p0='[-3]' (Nullable = false) (Size = 4) +@p1='[-3]' (Nullable = false) (Size = 4) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; @@ -1983,8 +1983,8 @@ public override async Task Edit_single_property_collection_of_enum_with_int_conv AssertSql( """ -@p0='[-3]' (Nullable = false) (Size = 4000) -@p1='[-3]' (Nullable = false) (Size = 4000) +@p0='[-3]' (Nullable = false) (Size = 4) +@p1='[-3]' (Nullable = false) (Size = 4) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; @@ -2007,8 +2007,8 @@ public override async Task Edit_single_property_collection_of_nullable_enum() AssertSql( """ -@p0='[-3]' (Nullable = false) (Size = 4000) -@p1='[-3]' (Nullable = false) (Size = 4000) +@p0='[-3]' (Nullable = false) (Size = 4) +@p1='[-3]' (Nullable = false) (Size = 4) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; @@ -2055,8 +2055,8 @@ public override async Task Edit_single_property_collection_of_nullable_enum_with AssertSql( """ -@p0='[-1,null,-7,2]' (Nullable = false) (Size = 4000) -@p1='[-1,-3,-7,2]' (Nullable = false) (Size = 4000) +@p0='[-1,null,-7,2]' (Nullable = false) (Size = 14) +@p1='[-1,-3,-7,2]' (Nullable = false) (Size = 12) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; @@ -2103,8 +2103,8 @@ public override async Task Edit_single_property_collection_of_nullable_enum_with AssertSql( """ -@p0='[-3]' (Nullable = false) (Size = 4000) -@p1='[-1]' (Nullable = false) (Size = 4000) +@p0='[-3]' (Nullable = false) (Size = 4) +@p1='[-1]' (Nullable = false) (Size = 4) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; @@ -2419,6 +2419,13 @@ public override async Task Add_and_update_nested_optional_primitive_collection(b _ => "1557" }; + string updateParameterSize = value switch + { + true => "4000", + false => "5", + _ => "2" + }; + string updateParameter = value switch { true => "NULL", @@ -2467,7 +2474,7 @@ FROM [JsonEntitiesAllTypes] AS [j] """, // -"@p0=" + updateParameter + @" (Nullable = false) (Size = 4000) +"@p0=" + updateParameter + @" (Nullable = false) (Size = " + updateParameterSize + @") @p1='7624' SET IMPLICIT_TRANSACTIONS OFF; diff --git a/test/EFCore.SqlServer.Tests/Diagnostics/SqlServerEventIdTest.cs b/test/EFCore.SqlServer.Tests/Diagnostics/SqlServerEventIdTest.cs index 58ecf35c6cd..cabca35ad48 100644 --- a/test/EFCore.SqlServer.Tests/Diagnostics/SqlServerEventIdTest.cs +++ b/test/EFCore.SqlServer.Tests/Diagnostics/SqlServerEventIdTest.cs @@ -22,6 +22,7 @@ public void Every_eventId_has_a_logger_method_and_logs_when_level_enabled() { { typeof(IList), () => new List { "Fake1", "Fake2" } }, { typeof(IProperty), () => property }, + { typeof(IEntityType), () => entityType }, { typeof(IReadOnlyProperty), () => property }, { typeof(string), () => "Fake" } }; diff --git a/test/EFCore.SqlServer.Tests/Infrastructure/SqlServerModelValidatorTest.cs b/test/EFCore.SqlServer.Tests/Infrastructure/SqlServerModelValidatorTest.cs index f28a1271469..53ceac990f7 100644 --- a/test/EFCore.SqlServer.Tests/Infrastructure/SqlServerModelValidatorTest.cs +++ b/test/EFCore.SqlServer.Tests/Infrastructure/SqlServerModelValidatorTest.cs @@ -11,6 +11,39 @@ namespace Microsoft.EntityFrameworkCore.Infrastructure; public class SqlServerModelValidatorTest : RelationalModelValidatorTest { + [ConditionalFact] + public void Detects_use_of_json_column() + { + var modelBuilder = CreateConventionModelBuilder(); + modelBuilder.Entity().Property(e => e.Name).HasColumnType("json"); + + VerifyWarning( + SqlServerResources.LogJsonTypeExperimental(new TestLogger()) + .GenerateMessage("Cheese"), modelBuilder); + } + + [ConditionalFact] + public void Detects_use_of_json_column_for_container() + { + var modelBuilder = CreateConventionModelBuilder(); + modelBuilder.Entity( + b => + { + b.OwnsOne( + x => x.OwnedReference, bb => + { + bb.ToJson().HasColumnType("json"); + bb.Ignore(x => x.NestedCollection); + bb.Ignore(x => x.NestedReference); + }); + b.Ignore(x => x.OwnedCollection); + }); + + VerifyWarning( + SqlServerResources.LogJsonTypeExperimental(new TestLogger()) + .GenerateMessage(nameof(ValidatorJsonOwnedRoot)), modelBuilder); + } + [ConditionalFact] // Issue #34324 public virtual void Throws_for_nested_primitive_collections() { diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/AdHocJsonQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/AdHocJsonQuerySqliteTest.cs index fcc06b5f823..c9ba2346901 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/AdHocJsonQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/AdHocJsonQuerySqliteTest.cs @@ -10,7 +10,7 @@ public class AdHocJsonQuerySqliteTest : AdHocJsonQueryTestBase protected override ITestStoreFactory TestStoreFactory => SqliteTestStoreFactory.Instance; - protected override async Task Seed29219(MyContext29219 ctx) + protected override async Task Seed29219(DbContext ctx) { var entity1 = new MyEntity29219 { @@ -31,7 +31,7 @@ protected override async Task Seed29219(MyContext29219 ctx) Collection = [new() { NonNullableScalar = 1001, NullableScalar = null }] }; - ctx.Entities.AddRange(entity1, entity2); + ctx.Set().AddRange(entity1, entity2); await ctx.SaveChangesAsync(); await ctx.Database.ExecuteSqlAsync( @@ -41,7 +41,7 @@ await ctx.Database.ExecuteSqlAsync( """); } - protected override async Task Seed30028(MyContext30028 ctx) + protected override async Task Seed30028(DbContext ctx) { // complete await ctx.Database.ExecuteSqlAsync( @@ -80,14 +80,14 @@ await ctx.Database.ExecuteSqlAsync( """); } - protected override async Task Seed33046(Context33046 ctx) + protected override async Task Seed33046(DbContext ctx) => await ctx.Database.ExecuteSqlAsync( $$""" INSERT INTO "Reviews" ("Rounds", "Id") VALUES('[{"RoundNumber":11,"SubRounds":[{"SubRoundNumber":111},{"SubRoundNumber":112}]}]', 1) """); - protected override Task SeedArrayOfPrimitives(MyContextArrayOfPrimitives ctx) + protected override Task SeedArrayOfPrimitives(DbContext ctx) { var entity1 = new MyEntityArrayOfPrimitives { @@ -129,11 +129,11 @@ protected override Task SeedArrayOfPrimitives(MyContextArrayOfPrimitives ctx) ] }; - ctx.Entities.AddRange(entity1, entity2); + ctx.Set().AddRange(entity1, entity2); return ctx.SaveChangesAsync(); } - protected override Task SeedJunkInJson(MyContextJunkInJson ctx) + protected override Task SeedJunkInJson(DbContext ctx) => ctx.Database.ExecuteSqlAsync( $$$""" INSERT INTO "Entities" ("Collection", "CollectionWithCtor", "Reference", "ReferenceWithCtor", "Id") @@ -145,7 +145,7 @@ protected override Task SeedJunkInJson(MyContextJunkInJson ctx) 1) """); - protected override Task SeedTrickyBuffering(MyContextTrickyBuffering ctx) + protected override Task SeedTrickyBuffering(DbContext ctx) => ctx.Database.ExecuteSqlAsync( $$$""" INSERT INTO "Entities" ("Reference", "Id") @@ -153,7 +153,7 @@ protected override Task SeedTrickyBuffering(MyContextTrickyBuffering ctx) '{"Name": "r1", "Number": 7, "JunkReference":{"Something": "SomeValue" }, "JunkCollection": [{"Foo": "junk value"}], "NestedReference": {"DoB": "2000-01-01T00:00:00"}, "NestedCollection": [{"DoB": "2000-02-01T00:00:00", "JunkReference": {"Something": "SomeValue"}}, {"DoB": "2000-02-02T00:00:00"}]}',1) """); - protected override Task SeedShadowProperties(MyContextShadowProperties ctx) + protected override Task SeedShadowProperties(DbContext ctx) => ctx.Database.ExecuteSqlAsync( $$""" INSERT INTO "Entities" ("Collection", "CollectionWithCtor", "Reference", "ReferenceWithCtor", "Id", "Name") @@ -166,7 +166,7 @@ protected override Task SeedShadowProperties(MyContextShadowProperties ctx) 'e1') """); - protected override async Task SeedNotICollection(MyContextNotICollection ctx) + protected override async Task SeedNotICollection(DbContext ctx) { await ctx.Database.ExecuteSqlAsync( $$"""