diff --git a/src/EFCore.Relational/Update/ColumnModification.cs b/src/EFCore.Relational/Update/ColumnModification.cs
index 3c5d5ae2b6f..1fb54784a11 100644
--- a/src/EFCore.Relational/Update/ColumnModification.cs
+++ b/src/EFCore.Relational/Update/ColumnModification.cs
@@ -1,8 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System.Data;
-
namespace Microsoft.EntityFrameworkCore.Update;
///
@@ -54,6 +52,7 @@ public ColumnModification(in ColumnModificationParameters columnModificationPara
IsNullable = columnModificationParameters.IsNullable;
_generateParameterName = columnModificationParameters.GenerateParameterName;
Entry = columnModificationParameters.Entry;
+ JsonPath = columnModificationParameters.JsonPath;
UseParameter = _generateParameterName != null;
}
@@ -174,6 +173,9 @@ public virtual object? Value
}
}
+ ///
+ public virtual string? JsonPath { get; }
+
///
public virtual void AddSharedColumnModification(IColumnModification modification)
{
diff --git a/src/EFCore.Relational/Update/ColumnModificationParameters.cs b/src/EFCore.Relational/Update/ColumnModificationParameters.cs
index f58d11e7287..7ddd7ce9150 100644
--- a/src/EFCore.Relational/Update/ColumnModificationParameters.cs
+++ b/src/EFCore.Relational/Update/ColumnModificationParameters.cs
@@ -76,7 +76,7 @@ public readonly record struct ColumnModificationParameters
/// Indicates whether the column is part of a primary or alternate key.
///
public bool IsKey { get; init; }
-
+
///
/// The column.
///
@@ -92,6 +92,11 @@ public readonly record struct ColumnModificationParameters
///
public string? ColumnType { get; init; }
+ ///
+ /// In case of JSON column modification, the JSON path leading to the JSON element that needs to be updated.
+ ///
+ public string? JsonPath { get; init; }
+
///
/// Creates a new instance.
///
@@ -137,8 +142,9 @@ public ColumnModificationParameters(
GenerateParameterName = null;
Entry = null;
+ JsonPath = null;
}
-
+
///
/// Creates a new instance.
///
@@ -182,6 +188,7 @@ public ColumnModificationParameters(
GenerateParameterName = null;
Entry = null;
+ JsonPath = null;
}
///
@@ -225,5 +232,54 @@ public ColumnModificationParameters(
GenerateParameterName = generateParameterName;
Entry = entry;
+ JsonPath = null;
+ }
+
+ ///
+ /// Creates a new instance.
+ ///
+ /// The name of the column.
+ /// The original value of the property mapped to this column.
+ /// The current value of the property mapped to this column.
+ /// The JSON path leading to the JSON element that needs to be updated.
+ /// The database type of the column.
+ /// The relational type mapping to be used for the command parameter.
+ /// Indicates whether a value must be read from the database for the column.
+ /// Indicates whether a value must be written to the database for the column.
+ /// Indicates whether the column part of a primary or alternate key.
+ /// Indicates whether the column is used in the WHERE clause when updating.
+ /// Indicates whether potentially sensitive data (e.g. database values) can be logged.
+ /// A value indicating whether the value could be null.
+ public ColumnModificationParameters(
+ string columnName,
+ object? originalValue,
+ object? value,
+ string? columnType,
+ RelationalTypeMapping? typeMapping,
+ string jsonPath,
+ bool read,
+ bool write,
+ bool key,
+ bool condition,
+ bool sensitiveLoggingEnabled,
+ bool? isNullable = null)
+ {
+ Column = null;
+ ColumnName = columnName;
+ OriginalValue = originalValue;
+ Value = value;
+ Property = null;
+ ColumnType = columnType;
+ TypeMapping = typeMapping;
+ JsonPath = jsonPath;
+ IsRead = read;
+ IsWrite = write;
+ IsKey = key;
+ IsCondition = condition;
+ SensitiveLoggingEnabled = sensitiveLoggingEnabled;
+ IsNullable = isNullable;
+
+ GenerateParameterName = null;
+ Entry = null;
}
}
diff --git a/src/EFCore.Relational/Update/IColumnModification.cs b/src/EFCore.Relational/Update/IColumnModification.cs
index b449dec4abd..5e8c3510bd9 100644
--- a/src/EFCore.Relational/Update/IColumnModification.cs
+++ b/src/EFCore.Relational/Update/IColumnModification.cs
@@ -122,6 +122,11 @@ public interface IColumnModification
///
public object? Value { get; set; }
+ ///
+ /// In case of JSON column modification, the JSON path leading to the JSON element that needs to be updated.
+ ///
+ public string? JsonPath { get; }
+
///
/// Adds a modification affecting the same database value.
///
diff --git a/src/EFCore.Relational/Update/ModificationCommand.cs b/src/EFCore.Relational/Update/ModificationCommand.cs
index 69744ad6b91..4dc4d9f55a5 100644
--- a/src/EFCore.Relational/Update/ModificationCommand.cs
+++ b/src/EFCore.Relational/Update/ModificationCommand.cs
@@ -6,6 +6,7 @@
using System.Text.Json.Nodes;
using Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
using Microsoft.EntityFrameworkCore.Internal;
+using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using IColumnMapping = Microsoft.EntityFrameworkCore.Metadata.IColumnMapping;
using ITableMapping = Microsoft.EntityFrameworkCore.Metadata.ITableMapping;
@@ -250,6 +251,26 @@ public virtual IColumnModification AddColumnModification(in ColumnModificationPa
protected virtual IColumnModification CreateColumnModification(in ColumnModificationParameters columnModificationParameters)
=> new ColumnModification(columnModificationParameters);
+ private record struct JsonPartialUpdatePathEntry
+ {
+ public JsonPartialUpdatePathEntry(
+ string propertyName,
+ int? ordinal,
+ IUpdateEntry parentEntry,
+ INavigation navigation)
+ {
+ PropertyName = propertyName;
+ Ordinal = ordinal;
+ ParentEntry = parentEntry;
+ Navigation = navigation;
+ }
+
+ public string PropertyName { get; }
+ public int? Ordinal { get; }
+ public IUpdateEntry ParentEntry { get; }
+ public INavigation Navigation { get; }
+ }
+
private List GenerateColumnModifications()
{
var state = EntityState;
@@ -257,6 +278,7 @@ private List GenerateColumnModifications()
var updating = state == EntityState.Modified;
var columnModifications = new List();
Dictionary? sharedTableColumnMap = null;
+ var jsonEntry = false;
if (_entries.Count > 1
|| (_entries.Count == 1 && _entries[0].SharedIdentityEntry != null))
@@ -290,74 +312,107 @@ private List GenerateColumnModifications()
}
InitializeSharedColumns(entry, tableMapping, updating, sharedTableColumnMap);
+
+ if (!jsonEntry && entry.EntityType.IsMappedToJson())
+ {
+ jsonEntry = true;
+ }
}
}
- var processedJsonNavigations = new List();
- foreach (var entry in _entries)
+ if (jsonEntry)
{
- if (entry.EntityType.IsMappedToJson())
+ var jsonColumnsUpdateMap = new Dictionary>();
+ var processedEntries = new List();
+ foreach (var entry in _entries.Where(e => e.EntityType.IsMappedToJson()))
{
- // for JSON entry, traverse to the entry for root JSON entity
- // and build entire JSON structure based on it
- // this will be the column modification command
- var jsonColumnName = entry.EntityType.GetContainerColumnName()!;
- var jsonColumnTypeMapping = entry.EntityType.GetContainerColumnTypeMapping()!;
-
- var currentEntry = entry;
- var currentOwnership = currentEntry.EntityType.FindOwnership()!;
- while (currentEntry.EntityType.IsMappedToJson())
+ var modifiedMembers = entry.ToEntityEntry().Members.Where(m => m is not NavigationEntry && m.IsModified).ToList();
+ var jsonColumn = entry.EntityType.GetContainerColumnName()!;
+ var jsonPartialUpdateInfo = FindJsonPartialUpdateInfo(entry, processedEntries);
+ processedEntries.Add(entry);
+
+ if (jsonPartialUpdateInfo == null)
{
- currentOwnership = currentEntry.EntityType.FindOwnership()!;
-#pragma warning disable EF1001 // Internal EF Core API usage.
- currentEntry = ((InternalEntityEntry)currentEntry).StateManager.FindPrincipal((InternalEntityEntry)currentEntry, currentOwnership)!;
-#pragma warning restore EF1001 // Internal EF Core API usage.
+ // this entry is a subtree of an entry that we already processed
+ // so we already need to update the parent - no need to have extra entry for the subtree
+ continue;
}
- var navigation = currentOwnership.GetNavigation(pointsToPrincipal: false)!;
- if (processedJsonNavigations.Contains(navigation))
+ if (jsonColumnsUpdateMap.TryGetValue(jsonColumn, out var currentJsonPartialUpdateInfo))
{
- continue;
+ jsonPartialUpdateInfo = FindCommonJsonPartialUpdateInfo(
+ currentJsonPartialUpdateInfo,
+ jsonPartialUpdateInfo);
}
- processedJsonNavigations.Add(navigation);
+ jsonColumnsUpdateMap[jsonColumn] = jsonPartialUpdateInfo;
+ }
+
+ foreach (var (jsonColumnName, updatePath) in jsonColumnsUpdateMap)
+ {
+ var finalUpdatePathElement = updatePath.Last();
+ var navigation = finalUpdatePathElement.Navigation;
+
+ var jsonColumnTypeMapping = navigation.TargetEntityType.GetContainerColumnTypeMapping()!;
+ var navigationValue = finalUpdatePathElement.ParentEntry.GetCurrentValue(navigation);
- // parent entity got deleted, no need to do any json-specific processing
- if (currentEntry.EntityState == EntityState.Deleted)
+ var json = default(JsonNode?);
+ if (finalUpdatePathElement.Ordinal != null && navigationValue != null)
{
- continue;
- }
+ int i = 0;
+ foreach (var navigationValueElement in (IEnumerable)navigationValue)
+ {
+ if (i == finalUpdatePathElement.Ordinal)
+ {
+ json = CreateJson(
+ navigationValueElement,
+ finalUpdatePathElement.ParentEntry,
+ navigation.TargetEntityType,
+ ordinal: null,
+ isCollection: false);
+
+ break;
+ }
- var navigationValue = currentEntry.GetCurrentValue(navigation)!;
+ i++;
+ }
+ }
+ else
+ {
+ json = CreateJson(
+ navigationValue,
+ finalUpdatePathElement.ParentEntry,
+ navigation.TargetEntityType,
+ ordinal: null,
+ isCollection: navigation.IsCollection);
+ }
- var json = CreateJson(
- navigationValue,
- currentEntry,
- currentOwnership.DeclaringEntityType,
- ordinal: null,
- isCollection: navigation.IsCollection);
+ var jsonPathString = string.Join(
+ ".", updatePath.Select(x => x.PropertyName + (x.Ordinal != null ? "[" + x.Ordinal + "]" : "")));
var columnModificationParameters = new ColumnModificationParameters(
- jsonColumnName,
- originalValue: null,
- value: json?.ToJsonString(),
- property: null,
- columnType: jsonColumnTypeMapping.StoreType,
- jsonColumnTypeMapping,
- read: false,
- write: true,
- key: false,
- condition: false,
- _sensitiveLoggingEnabled)
+ jsonColumnName,
+ originalValue: null,
+ value: json?.ToJsonString(),
+ columnType: jsonColumnTypeMapping.StoreType,
+ jsonColumnTypeMapping,
+ jsonPath: jsonPathString,
+ read: false,
+ write: true,
+ key: false,
+ condition: false,
+ _sensitiveLoggingEnabled)
{
GenerateParameterName = _generateParameterName,
};
columnModifications.Add(new ColumnModification(columnModificationParameters));
-
- continue;
}
+ }
+ var processedJsonNavigations = new List();
+ foreach (var entry in _entries.Where(x => !x.EntityType.IsMappedToJson()))
+ {
var nonMainEntry = !_mainEntryAdded || entry != _entries[0];
IEnumerable columnMappings;
@@ -526,6 +581,87 @@ entry.EntityState is EntityState.Modified or EntityState.Added
}
return columnModifications;
+
+ static List? FindJsonPartialUpdateInfo(IUpdateEntry entry, List processedEntries)
+ {
+ var result = new List();
+ var currentEntry = entry;
+ var currentOwnership = currentEntry.EntityType.FindOwnership()!;
+
+ while (currentEntry.EntityType.IsMappedToJson())
+ {
+ var jsonPropertyName = currentEntry.EntityType.GetJsonPropertyName()!;
+ currentOwnership = currentEntry.EntityType.FindOwnership()!;
+ var previousEntry = currentEntry;
+#pragma warning disable EF1001 // Internal EF Core API usage.
+ currentEntry = ((InternalEntityEntry)currentEntry).StateManager.FindPrincipal((InternalEntityEntry)currentEntry, currentOwnership)!;
+#pragma warning restore EF1001 // Internal EF Core API usage.
+
+ if (processedEntries.Contains(currentEntry))
+ {
+ return null;
+ }
+
+ var ordinal = default(int?);
+ if (!currentOwnership.IsUnique
+ && previousEntry.EntityState != EntityState.Added
+ && previousEntry.EntityState != EntityState.Deleted)
+ {
+ var ordinalProperty = previousEntry.EntityType.FindPrimaryKey()!.Properties.Last();
+ ordinal = (int)previousEntry.GetCurrentProviderValue(ordinalProperty)! - 1;
+ }
+
+ var pathEntry = new JsonPartialUpdatePathEntry(
+ currentOwnership.PrincipalEntityType.IsMappedToJson() ? jsonPropertyName : "$",
+ ordinal,
+ currentEntry,
+ currentOwnership.GetNavigation(pointsToPrincipal: false)!);
+
+ result.Insert(0, pathEntry);
+ }
+
+ // parent entity got deleted, no need to do any json-specific processing
+ if (currentEntry.EntityState == EntityState.Deleted)
+ {
+ return null;
+ }
+
+ return result;
+ }
+
+ static List FindCommonJsonPartialUpdateInfo(
+ List first,
+ List second)
+ {
+ var result = new List();
+ for (var i = 0; i < Math.Min(first.Count, second.Count); i++)
+ {
+ if (first[i].PropertyName == second[i].PropertyName)
+ {
+ if (first[i].Ordinal == second[i].Ordinal)
+ {
+ result.Add(first[i]);
+ continue;
+ }
+ else
+ {
+ var common = new JsonPartialUpdatePathEntry(
+ first[i].PropertyName,
+ null,
+ first[i].ParentEntry,
+ first[i].Navigation);
+
+ result.Add(common);
+ }
+
+ break;
+ }
+ }
+
+ Debug.Assert(result.Count > 0, "Common denominator should always have at least one node - the root.");
+
+ return result;
+ }
}
private JsonNode? CreateJson(object? navigationValue, IUpdateEntry parentEntry, IEntityType entityType, int? ordinal, bool isCollection)
diff --git a/src/EFCore.Relational/Update/UpdateSqlGenerator.cs b/src/EFCore.Relational/Update/UpdateSqlGenerator.cs
index 3a8c9cde012..f8ebf596685 100644
--- a/src/EFCore.Relational/Update/UpdateSqlGenerator.cs
+++ b/src/EFCore.Relational/Update/UpdateSqlGenerator.cs
@@ -317,17 +317,36 @@ protected virtual void AppendUpdateCommandHeader(
var (g, n, s) = p;
g.SqlGenerationHelper.DelimitIdentifier(sb, o.ColumnName);
sb.Append(" = ");
- if (!o.UseCurrentValueParameter)
- {
- AppendSqlLiteral(sb, o, n, s);
- }
- else
- {
- g.SqlGenerationHelper.GenerateParameterNamePlaceholder(sb, o.ParameterName);
- }
+ AppendUpdateColumnValue(g.SqlGenerationHelper, o, sb, n, s);
});
}
+ ///
+ /// Appends a SQL fragment representing the value that is assigned to a column which is being updated.
+ ///
+ /// The update sql generator helper.
+ /// The operation representing the data to be updated.
+ /// The builder to which the SQL should be appended.
+ /// The name of the table.
+ /// The table schema, or to use the default schema.
+ protected virtual void AppendUpdateColumnValue(
+ ISqlGenerationHelper updateSqlGeneratorHelper,
+ IColumnModification columnModification,
+ StringBuilder stringBuilder,
+ string name,
+ string? schema)
+ {
+ if (!columnModification.UseCurrentValueParameter)
+ {
+ AppendSqlLiteral(stringBuilder, columnModification, name, schema);
+ }
+ else
+ {
+ updateSqlGeneratorHelper.GenerateParameterNamePlaceholder(
+ stringBuilder, columnModification.ParameterName);
+ }
+ }
+
///
public virtual ResultSetMapping AppendStoredProcedureCall(
StringBuilder commandStringBuilder,
diff --git a/src/EFCore.SqlServer/Update/Internal/SqlServerUpdateSqlGenerator.cs b/src/EFCore.SqlServer/Update/Internal/SqlServerUpdateSqlGenerator.cs
index 4ae6e84480c..960161c7e6a 100644
--- a/src/EFCore.SqlServer/Update/Internal/SqlServerUpdateSqlGenerator.cs
+++ b/src/EFCore.SqlServer/Update/Internal/SqlServerUpdateSqlGenerator.cs
@@ -135,6 +135,33 @@ protected override void AppendUpdateCommand(
commandStringBuilder.AppendLine(SqlGenerationHelper.StatementTerminator);
}
+ ///
+ protected override void AppendUpdateColumnValue(
+ ISqlGenerationHelper updateSqlGeneratorHelper,
+ IColumnModification columnModification,
+ StringBuilder stringBuilder,
+ string name,
+ string? schema)
+ {
+ if (columnModification.JsonPath != null
+ && columnModification.JsonPath != "$")
+ {
+ stringBuilder.Append("JSON_MODIFY(");
+ updateSqlGeneratorHelper.DelimitIdentifier(stringBuilder, columnModification.ColumnName);
+
+ // using strict so that we don't remove json elements when they are assigned NULL value
+ stringBuilder.Append(", 'strict ");
+ stringBuilder.Append(columnModification.JsonPath);
+ stringBuilder.Append("', JSON_QUERY(");
+ base.AppendUpdateColumnValue(updateSqlGeneratorHelper, columnModification, stringBuilder, name, schema);
+ stringBuilder.Append("))");
+ }
+ else
+ {
+ base.AppendUpdateColumnValue(updateSqlGeneratorHelper, columnModification, stringBuilder, name, schema);
+ }
+ }
+
///
/// 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/test/EFCore.Relational.Specification.Tests/Update/JsonUpdateFixtureBase.cs b/test/EFCore.Relational.Specification.Tests/Update/JsonUpdateFixtureBase.cs
new file mode 100644
index 00000000000..8d2a773a636
--- /dev/null
+++ b/test/EFCore.Relational.Specification.Tests/Update/JsonUpdateFixtureBase.cs
@@ -0,0 +1,112 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.EntityFrameworkCore.TestModels.JsonQuery;
+
+namespace Microsoft.EntityFrameworkCore.Update;
+
+public abstract class JsonUpdateFixtureBase : SharedStoreFixtureBase
+{
+ protected override string StoreName => "JsonUpdateTest";
+
+ public TestSqlLoggerFactory TestSqlLoggerFactory
+ => (TestSqlLoggerFactory)ListLoggerFactory;
+
+ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context)
+ {
+ modelBuilder.Entity().Property(x => x.Id).ValueGeneratedNever();
+ modelBuilder.Entity().OwnsOne(x => x.OwnedReferenceRoot, b =>
+ {
+ b.ToJson();
+ b.WithOwner(x => x.Owner);
+ b.OwnsOne(x => x.OwnedReferenceBranch, bb =>
+ {
+ bb.Property(x => x.Fraction).HasPrecision(18, 2);
+ bb.OwnsOne(x => x.OwnedReferenceLeaf).WithOwner(x => x.Parent);
+ bb.OwnsMany(x => x.OwnedCollectionLeaf);
+ });
+ b.OwnsMany(x => x.OwnedCollectionBranch, bb =>
+ {
+ bb.Property(x => x.Fraction).HasPrecision(18, 2);
+ bb.OwnsOne(x => x.OwnedReferenceLeaf);
+ bb.Navigation(x => x.OwnedReferenceLeaf).IsRequired(false);
+ bb.OwnsMany(x => x.OwnedCollectionLeaf).WithOwner(x => x.Parent);
+ });
+ });
+
+ modelBuilder.Entity().Navigation(x => x.OwnedReferenceRoot).IsRequired(false);
+
+ modelBuilder.Entity().OwnsMany(x => x.OwnedCollectionRoot, b =>
+ {
+ b.OwnsOne(x => x.OwnedReferenceBranch, bb =>
+ {
+ bb.Property(x => x.Fraction).HasPrecision(18, 2);
+ bb.OwnsOne(x => x.OwnedReferenceLeaf);
+ bb.OwnsMany(x => x.OwnedCollectionLeaf).WithOwner(x => x.Parent);
+ });
+
+ b.OwnsMany(x => x.OwnedCollectionBranch, bb =>
+ {
+ bb.Property(x => x.Fraction).HasPrecision(18, 2);
+ bb.OwnsOne(x => x.OwnedReferenceLeaf).WithOwner(x => x.Parent);
+ bb.OwnsMany(x => x.OwnedCollectionLeaf);
+ });
+ b.ToJson();
+ });
+
+ modelBuilder.Entity().Property(x => x.Id).ValueGeneratedNever();
+ modelBuilder.Entity(b =>
+ {
+ b.OwnsOne(x => x.ReferenceOnBase, bb =>
+ {
+ bb.ToJson();
+ bb.OwnsOne(x => x.OwnedReferenceLeaf);
+ bb.OwnsMany(x => x.OwnedCollectionLeaf);
+ bb.Property(x => x.Fraction).HasPrecision(18, 2);
+ });
+
+ b.OwnsMany(x => x.CollectionOnBase, bb =>
+ {
+ bb.ToJson();
+ bb.OwnsOne(x => x.OwnedReferenceLeaf);
+ bb.OwnsMany(x => x.OwnedCollectionLeaf);
+ bb.Property(x => x.Fraction).HasPrecision(18, 2);
+ });
+ });
+
+ modelBuilder.Entity(b =>
+ {
+ b.HasBaseType();
+ b.OwnsOne(x => x.ReferenceOnDerived, bb =>
+ {
+ bb.ToJson();
+ bb.OwnsOne(x => x.OwnedReferenceLeaf);
+ bb.OwnsMany(x => x.OwnedCollectionLeaf);
+ bb.Property(x => x.Fraction).HasPrecision(18, 2);
+ });
+
+ b.OwnsMany(x => x.CollectionOnDerived, bb =>
+ {
+ bb.ToJson();
+ bb.OwnsOne(x => x.OwnedReferenceLeaf);
+ bb.OwnsMany(x => x.OwnedCollectionLeaf);
+ bb.Property(x => x.Fraction).HasPrecision(18, 2);
+ });
+ });
+
+ modelBuilder.Ignore();
+ modelBuilder.Ignore();
+
+ base.OnModelCreating(modelBuilder, context);
+ }
+
+ protected override void Seed(JsonQueryContext context)
+ {
+ var jsonEntitiesBasic = JsonQueryData.CreateJsonEntitiesBasic();
+ var jsonEntitiesInheritance = JsonQueryData.CreateJsonEntitiesInheritance();
+
+ context.JsonEntitiesBasic.AddRange(jsonEntitiesBasic);
+ context.JsonEntitiesInheritance.AddRange(jsonEntitiesInheritance);
+ context.SaveChanges();
+ }
+}
diff --git a/test/EFCore.Relational.Specification.Tests/Update/JsonUpdateTestBase.cs b/test/EFCore.Relational.Specification.Tests/Update/JsonUpdateTestBase.cs
index b55f4a8c988..50ca05af8aa 100644
--- a/test/EFCore.Relational.Specification.Tests/Update/JsonUpdateTestBase.cs
+++ b/test/EFCore.Relational.Specification.Tests/Update/JsonUpdateTestBase.cs
@@ -5,9 +5,17 @@
namespace Microsoft.EntityFrameworkCore.Update;
-public abstract class JsonUpdateTestBase: SharedStoreFixtureBase
+public abstract class JsonUpdateTestBase : IClassFixture
+ where TFixture : JsonUpdateFixtureBase, new()
{
- protected override string StoreName => "JsonUpdateTest";
+ public TFixture Fixture { get; }
+
+ protected JsonUpdateTestBase(TFixture fixture)
+ {
+ Fixture = fixture;
+ }
+
+ public JsonQueryContext CreateContext() => Fixture.CreateContext();
[ConditionalFact]
public virtual Task Add_entity_with_json()
@@ -42,6 +50,7 @@ public virtual Task Add_entity_with_json()
};
context.Set().Add(newEntity);
+ ClearLog();
await context.SaveChangesAsync();
},
async context =>
@@ -104,6 +113,7 @@ public virtual Task Add_json_reference_root()
OwnedReferenceLeaf = new JsonOwnedLeaf { SomethingSomething = "ss3" }
}
};
+ ClearLog();
await context.SaveChangesAsync();
},
@@ -133,7 +143,7 @@ public virtual Task Add_json_reference_leaf()
{
var query = await context.JsonEntitiesBasic.ToListAsync();
var entity = query.Single();
- entity.OwnedReferenceRoot.OwnedReferenceBranch.OwnedCollectionLeaf = null;
+ entity.OwnedReferenceRoot.OwnedCollectionBranch[0].OwnedReferenceLeaf = null;
await context.SaveChangesAsync();
},
async context =>
@@ -141,16 +151,17 @@ public virtual Task Add_json_reference_leaf()
var query = await context.JsonEntitiesBasic.ToListAsync();
var entity = query.Single();
- Assert.Null(entity.OwnedReferenceRoot.OwnedReferenceBranch.OwnedCollectionLeaf);
+ Assert.Null(entity.OwnedReferenceRoot.OwnedCollectionBranch[0].OwnedReferenceLeaf);
var newLeaf = new JsonOwnedLeaf { SomethingSomething = "ss3" };
- entity.OwnedReferenceRoot.OwnedReferenceBranch.OwnedReferenceLeaf = newLeaf;
-
+ entity.OwnedReferenceRoot.OwnedCollectionBranch[0].OwnedReferenceLeaf = newLeaf;
+
+ ClearLog();
await context.SaveChangesAsync();
},
async context =>
{
var updatedEntity = await context.JsonEntitiesBasic.SingleAsync();
- var updatedReference = updatedEntity.OwnedReferenceRoot.OwnedReferenceBranch.OwnedReferenceLeaf;
+ var updatedReference = updatedEntity.OwnedReferenceRoot.OwnedCollectionBranch[0].OwnedReferenceLeaf;
Assert.Equal("ss3", updatedReference.SomethingSomething);
});
@@ -184,6 +195,7 @@ public virtual Task Add_element_to_json_collection_root()
};
entity.OwnedCollectionRoot.Add(newRoot);
+ ClearLog();
await context.SaveChangesAsync();
},
async context =>
@@ -227,6 +239,7 @@ public virtual Task Add_element_to_json_collection_branch()
};
entity.OwnedReferenceRoot.OwnedCollectionBranch.Add(newBranch);
+ ClearLog();
await context.SaveChangesAsync();
},
async context =>
@@ -255,6 +268,7 @@ public virtual Task Add_element_to_json_collection_leaf()
var entity = query.Single();
var newLeaf = new JsonOwnedLeaf { SomethingSomething = "ss1" };
entity.OwnedReferenceRoot.OwnedReferenceBranch.OwnedCollectionLeaf.Add(newLeaf);
+ ClearLog();
await context.SaveChangesAsync();
},
async context =>
@@ -276,6 +290,7 @@ public virtual Task Delete_entity_with_json()
var entity = query.Single();
context.Set().Remove(entity);
+ ClearLog();
await context.SaveChangesAsync();
},
async context =>
@@ -295,6 +310,7 @@ public virtual Task Delete_json_reference_root()
var query = await context.JsonEntitiesBasic.ToListAsync();
var entity = query.Single();
entity.OwnedReferenceRoot = null;
+ ClearLog();
await context.SaveChangesAsync();
},
async context =>
@@ -313,6 +329,7 @@ public virtual Task Delete_json_reference_leaf()
var query = await context.JsonEntitiesBasic.ToListAsync();
var entity = query.Single();
entity.OwnedReferenceRoot.OwnedReferenceBranch.OwnedReferenceLeaf = null;
+ ClearLog();
await context.SaveChangesAsync();
},
async context =>
@@ -331,6 +348,7 @@ public virtual Task Delete_json_collection_root()
var query = await context.JsonEntitiesBasic.ToListAsync();
var entity = query.Single();
entity.OwnedCollectionRoot = null;
+ ClearLog();
await context.SaveChangesAsync();
},
async context =>
@@ -349,6 +367,7 @@ public virtual Task Delete_json_collection_branch()
var query = await context.JsonEntitiesBasic.ToListAsync();
var entity = query.Single();
entity.OwnedReferenceRoot.OwnedCollectionBranch = null;
+ ClearLog();
await context.SaveChangesAsync();
},
async context =>
@@ -367,6 +386,7 @@ public virtual Task Edit_element_in_json_collection_root1()
var query = await context.JsonEntitiesBasic.ToListAsync();
var entity = query.Single();
entity.OwnedCollectionRoot[0].Name = "Modified";
+ ClearLog();
await context.SaveChangesAsync();
},
async context =>
@@ -387,6 +407,7 @@ public virtual Task Edit_element_in_json_collection_root2()
var query = await context.JsonEntitiesBasic.ToListAsync();
var entity = query.Single();
entity.OwnedCollectionRoot[1].Name = "Modified";
+ ClearLog();
await context.SaveChangesAsync();
},
async context =>
@@ -407,6 +428,7 @@ public virtual Task Edit_element_in_json_collection_branch()
var query = await context.JsonEntitiesBasic.ToListAsync();
var entity = query.Single();
entity.OwnedCollectionRoot[0].OwnedCollectionBranch[0].Date = new DateTime(2111, 11, 11);
+ ClearLog();
await context.SaveChangesAsync();
},
async context =>
@@ -439,6 +461,7 @@ public virtual Task Add_element_to_json_collection_on_derived()
};
entity.CollectionOnDerived.Add(newBranch);
+ ClearLog();
await context.SaveChangesAsync();
},
async context =>
@@ -456,109 +479,134 @@ public virtual Task Add_element_to_json_collection_on_derived()
Assert.Equal("ss2", collectionLeaf[1].SomethingSomething);
});
- public void UseTransaction(DatabaseFacade facade, IDbContextTransaction transaction)
- => facade.UseTransaction(transaction.GetDbTransaction());
-
- protected override void Seed(JsonQueryContext context)
- {
- var jsonEntitiesBasic = JsonQueryData.CreateJsonEntitiesBasic();
- var jsonEntitiesInheritance = JsonQueryData.CreateJsonEntitiesInheritance();
+ [ConditionalFact]
+ public virtual Task Edit_element_in_json_multiple_levels_partial_update()
+ => TestHelpers.ExecuteWithStrategyInTransactionAsync(
+ CreateContext,
+ UseTransaction,
+ async context =>
+ {
+ var query = await context.JsonEntitiesBasic.ToListAsync();
+ var entity = query.Single();
+ entity.OwnedReferenceRoot.OwnedReferenceBranch.Date = new DateTime(2111, 11, 11);
+ entity.OwnedReferenceRoot.Name = "edit";
+ entity.OwnedCollectionRoot[0].OwnedCollectionBranch[1].OwnedCollectionLeaf[0].SomethingSomething = "yet another change";
+ entity.OwnedCollectionRoot[0].OwnedCollectionBranch[1].OwnedCollectionLeaf[1].SomethingSomething = "and another";
+ entity.OwnedCollectionRoot[0].OwnedCollectionBranch[0].OwnedCollectionLeaf[0].SomethingSomething = "...and another";
- context.JsonEntitiesBasic.AddRange(jsonEntitiesBasic);
- context.JsonEntitiesInheritance.AddRange(jsonEntitiesInheritance);
- context.SaveChanges();
- }
+ ClearLog();
+ await context.SaveChangesAsync();
+ },
+ async context =>
+ {
+ var result = await context.Set().SingleAsync();
+ Assert.Equal(new DateTime(2111, 11, 11), result.OwnedReferenceRoot.OwnedReferenceBranch.Date);
+ Assert.Equal("edit", result.OwnedReferenceRoot.Name);
+ Assert.Equal("yet another change", result.OwnedCollectionRoot[0].OwnedCollectionBranch[1].OwnedCollectionLeaf[0].SomethingSomething);
+ Assert.Equal("and another", result.OwnedCollectionRoot[0].OwnedCollectionBranch[1].OwnedCollectionLeaf[1].SomethingSomething);
+ Assert.Equal("...and another", result.OwnedCollectionRoot[0].OwnedCollectionBranch[0].OwnedCollectionLeaf[0].SomethingSomething);
+ });
- protected override void Clean(DbContext context)
- {
- base.Clean(context);
- }
+ [ConditionalFact]
+ public virtual Task Edit_element_in_json_branch_collection_and_add_element_to_the_same_collection()
+ => TestHelpers.ExecuteWithStrategyInTransactionAsync(
+ CreateContext,
+ UseTransaction,
+ async context =>
+ {
+ var query = await context.JsonEntitiesBasic.ToListAsync();
+ var entity = query.Single();
+ entity.OwnedReferenceRoot.OwnedCollectionBranch[0].Fraction = 4321.3m;
+ entity.OwnedReferenceRoot.OwnedCollectionBranch.Add(new JsonOwnedBranch
+ {
+ Date = new DateTime(2222, 11, 11),
+ Enum = JsonEnum.Three,
+ Fraction = 45.32m,
+ OwnedReferenceLeaf = new JsonOwnedLeaf { SomethingSomething = "cc" },
+ });
- protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context)
- {
- modelBuilder.Entity().Property(x => x.Id).ValueGeneratedNever();
- modelBuilder.Entity().OwnsOne(x => x.OwnedReferenceRoot, b =>
- {
- b.ToJson();
- b.WithOwner(x => x.Owner);
- b.OwnsOne(x => x.OwnedReferenceBranch, bb =>
- {
- bb.Property(x => x.Fraction).HasPrecision(18, 2);
- bb.OwnsOne(x => x.OwnedReferenceLeaf).WithOwner(x => x.Parent);
- bb.OwnsMany(x => x.OwnedCollectionLeaf);
- });
- b.OwnsMany(x => x.OwnedCollectionBranch, bb =>
+ ClearLog();
+ await context.SaveChangesAsync();
+ },
+ async context =>
{
- bb.Property(x => x.Fraction).HasPrecision(18, 2);
- bb.OwnsOne(x => x.OwnedReferenceLeaf);
- bb.Navigation(x => x.OwnedReferenceLeaf).IsRequired(false);
- bb.OwnsMany(x => x.OwnedCollectionLeaf).WithOwner(x => x.Parent);
+ var result = await context.Set().SingleAsync();
+ Assert.Equal(4321.3m, result.OwnedReferenceRoot.OwnedCollectionBranch[0].Fraction);
+
+ Assert.Equal(new DateTime(2222, 11, 11), result.OwnedReferenceRoot.OwnedCollectionBranch[2].Date);
+ Assert.Equal(JsonEnum.Three, result.OwnedReferenceRoot.OwnedCollectionBranch[2].Enum);
+ Assert.Equal(45.32m, result.OwnedReferenceRoot.OwnedCollectionBranch[2].Fraction);
+ Assert.Equal("cc", result.OwnedReferenceRoot.OwnedCollectionBranch[2].OwnedReferenceLeaf.SomethingSomething);
});
- });
- modelBuilder.Entity().Navigation(x => x.OwnedReferenceRoot).IsRequired(false);
+ [ConditionalFact]
+ public virtual Task Edit_two_elements_in_the_same_json_collection()
+ => TestHelpers.ExecuteWithStrategyInTransactionAsync(
+ CreateContext,
+ UseTransaction,
+ async context =>
+ {
+ var query = await context.JsonEntitiesBasic.ToListAsync();
+ var entity = query.Single();
+ entity.OwnedReferenceRoot.OwnedCollectionBranch[0].OwnedCollectionLeaf[0].SomethingSomething = "edit1";
+ entity.OwnedReferenceRoot.OwnedCollectionBranch[0].OwnedCollectionLeaf[1].SomethingSomething = "edit2";
- modelBuilder.Entity().OwnsMany(x => x.OwnedCollectionRoot, b =>
- {
- b.OwnsOne(x => x.OwnedReferenceBranch, bb =>
+ ClearLog();
+ await context.SaveChangesAsync();
+ },
+ async context =>
{
- bb.Property(x => x.Fraction).HasPrecision(18, 2);
- bb.OwnsOne(x => x.OwnedReferenceLeaf);
- bb.OwnsMany(x => x.OwnedCollectionLeaf).WithOwner(x => x.Parent);
+ var result = await context.Set().SingleAsync();
+ Assert.Equal("edit1", result.OwnedReferenceRoot.OwnedCollectionBranch[0].OwnedCollectionLeaf[0].SomethingSomething);
+ Assert.Equal("edit2", result.OwnedReferenceRoot.OwnedCollectionBranch[0].OwnedCollectionLeaf[1].SomethingSomething);
});
- b.OwnsMany(x => x.OwnedCollectionBranch, bb =>
+ [ConditionalFact]
+ public virtual Task Edit_two_elements_in_the_same_json_collection_at_the_root()
+ => TestHelpers.ExecuteWithStrategyInTransactionAsync(
+ CreateContext,
+ UseTransaction,
+ async context =>
{
- bb.Property(x => x.Fraction).HasPrecision(18, 2);
- bb.OwnsOne(x => x.OwnedReferenceLeaf).WithOwner(x => x.Parent);
- bb.OwnsMany(x => x.OwnedCollectionLeaf);
- });
- b.ToJson();
- });
-
- modelBuilder.Entity().Property(x => x.Id).ValueGeneratedNever();
- modelBuilder.Entity(b =>
- {
- b.OwnsOne(x => x.ReferenceOnBase, bb =>
- {
- bb.ToJson();
- bb.OwnsOne(x => x.OwnedReferenceLeaf);
- bb.OwnsMany(x => x.OwnedCollectionLeaf);
- bb.Property(x => x.Fraction).HasPrecision(18, 2);
- });
+ var query = await context.JsonEntitiesBasic.ToListAsync();
+ var entity = query.Single();
+ entity.OwnedCollectionRoot[0].Name = "edit1";
+ entity.OwnedCollectionRoot[1].Name = "edit2";
- b.OwnsMany(x => x.CollectionOnBase, bb =>
+ ClearLog();
+ await context.SaveChangesAsync();
+ },
+ async context =>
{
- bb.ToJson();
- bb.OwnsOne(x => x.OwnedReferenceLeaf);
- bb.OwnsMany(x => x.OwnedCollectionLeaf);
- bb.Property(x => x.Fraction).HasPrecision(18, 2);
+ var result = await context.Set().SingleAsync();
+ Assert.Equal("edit1", result.OwnedCollectionRoot[0].Name);
+ Assert.Equal("edit2", result.OwnedCollectionRoot[1].Name);
});
- });
- modelBuilder.Entity(b =>
- {
- b.HasBaseType();
- b.OwnsOne(x => x.ReferenceOnDerived, bb =>
+ [ConditionalFact]
+ public virtual Task Edit_collection_element_and_reference_at_once()
+ => TestHelpers.ExecuteWithStrategyInTransactionAsync(
+ CreateContext,
+ UseTransaction,
+ async context =>
{
- bb.ToJson();
- bb.OwnsOne(x => x.OwnedReferenceLeaf);
- bb.OwnsMany(x => x.OwnedCollectionLeaf);
- bb.Property(x => x.Fraction).HasPrecision(18, 2);
- });
+ var query = await context.JsonEntitiesBasic.ToListAsync();
+ var entity = query.Single();
+ entity.OwnedReferenceRoot.OwnedCollectionBranch[1].OwnedCollectionLeaf[0].SomethingSomething = "edit1";
+ entity.OwnedReferenceRoot.OwnedCollectionBranch[1].OwnedReferenceLeaf.SomethingSomething = "edit2";
- b.OwnsMany(x => x.CollectionOnDerived, bb =>
+ ClearLog();
+ await context.SaveChangesAsync();
+ },
+ async context =>
{
- bb.ToJson();
- bb.OwnsOne(x => x.OwnedReferenceLeaf);
- bb.OwnsMany(x => x.OwnedCollectionLeaf);
- bb.Property(x => x.Fraction).HasPrecision(18, 2);
+ var result = await context.Set().SingleAsync();
+ Assert.Equal("edit1", result.OwnedReferenceRoot.OwnedCollectionBranch[1].OwnedCollectionLeaf[0].SomethingSomething);
+ Assert.Equal("edit2", result.OwnedReferenceRoot.OwnedCollectionBranch[1].OwnedReferenceLeaf.SomethingSomething);
});
- });
- modelBuilder.Ignore();
- modelBuilder.Ignore();
+ public void UseTransaction(DatabaseFacade facade, IDbContextTransaction transaction)
+ => facade.UseTransaction(transaction.GetDbTransaction());
- base.OnModelCreating(modelBuilder, context);
- }
+ protected abstract void ClearLog();
}
diff --git a/test/EFCore.SqlServer.FunctionalTests/Update/SqlServerJsonUpdateTest.cs b/test/EFCore.SqlServer.FunctionalTests/Update/JsonUpdateSqlServerFixture.cs
similarity index 82%
rename from test/EFCore.SqlServer.FunctionalTests/Update/SqlServerJsonUpdateTest.cs
rename to test/EFCore.SqlServer.FunctionalTests/Update/JsonUpdateSqlServerFixture.cs
index 849d21649ef..cdee2fa67bf 100644
--- a/test/EFCore.SqlServer.FunctionalTests/Update/SqlServerJsonUpdateTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/Update/JsonUpdateSqlServerFixture.cs
@@ -3,7 +3,7 @@
namespace Microsoft.EntityFrameworkCore.Update;
-public class SqlServerJsonUpdateTest : JsonUpdateTestBase
+public class JsonUpdateSqlServerFixture : JsonUpdateFixtureBase
{
protected override ITestStoreFactory TestStoreFactory
=> SqlServerTestStoreFactory.Instance;
diff --git a/test/EFCore.SqlServer.FunctionalTests/Update/JsonUpdateSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Update/JsonUpdateSqlServerTest.cs
new file mode 100644
index 00000000000..4cb48b4afda
--- /dev/null
+++ b/test/EFCore.SqlServer.FunctionalTests/Update/JsonUpdateSqlServerTest.cs
@@ -0,0 +1,379 @@
+// 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.Update;
+
+public class JsonUpdateSqlServerTest : JsonUpdateTestBase
+{
+ public JsonUpdateSqlServerTest(JsonUpdateSqlServerFixture fixture)
+ : base(fixture)
+ {
+ ClearLog();
+ }
+
+ public override async Task Add_element_to_json_collection_branch()
+ {
+ await base.Add_element_to_json_collection_branch();
+
+ AssertSql(
+ @"@p0='[{""Date"":""2101-01-01T00:00:00"",""Enum"":""Two"",""Fraction"":10.1,""OwnedCollectionLeaf"":[{""SomethingSomething"":""e1_r_c1_c1""},{""SomethingSomething"":""e1_r_c1_c2""}],""OwnedReferenceLeaf"":{""SomethingSomething"":""e1_r_c1_r""}},{""Date"":""2102-01-01T00:00:00"",""Enum"":""Three"",""Fraction"":10.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"":""Three"",""Fraction"":42.42,""OwnedCollectionLeaf"":[{""SomethingSomething"":""ss1""},{""SomethingSomething"":""ss2""}],""OwnedReferenceLeaf"":{""SomethingSomething"":""ss3""}}]' (Nullable = false) (Size = 622)
+@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;",
+ //
+ @"SELECT TOP(2) [j].[Id], [j].[Name], JSON_QUERY([j].[OwnedCollectionRoot],'$'), JSON_QUERY([j].[OwnedReferenceRoot],'$')
+FROM [JsonEntitiesBasic] AS [j]");
+ }
+
+ public override async Task Add_element_to_json_collection_leaf()
+ {
+ await base.Add_element_to_json_collection_leaf();
+
+ 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;",
+ //
+ @"SELECT TOP(2) [j].[Id], [j].[Name], JSON_QUERY([j].[OwnedCollectionRoot],'$'), JSON_QUERY([j].[OwnedReferenceRoot],'$')
+FROM [JsonEntitiesBasic] AS [j]");
+ }
+
+ 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"":""Two"",""Fraction"":221.1,""OwnedCollectionLeaf"":[{""SomethingSomething"":""d2_r_c1""},{""SomethingSomething"":""d2_r_c2""}],""OwnedReferenceLeaf"":{""SomethingSomething"":""d2_r_r""}},{""Date"":""2222-01-01T00:00:00"",""Enum"":""Three"",""Fraction"":222.1,""OwnedCollectionLeaf"":[{""SomethingSomething"":""d2_r_c1""},{""SomethingSomething"":""d2_r_c2""}],""OwnedReferenceLeaf"":{""SomethingSomething"":""d2_r_r""}},{""Date"":""2010-10-10T00:00:00"",""Enum"":""Three"",""Fraction"":42.42,""OwnedCollectionLeaf"":[{""SomethingSomething"":""ss1""},{""SomethingSomething"":""ss2""}],""OwnedReferenceLeaf"":{""SomethingSomething"":""ss3""}}]' (Nullable = false) (Size = 606)
+@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], JSON_QUERY([j].[CollectionOnBase],'$'), JSON_QUERY([j].[ReferenceOnBase],'$'), JSON_QUERY([j].[CollectionOnDerived],'$'), JSON_QUERY([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"",""Number"":11,""OwnedCollectionBranch"":[{""Date"":""2111-01-01T00:00:00"",""Enum"":""Two"",""Fraction"":11.1,""OwnedCollectionLeaf"":[{""SomethingSomething"":""e1_c1_c1_c1""},{""SomethingSomething"":""e1_c1_c1_c2""}],""OwnedReferenceLeaf"":{""SomethingSomething"":""e1_c1_c1_r""}},{""Date"":""2112-01-01T00:00:00"",""Enum"":""Three"",""Fraction"":11.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"":""One"",""Fraction"":11.0,""OwnedCollectionLeaf"":[{""SomethingSomething"":""e1_c1_r_c1""},{""SomethingSomething"":""e1_c1_r_c2""}],""OwnedReferenceLeaf"":{""SomethingSomething"":""e1_c1_r_r""}}},{""Name"":""e1_c2"",""Number"":12,""OwnedCollectionBranch"":[{""Date"":""2121-01-01T00:00:00"",""Enum"":""Two"",""Fraction"":12.1,""OwnedCollectionLeaf"":[{""SomethingSomething"":""e1_c2_c1_c1""},{""SomethingSomething"":""e1_c2_c1_c2""}],""OwnedReferenceLeaf"":{""SomethingSomething"":""e1_c2_c1_r""}},{""Date"":""2122-01-01T00:00:00"",""Enum"":""One"",""Fraction"":12.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"":""Three"",""Fraction"":12.0,""OwnedCollectionLeaf"":[{""SomethingSomething"":""e1_c2_r_c1""},{""SomethingSomething"":""e1_c2_r_c2""}],""OwnedReferenceLeaf"":{""SomethingSomething"":""e1_c2_r_r""}}},{""Name"":""new Name"",""Number"":142,""OwnedCollectionBranch"":[],""OwnedReferenceBranch"":{""Date"":""2010-10-10T00:00:00"",""Enum"":""Three"",""Fraction"":42.42,""OwnedCollectionLeaf"":[{""SomethingSomething"":""ss1""},{""SomethingSomething"":""ss2""}],""OwnedReferenceLeaf"":{""SomethingSomething"":""ss3""}}}]' (Nullable = false) (Size = 1723)
+@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].[Name], JSON_QUERY([j].[OwnedCollectionRoot],'$'), JSON_QUERY([j].[OwnedReferenceRoot],'$')
+FROM [JsonEntitiesBasic] AS [j]");
+ }
+
+ public override async Task Add_entity_with_json()
+ {
+ await base.Add_entity_with_json();
+
+ AssertSql(
+ @"@p0='{""Name"":""RootName"",""Number"":42,""OwnedCollectionBranch"":[],""OwnedReferenceBranch"":{""Date"":""2010-10-10T00:00:00"",""Enum"":""Three"",""Fraction"":42.42,""OwnedCollectionLeaf"":[{""SomethingSomething"":""ss1""},{""SomethingSomething"":""ss2""}],""OwnedReferenceLeaf"":{""SomethingSomething"":""ss3""}}}' (Nullable = false) (Size = 276)
+@p1='2'
+@p2='NewEntity' (Size = 4000)
+
+SET IMPLICIT_TRANSACTIONS OFF;
+SET NOCOUNT ON;
+INSERT INTO [JsonEntitiesBasic] ([OwnedReferenceRoot], [Id], [Name])
+VALUES (@p0, @p1, @p2);",
+ //
+ @"SELECT [j].[Id], [j].[Name], JSON_QUERY([j].[OwnedCollectionRoot],'$'), JSON_QUERY([j].[OwnedReferenceRoot],'$')
+FROM [JsonEntitiesBasic] AS [j]");
+ }
+
+ public override async Task Add_json_reference_leaf()
+ {
+ await base.Add_json_reference_leaf();
+
+ AssertSql(
+ @"@p0='{""SomethingSomething"":""ss3""}' (Nullable = false) (Size = 28)
+@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;",
+ //
+ @"SELECT TOP(2) [j].[Id], [j].[Name], JSON_QUERY([j].[OwnedCollectionRoot],'$'), JSON_QUERY([j].[OwnedReferenceRoot],'$')
+FROM [JsonEntitiesBasic] AS [j]");
+ }
+
+ public override async Task Add_json_reference_root()
+ {
+ await base.Add_json_reference_root();
+
+ AssertSql(
+ @"@p0='{""Name"":""RootName"",""Number"":42,""OwnedCollectionBranch"":[],""OwnedReferenceBranch"":{""Date"":""2010-10-10T00:00:00"",""Enum"":""Three"",""Fraction"":42.42,""OwnedCollectionLeaf"":[{""SomethingSomething"":""ss1""},{""SomethingSomething"":""ss2""}],""OwnedReferenceLeaf"":{""SomethingSomething"":""ss3""}}}' (Nullable = false) (Size = 276)
+@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].[Name], JSON_QUERY([j].[OwnedCollectionRoot],'$'), JSON_QUERY([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()
+ {
+ await base.Delete_json_collection_branch();
+
+ AssertSql(
+ @"@p0='[]' (Nullable = false) (Size = 2)
+@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;",
+ //
+ @"SELECT TOP(2) [j].[Id], [j].[Name], JSON_QUERY([j].[OwnedCollectionRoot],'$'), JSON_QUERY([j].[OwnedReferenceRoot],'$')
+FROM [JsonEntitiesBasic] AS [j]");
+ }
+
+ public override async Task Delete_json_collection_root()
+ {
+ await base.Delete_json_collection_root();
+
+ AssertSql(
+ @"@p0='[]' (Nullable = false) (Size = 2)
+@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].[Name], JSON_QUERY([j].[OwnedCollectionRoot],'$'), JSON_QUERY([j].[OwnedReferenceRoot],'$')
+FROM [JsonEntitiesBasic] AS [j]");
+ }
+
+ public override async Task Delete_json_reference_leaf()
+ {
+ await base.Delete_json_reference_leaf();
+
+ 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;",
+ //
+ @"SELECT TOP(2) [j].[Id], [j].[Name], JSON_QUERY([j].[OwnedCollectionRoot],'$'), JSON_QUERY([j].[OwnedReferenceRoot],'$')
+FROM [JsonEntitiesBasic] AS [j]");
+ }
+
+ public override async Task Delete_json_reference_root()
+ {
+ await 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].[Name], JSON_QUERY([j].[OwnedCollectionRoot],'$'), JSON_QUERY([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='{""Date"":""2111-11-11T00:00:00"",""Enum"":""Two"",""Fraction"":11.1,""OwnedCollectionLeaf"":[{""SomethingSomething"":""e1_c1_c1_c1""},{""SomethingSomething"":""e1_c1_c1_c2""}],""OwnedReferenceLeaf"":{""SomethingSomething"":""e1_c1_c1_r""}}' (Nullable = false) (Size = 214)
+@p1='1'
+
+SET IMPLICIT_TRANSACTIONS OFF;
+SET NOCOUNT ON;
+UPDATE [JsonEntitiesBasic] SET [OwnedCollectionRoot] = JSON_MODIFY([OwnedCollectionRoot], 'strict $[0].OwnedCollectionBranch[0]', JSON_QUERY(@p0))
+OUTPUT 1
+WHERE [Id] = @p1;",
+ //
+ @"SELECT TOP(2) [j].[Id], [j].[Name], JSON_QUERY([j].[OwnedCollectionRoot],'$'), JSON_QUERY([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='{""Name"":""Modified"",""Number"":11,""OwnedCollectionBranch"":[{""Date"":""2111-01-01T00:00:00"",""Enum"":""Two"",""Fraction"":11.1,""OwnedCollectionLeaf"":[{""SomethingSomething"":""e1_c1_c1_c1""},{""SomethingSomething"":""e1_c1_c1_c2""}],""OwnedReferenceLeaf"":{""SomethingSomething"":""e1_c1_c1_r""}},{""Date"":""2112-01-01T00:00:00"",""Enum"":""Three"",""Fraction"":11.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"":""One"",""Fraction"":11.0,""OwnedCollectionLeaf"":[{""SomethingSomething"":""e1_c1_r_c1""},{""SomethingSomething"":""e1_c1_r_c2""}],""OwnedReferenceLeaf"":{""SomethingSomething"":""e1_c1_r_r""}}}' (Nullable = false) (Size = 724)
+@p1='1'
+
+SET IMPLICIT_TRANSACTIONS OFF;
+SET NOCOUNT ON;
+UPDATE [JsonEntitiesBasic] SET [OwnedCollectionRoot] = JSON_MODIFY([OwnedCollectionRoot], 'strict $[0]', JSON_QUERY(@p0))
+OUTPUT 1
+WHERE [Id] = @p1;",
+ //
+ @"SELECT TOP(2) [j].[Id], [j].[Name], JSON_QUERY([j].[OwnedCollectionRoot],'$'), JSON_QUERY([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='{""Name"":""Modified"",""Number"":12,""OwnedCollectionBranch"":[{""Date"":""2121-01-01T00:00:00"",""Enum"":""Two"",""Fraction"":12.1,""OwnedCollectionLeaf"":[{""SomethingSomething"":""e1_c2_c1_c1""},{""SomethingSomething"":""e1_c2_c1_c2""}],""OwnedReferenceLeaf"":{""SomethingSomething"":""e1_c2_c1_r""}},{""Date"":""2122-01-01T00:00:00"",""Enum"":""One"",""Fraction"":12.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"":""Three"",""Fraction"":12.0,""OwnedCollectionLeaf"":[{""SomethingSomething"":""e1_c2_r_c1""},{""SomethingSomething"":""e1_c2_r_c2""}],""OwnedReferenceLeaf"":{""SomethingSomething"":""e1_c2_r_r""}}}' (Nullable = false) (Size = 724)
+@p1='1'
+
+SET IMPLICIT_TRANSACTIONS OFF;
+SET NOCOUNT ON;
+UPDATE [JsonEntitiesBasic] SET [OwnedCollectionRoot] = JSON_MODIFY([OwnedCollectionRoot], 'strict $[1]', JSON_QUERY(@p0))
+OUTPUT 1
+WHERE [Id] = @p1;",
+ //
+ @"SELECT TOP(2) [j].[Id], [j].[Name], JSON_QUERY([j].[OwnedCollectionRoot],'$'), JSON_QUERY([j].[OwnedReferenceRoot],'$')
+FROM [JsonEntitiesBasic] AS [j]");
+ }
+
+ public override async Task Edit_element_in_json_multiple_levels_partial_update()
+ {
+ await base.Edit_element_in_json_multiple_levels_partial_update();
+
+ AssertSql(
+ @"@p0='[{""Date"":""2111-01-01T00:00:00"",""Enum"":""Two"",""Fraction"":11.1,""OwnedCollectionLeaf"":[{""SomethingSomething"":""...and another""},{""SomethingSomething"":""e1_c1_c1_c2""}],""OwnedReferenceLeaf"":{""SomethingSomething"":""e1_c1_c1_r""}},{""Date"":""2112-01-01T00:00:00"",""Enum"":""Three"",""Fraction"":11.2,""OwnedCollectionLeaf"":[{""SomethingSomething"":""yet another change""},{""SomethingSomething"":""and another""}],""OwnedReferenceLeaf"":{""SomethingSomething"":""e1_c1_c2_r""}}]' (Nullable = false) (Size = 443)
+@p1='{""Name"":""edit"",""Number"":10,""OwnedCollectionBranch"":[{""Date"":""2101-01-01T00:00:00"",""Enum"":""Two"",""Fraction"":10.1,""OwnedCollectionLeaf"":[{""SomethingSomething"":""e1_r_c1_c1""},{""SomethingSomething"":""e1_r_c1_c2""}],""OwnedReferenceLeaf"":{""SomethingSomething"":""e1_r_c1_r""}},{""Date"":""2102-01-01T00:00:00"",""Enum"":""Three"",""Fraction"":10.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"":""One"",""Fraction"":10.0,""OwnedCollectionLeaf"":[{""SomethingSomething"":""e1_r_r_c1""},{""SomethingSomething"":""e1_r_r_c2""}],""OwnedReferenceLeaf"":{""SomethingSomething"":""e1_r_r_r""}}}' (Nullable = false) (Size = 711)
+@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;",
+ //
+ @"SELECT TOP(2) [j].[Id], [j].[Name], JSON_QUERY([j].[OwnedCollectionRoot],'$'), JSON_QUERY([j].[OwnedReferenceRoot],'$')
+FROM [JsonEntitiesBasic] AS [j]");
+ }
+
+ public override async Task Edit_element_in_json_branch_collection_and_add_element_to_the_same_collection()
+ {
+ await base.Edit_element_in_json_branch_collection_and_add_element_to_the_same_collection();
+
+ AssertSql(
+ @"@p0='[{""Date"":""2101-01-01T00:00:00"",""Enum"":""Two"",""Fraction"":4321.3,""OwnedCollectionLeaf"":[{""SomethingSomething"":""e1_r_c1_c1""},{""SomethingSomething"":""e1_r_c1_c2""}],""OwnedReferenceLeaf"":{""SomethingSomething"":""e1_r_c1_r""}},{""Date"":""2102-01-01T00:00:00"",""Enum"":""Three"",""Fraction"":10.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"":""Three"",""Fraction"":45.32,""OwnedCollectionLeaf"":[],""OwnedReferenceLeaf"":{""SomethingSomething"":""cc""}}]' (Nullable = false) (Size = 566)
+@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;",
+ //
+ @"SELECT TOP(2) [j].[Id], [j].[Name], JSON_QUERY([j].[OwnedCollectionRoot],'$'), JSON_QUERY([j].[OwnedReferenceRoot],'$')
+FROM [JsonEntitiesBasic] AS [j]");
+ }
+
+ public override async Task Edit_two_elements_in_the_same_json_collection()
+ {
+ await base.Edit_two_elements_in_the_same_json_collection();
+
+ 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;",
+ //
+ @"SELECT TOP(2) [j].[Id], [j].[Name], JSON_QUERY([j].[OwnedCollectionRoot],'$'), JSON_QUERY([j].[OwnedReferenceRoot],'$')
+FROM [JsonEntitiesBasic] AS [j]");
+ }
+
+ 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"",""Number"":11,""OwnedCollectionBranch"":[{""Date"":""2111-01-01T00:00:00"",""Enum"":""Two"",""Fraction"":11.1,""OwnedCollectionLeaf"":[{""SomethingSomething"":""e1_c1_c1_c1""},{""SomethingSomething"":""e1_c1_c1_c2""}],""OwnedReferenceLeaf"":{""SomethingSomething"":""e1_c1_c1_r""}},{""Date"":""2112-01-01T00:00:00"",""Enum"":""Three"",""Fraction"":11.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"":""One"",""Fraction"":11.0,""OwnedCollectionLeaf"":[{""SomethingSomething"":""e1_c1_r_c1""},{""SomethingSomething"":""e1_c1_r_c2""}],""OwnedReferenceLeaf"":{""SomethingSomething"":""e1_c1_r_r""}}},{""Name"":""edit2"",""Number"":12,""OwnedCollectionBranch"":[{""Date"":""2121-01-01T00:00:00"",""Enum"":""Two"",""Fraction"":12.1,""OwnedCollectionLeaf"":[{""SomethingSomething"":""e1_c2_c1_c1""},{""SomethingSomething"":""e1_c2_c1_c2""}],""OwnedReferenceLeaf"":{""SomethingSomething"":""e1_c2_c1_r""}},{""Date"":""2122-01-01T00:00:00"",""Enum"":""One"",""Fraction"":12.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"":""Three"",""Fraction"":12.0,""OwnedCollectionLeaf"":[{""SomethingSomething"":""e1_c2_r_c1""},{""SomethingSomething"":""e1_c2_r_c2""}],""OwnedReferenceLeaf"":{""SomethingSomething"":""e1_c2_r_r""}}}]' (Nullable = false) (Size = 1445)
+@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].[Name], JSON_QUERY([j].[OwnedCollectionRoot],'$'), JSON_QUERY([j].[OwnedReferenceRoot],'$')
+FROM [JsonEntitiesBasic] AS [j]");
+ }
+
+ public override async Task Edit_collection_element_and_reference_at_once()
+ {
+ await base.Edit_collection_element_and_reference_at_once();
+
+ AssertSql(
+ @"@p0='{""Date"":""2102-01-01T00:00:00"",""Enum"":""Three"",""Fraction"":10.2,""OwnedCollectionLeaf"":[{""SomethingSomething"":""edit1""},{""SomethingSomething"":""e1_r_c2_c2""}],""OwnedReferenceLeaf"":{""SomethingSomething"":""edit2""}}' (Nullable = false) (Size = 204)
+@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;",
+ //
+ @"SELECT TOP(2) [j].[Id], [j].[Name], JSON_QUERY([j].[OwnedCollectionRoot],'$'), JSON_QUERY([j].[OwnedReferenceRoot],'$')
+FROM [JsonEntitiesBasic] AS [j]");
+ }
+
+ protected override void ClearLog() => Fixture.TestSqlLoggerFactory.Clear();
+
+ private void AssertSql(params string[] expected)
+ => Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
+}
diff --git a/test/EFCore.Sqlite.FunctionalTests/SqliteComplianceTest.cs b/test/EFCore.Sqlite.FunctionalTests/SqliteComplianceTest.cs
index c3d15497ae7..0aba24228e3 100644
--- a/test/EFCore.Sqlite.FunctionalTests/SqliteComplianceTest.cs
+++ b/test/EFCore.Sqlite.FunctionalTests/SqliteComplianceTest.cs
@@ -9,7 +9,7 @@ public class SqliteComplianceTest : RelationalComplianceTestBase
{
typeof(FromSqlSprocQueryTestBase<>),
typeof(JsonQueryTestBase<>),
- typeof(JsonUpdateTestBase),
+ typeof(JsonUpdateTestBase<>),
typeof(SqlExecutorTestBase<>),
typeof(UdfDbFunctionTestBase<>),
typeof(TPCRelationshipsQueryTestBase<>), // internal class is added