diff --git a/src/EFCore.Relational/Update/ColumnModificationParameters.cs b/src/EFCore.Relational/Update/ColumnModificationParameters.cs
index 7ddd7ce9150..1a613ba3bac 100644
--- a/src/EFCore.Relational/Update/ColumnModificationParameters.cs
+++ b/src/EFCore.Relational/Update/ColumnModificationParameters.cs
@@ -236,14 +236,14 @@ public ColumnModificationParameters(
}
///
- /// Creates a new instance.
+ /// Creates a new instance specific for updating objects mapped to JSON column.
///
- /// 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 name of the JSON column.
+ /// The current value of the JSON element located at the given JSON path.
+ /// In case of JSON column single scalar property modification, the scalar property that is being modified, null otherwise.
+ /// The database type of the JSON column.
/// The relational type mapping to be used for the command parameter.
+ /// The JSON path leading to the JSON element that needs to be updated.
/// 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.
@@ -252,8 +252,8 @@ public ColumnModificationParameters(
/// A value indicating whether the value could be null.
public ColumnModificationParameters(
string columnName,
- object? originalValue,
object? value,
+ IProperty? property,
string? columnType,
RelationalTypeMapping? typeMapping,
string jsonPath,
@@ -266,12 +266,11 @@ public ColumnModificationParameters(
{
Column = null;
ColumnName = columnName;
- OriginalValue = originalValue;
+ OriginalValue = null;
Value = value;
- Property = null;
+ Property = property;
ColumnType = columnType;
TypeMapping = typeMapping;
- JsonPath = jsonPath;
IsRead = read;
IsWrite = write;
IsKey = key;
@@ -281,5 +280,6 @@ public ColumnModificationParameters(
GenerateParameterName = null;
Entry = null;
+ JsonPath = jsonPath;
}
}
diff --git a/src/EFCore.Relational/Update/IColumnModification.cs b/src/EFCore.Relational/Update/IColumnModification.cs
index 5e8c3510bd9..c24d009a9db 100644
--- a/src/EFCore.Relational/Update/IColumnModification.cs
+++ b/src/EFCore.Relational/Update/IColumnModification.cs
@@ -28,6 +28,9 @@ public interface IColumnModification
///
/// The property that maps to the column.
///
+ ///
+ /// In case of JSON column single scalar property modification, the scalar property that is being modified.
+ ///
public IProperty? Property { get; }
///
diff --git a/src/EFCore.Relational/Update/ModificationCommand.cs b/src/EFCore.Relational/Update/ModificationCommand.cs
index 4dc4d9f55a5..90c2824af6a 100644
--- a/src/EFCore.Relational/Update/ModificationCommand.cs
+++ b/src/EFCore.Relational/Update/ModificationCommand.cs
@@ -3,6 +3,7 @@
using System.Collections;
using System.Data;
+using System.Text.Json;
using System.Text.Json.Nodes;
using Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
using Microsoft.EntityFrameworkCore.Internal;
@@ -251,6 +252,13 @@ public virtual IColumnModification AddColumnModification(in ColumnModificationPa
protected virtual IColumnModification CreateColumnModification(in ColumnModificationParameters columnModificationParameters)
=> new ColumnModification(columnModificationParameters);
+ private sealed class JsonPartialUpdateInfo
+ {
+ public List Path { get; } = new();
+ public IProperty? Property { get; set; }
+ public object? PropertyValue { get; set; }
+ }
+
private record struct JsonPartialUpdatePathEntry
{
public JsonPartialUpdatePathEntry(
@@ -322,19 +330,16 @@ private List GenerateColumnModifications()
if (jsonEntry)
{
- var jsonColumnsUpdateMap = new Dictionary>();
+ var jsonColumnsUpdateMap = new Dictionary();
var processedEntries = new List();
foreach (var entry in _entries.Where(e => e.EntityType.IsMappedToJson()))
{
- var modifiedMembers = entry.ToEntityEntry().Members.Where(m => m is not NavigationEntry && m.IsModified).ToList();
+ var modifiedMembers = entry.ToEntityEntry().Properties.Where(m => m.IsModified).ToList();
var jsonColumn = entry.EntityType.GetContainerColumnName()!;
var jsonPartialUpdateInfo = FindJsonPartialUpdateInfo(entry, processedEntries);
- processedEntries.Add(entry);
if (jsonPartialUpdateInfo == null)
{
- // 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;
}
@@ -348,52 +353,60 @@ private List GenerateColumnModifications()
jsonColumnsUpdateMap[jsonColumn] = jsonPartialUpdateInfo;
}
- foreach (var (jsonColumnName, updatePath) in jsonColumnsUpdateMap)
+ foreach (var (jsonColumnName, updateInfo) in jsonColumnsUpdateMap)
{
- var finalUpdatePathElement = updatePath.Last();
+ var finalUpdatePathElement = updateInfo.Path.Last();
var navigation = finalUpdatePathElement.Navigation;
var jsonColumnTypeMapping = navigation.TargetEntityType.GetContainerColumnTypeMapping()!;
var navigationValue = finalUpdatePathElement.ParentEntry.GetCurrentValue(navigation);
var json = default(JsonNode?);
- if (finalUpdatePathElement.Ordinal != null && navigationValue != null)
+ var jsonPathString = string.Join(
+ ".", updateInfo.Path.Select(x => x.PropertyName + (x.Ordinal != null ? "[" + x.Ordinal + "]" : "")));
+
+ if (updateInfo.Property != null)
{
- int i = 0;
- foreach (var navigationValueElement in (IEnumerable)navigationValue)
+ json = new JsonArray(JsonValue.Create(updateInfo.PropertyValue));
+ jsonPathString = jsonPathString + "." + updateInfo.Property.GetJsonPropertyName();
+ }
+ else
+ {
+ if (finalUpdatePathElement.Ordinal != null && navigationValue != null)
{
- if (i == finalUpdatePathElement.Ordinal)
+ var i = 0;
+ foreach (var navigationValueElement in (IEnumerable)navigationValue)
{
- json = CreateJson(
- navigationValueElement,
- finalUpdatePathElement.ParentEntry,
- navigation.TargetEntityType,
- ordinal: null,
- isCollection: false);
-
- break;
+ if (i == finalUpdatePathElement.Ordinal)
+ {
+ json = CreateJson(
+ navigationValueElement,
+ finalUpdatePathElement.ParentEntry,
+ navigation.TargetEntityType,
+ ordinal: null,
+ isCollection: false);
+
+ break;
+ }
+
+ i++;
}
-
- i++;
}
- }
- else
- {
- json = CreateJson(
- navigationValue,
- finalUpdatePathElement.ParentEntry,
- navigation.TargetEntityType,
- ordinal: null,
- isCollection: navigation.IsCollection);
+ else
+ {
+ json = CreateJson(
+ navigationValue,
+ finalUpdatePathElement.ParentEntry,
+ navigation.TargetEntityType,
+ 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: updateInfo.Property,
columnType: jsonColumnTypeMapping.StoreType,
jsonColumnTypeMapping,
jsonPath: jsonPathString,
@@ -582,9 +595,9 @@ entry.EntityState is EntityState.Modified or EntityState.Added
return columnModifications;
- static List? FindJsonPartialUpdateInfo(IUpdateEntry entry, List processedEntries)
+ static JsonPartialUpdateInfo? FindJsonPartialUpdateInfo(IUpdateEntry entry, List processedEntries)
{
- var result = new List();
+ var result = new JsonPartialUpdateInfo();
var currentEntry = entry;
var currentOwnership = currentEntry.EntityType.FindOwnership()!;
@@ -617,7 +630,20 @@ entry.EntityState is EntityState.Modified or EntityState.Added
currentEntry,
currentOwnership.GetNavigation(pointsToPrincipal: false)!);
- result.Insert(0, pathEntry);
+ result.Path.Insert(0, pathEntry);
+ }
+
+ var modifiedMembers = entry.ToEntityEntry().Properties.Where(m => m.IsModified).ToList();
+ if (modifiedMembers.Count == 1)
+ {
+ result.Property = modifiedMembers.Single().Metadata;
+ result.PropertyValue = entry.GetCurrentProviderValue(result.Property);
+ }
+ else
+ {
+ // only add to processed entries list if we are planning to update the entire entity
+ // (rather than just a single property on that entity)
+ processedEntries.Add(entry);
}
// parent entity got deleted, no need to do any json-specific processing
@@ -629,36 +655,36 @@ entry.EntityState is EntityState.Modified or EntityState.Added
return result;
}
- static List FindCommonJsonPartialUpdateInfo(
- List first,
- List second)
+ static JsonPartialUpdateInfo FindCommonJsonPartialUpdateInfo(
+ JsonPartialUpdateInfo first,
+ JsonPartialUpdateInfo second)
{
- var result = new List();
- for (var i = 0; i < Math.Min(first.Count, second.Count); i++)
+ var result = new JsonPartialUpdateInfo();
+ for (var i = 0; i < Math.Min(first.Path.Count, second.Path.Count); i++)
{
- if (first[i].PropertyName == second[i].PropertyName)
+ if (first.Path[i].PropertyName == second.Path[i].PropertyName)
{
- if (first[i].Ordinal == second[i].Ordinal)
+ if (first.Path[i].Ordinal == second.Path[i].Ordinal)
{
- result.Add(first[i]);
+ result.Path.Add(first.Path[i]);
continue;
}
else
{
var common = new JsonPartialUpdatePathEntry(
- first[i].PropertyName,
+ first.Path[i].PropertyName,
null,
- first[i].ParentEntry,
- first[i].Navigation);
+ first.Path[i].ParentEntry,
+ first.Path[i].Navigation);
- result.Add(common);
+ result.Path.Add(common);
}
break;
}
}
- Debug.Assert(result.Count > 0, "Common denominator should always have at least one node - the root.");
+ Debug.Assert(result.Path.Count > 0, "Common denominator should always have at least one node - the root.");
return result;
}
diff --git a/src/EFCore.SqlServer/Update/Internal/SqlServerUpdateSqlGenerator.cs b/src/EFCore.SqlServer/Update/Internal/SqlServerUpdateSqlGenerator.cs
index 960161c7e6a..63feb980ccd 100644
--- a/src/EFCore.SqlServer/Update/Internal/SqlServerUpdateSqlGenerator.cs
+++ b/src/EFCore.SqlServer/Update/Internal/SqlServerUpdateSqlGenerator.cs
@@ -4,6 +4,7 @@
using System.Data;
using System.Globalization;
using System.Text;
+using Microsoft.Extensions.Primitives;
namespace Microsoft.EntityFrameworkCore.SqlServer.Update.Internal;
@@ -152,9 +153,37 @@ protected override void AppendUpdateColumnValue(
// 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("))");
+ stringBuilder.Append("', ");
+
+ if (columnModification.Property != null)
+ {
+ var needsTypeConversion = columnModification.Property.ClrType.IsNumeric()
+ || columnModification.Property.ClrType == typeof(bool);
+
+ if (needsTypeConversion)
+ {
+ stringBuilder.Append("CAST(");
+ }
+
+ stringBuilder.Append("JSON_VALUE(");
+ base.AppendUpdateColumnValue(updateSqlGeneratorHelper, columnModification, stringBuilder, name, schema);
+ stringBuilder.Append(", '$[0]')");
+
+ if (needsTypeConversion)
+ {
+ stringBuilder.Append(" AS ");
+ stringBuilder.Append(columnModification.Property.GetRelationalTypeMapping().StoreType);
+ stringBuilder.Append(")");
+ }
+ }
+ else
+ {
+ stringBuilder.Append("JSON_QUERY(");
+ base.AppendUpdateColumnValue(updateSqlGeneratorHelper, columnModification, stringBuilder, name, schema);
+ stringBuilder.Append(")");
+ }
+
+ stringBuilder.Append(")");
}
else
{
diff --git a/test/EFCore.Relational.Specification.Tests/Query/JsonQueryFixtureBase.cs b/test/EFCore.Relational.Specification.Tests/Query/JsonQueryFixtureBase.cs
index a6e3719e4bb..d97451abc4f 100644
--- a/test/EFCore.Relational.Specification.Tests/Query/JsonQueryFixtureBase.cs
+++ b/test/EFCore.Relational.Specification.Tests/Query/JsonQueryFixtureBase.cs
@@ -31,6 +31,7 @@ public virtual ISetSource GetExpectedData()
{ typeof(JsonEntitySingleOwned), e => ((JsonEntitySingleOwned)e)?.Id },
{ typeof(JsonEntityInheritanceBase), e => ((JsonEntityInheritanceBase)e)?.Id },
{ typeof(JsonEntityInheritanceDerived), e => ((JsonEntityInheritanceDerived)e)?.Id },
+ { typeof(JsonEntityAllTypes), e => ((JsonEntityAllTypes)e)?.Id },
}.ToDictionary(e => e.Key, e => (object)e.Value);
public IReadOnlyDictionary EntityAsserters { get; } = new Dictionary>
@@ -240,6 +241,27 @@ public virtual ISetSource GetExpectedData()
}
}
},
+ {
+ typeof(JsonEntityAllTypes), (e, a) =>
+ {
+ Assert.Equal(e == null, a == null);
+ if (a != null)
+ {
+ var ee = (JsonEntityAllTypes)e;
+ var aa = (JsonEntityAllTypes)a;
+
+ Assert.Equal(ee.Id, aa.Id);
+
+ AssertAllTypes(ee.Reference, aa.Reference);
+
+ Assert.Equal(ee.Collection?.Count ?? 0, aa.Collection?.Count ?? 0);
+ for (var i = 0; i < ee.Collection.Count; i++)
+ {
+ AssertAllTypes(ee.Collection[i], aa.Collection[i]);
+ }
+ }
+ }
+ },
}.ToDictionary(e => e.Key, e => (object)e.Value);
private static void AssertOwnedRoot(JsonOwnedRoot expected, JsonOwnedRoot actual)
@@ -293,6 +315,25 @@ public static void AssertCustomNameBranch(JsonOwnedCustomNameBranch expected, Js
Assert.Equal(expected.Fraction, actual.Fraction);
}
+ public static void AssertAllTypes(JsonOwnedAllTypes expected, JsonOwnedAllTypes actual)
+ {
+ Assert.Equal(expected.TestBoolean, actual.TestBoolean);
+ Assert.Equal(expected.TestCharacter, actual.TestCharacter);
+ Assert.Equal(expected.TestDateTime, actual.TestDateTime);
+ Assert.Equal(expected.TestDateTimeOffset, actual.TestDateTimeOffset);
+ Assert.Equal(expected.TestDouble, actual.TestDouble);
+ Assert.Equal(expected.TestGuid, actual.TestGuid);
+ Assert.Equal(expected.TestInt16, actual.TestInt16);
+ Assert.Equal(expected.TestInt32, actual.TestInt32);
+ Assert.Equal(expected.TestInt64, actual.TestInt64);
+ Assert.Equal(expected.TestSignedByte, actual.TestSignedByte);
+ Assert.Equal(expected.TestSingle, actual.TestSingle);
+ Assert.Equal(expected.TestTimeSpan, actual.TestTimeSpan);
+ Assert.Equal(expected.TestUnsignedInt16, actual.TestUnsignedInt16);
+ Assert.Equal(expected.TestUnsignedInt32, actual.TestUnsignedInt32);
+ Assert.Equal(expected.TestUnsignedInt64, actual.TestUnsignedInt64);
+ }
+
protected override string StoreName { get; } = "JsonQueryTest";
public new RelationalTestStore TestStore
@@ -420,5 +461,17 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con
bb.Property(x => x.Fraction).HasPrecision(18, 2);
});
});
+
+ modelBuilder.Entity().Property(x => x.Id).ValueGeneratedNever();
+ modelBuilder.Entity().OwnsOne(x => x.Reference, b =>
+ {
+ b.ToJson();
+ b.Property(x => x.TestDecimal).HasPrecision(18, 3);
+ });
+ modelBuilder.Entity().OwnsMany(x => x.Collection, b =>
+ {
+ b.ToJson();
+ b.Property(x => x.TestDecimal).HasPrecision(18, 3);
+ });
}
}
diff --git a/test/EFCore.Relational.Specification.Tests/Query/JsonQueryTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/JsonQueryTestBase.cs
index 2794e360121..9a89f73d09b 100644
--- a/test/EFCore.Relational.Specification.Tests/Query/JsonQueryTestBase.cs
+++ b/test/EFCore.Relational.Specification.Tests/Query/JsonQueryTestBase.cs
@@ -675,4 +675,38 @@ public virtual Task Json_with_include_on_entity_collection_and_reference(bool as
new ExpectedInclude(x => x.EntityReference),
new ExpectedInclude(x => x.EntityCollection)),
entryCount: 44);
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Json_all_types_entity_projection(bool async)
+ => AssertQuery(
+ async,
+ ss => ss.Set(),
+ entryCount: 3);
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Json_all_types_projection_individual_properties(bool async)
+ => AssertQuery(
+ async,
+ ss => ss.Set().Select(x => new
+ {
+ x.Reference.TestBoolean,
+ x.Reference.TestByte,
+ x.Reference.TestCharacter,
+ x.Reference.TestDateTime,
+ x.Reference.TestDateTimeOffset,
+ x.Reference.TestDecimal,
+ x.Reference.TestDouble,
+ x.Reference.TestGuid,
+ x.Reference.TestInt16,
+ x.Reference.TestInt32,
+ x.Reference.TestInt64,
+ x.Reference.TestSignedByte,
+ x.Reference.TestSingle,
+ x.Reference.TestTimeSpan,
+ x.Reference.TestUnsignedInt16,
+ x.Reference.TestUnsignedInt32,
+ x.Reference.TestUnsignedInt64
+ }));
}
diff --git a/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonEntityAllTypes.cs b/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonEntityAllTypes.cs
new file mode 100644
index 00000000000..5aa5eefac16
--- /dev/null
+++ b/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonEntityAllTypes.cs
@@ -0,0 +1,12 @@
+// 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.TestModels.JsonQuery
+{
+ public class JsonEntityAllTypes
+ {
+ public int Id { get; set; }
+ public JsonOwnedAllTypes Reference { get; set; }
+ public List Collection { get; set; }
+ }
+}
diff --git a/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonOwnedAllTypes.cs b/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonOwnedAllTypes.cs
new file mode 100644
index 00000000000..d54d567e6e5
--- /dev/null
+++ b/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonOwnedAllTypes.cs
@@ -0,0 +1,26 @@
+// 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.TestModels.JsonQuery
+{
+ public class JsonOwnedAllTypes
+ {
+ public short TestInt16 { get; set; }
+ public int TestInt32 { get; set; }
+ public long TestInt64 { get; set; }
+ public double TestDouble { get; set; }
+ public decimal TestDecimal { get; set; }
+ public DateTime TestDateTime { get; set; }
+ public DateTimeOffset TestDateTimeOffset { get; set; }
+ public TimeSpan TestTimeSpan { get; set; }
+ public float TestSingle { get; set; }
+ public bool TestBoolean { get; set; }
+ public byte TestByte { get; set; }
+ public Guid TestGuid { get; set; }
+ public ushort TestUnsignedInt16 { get; set; }
+ public uint TestUnsignedInt32 { get; set; }
+ public ulong TestUnsignedInt64 { get; set; }
+ public char TestCharacter { get; set; }
+ public sbyte TestSignedByte { get; set; }
+ }
+}
diff --git a/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonQueryContext.cs b/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonQueryContext.cs
index 86fc815042c..b23e0c0e310 100644
--- a/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonQueryContext.cs
+++ b/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonQueryContext.cs
@@ -16,6 +16,7 @@ public JsonQueryContext(DbContextOptions options)
public DbSet JsonEntitiesCustomNaming { get; set; }
public DbSet JsonEntitiesSingleOwned { get; set; }
public DbSet JsonEntitiesInheritance { get; set; }
+ public DbSet JsonEntitiesAllTypes { get; set; }
public static void Seed(JsonQueryContext context)
{
@@ -27,6 +28,7 @@ public static void Seed(JsonQueryContext context)
var jsonEntitiesCustomNaming = JsonQueryData.CreateJsonEntitiesCustomNaming();
var jsonEntitiesSingleOwned = JsonQueryData.CreateJsonEntitiesSingleOwned();
var jsonEntitiesInheritance = JsonQueryData.CreateJsonEntitiesInheritance();
+ var jsonEntitiesAllTypes = JsonQueryData.CreateJsonEntitiesAllTypes();
context.JsonEntitiesBasic.AddRange(jsonEntitiesBasic);
context.JsonEntitiesBasicForReference.AddRange(jsonEntitiesBasicForReference);
@@ -34,6 +36,7 @@ public static void Seed(JsonQueryContext context)
context.JsonEntitiesCustomNaming.AddRange(jsonEntitiesCustomNaming);
context.JsonEntitiesSingleOwned.AddRange(jsonEntitiesSingleOwned);
context.JsonEntitiesInheritance.AddRange(jsonEntitiesInheritance);
+ context.JsonEntitiesAllTypes.AddRange(jsonEntitiesAllTypes);
context.SaveChanges();
}
}
diff --git a/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonQueryData.cs b/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonQueryData.cs
index db835a3f29f..3e4215c4fee 100644
--- a/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonQueryData.cs
+++ b/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonQueryData.cs
@@ -15,6 +15,7 @@ public JsonQueryData()
JsonEntitiesCustomNaming = CreateJsonEntitiesCustomNaming();
JsonEntitiesSingleOwned = CreateJsonEntitiesSingleOwned();
JsonEntitiesInheritance = CreateJsonEntitiesInheritance();
+ JsonEntitiesAllTypes = CreateJsonEntitiesAllTypes();
}
public IReadOnlyList JsonEntitiesBasic { get; }
@@ -23,6 +24,7 @@ public JsonQueryData()
public IReadOnlyList JsonEntitiesCustomNaming { get; set; }
public IReadOnlyList JsonEntitiesSingleOwned { get; set; }
public IReadOnlyList JsonEntitiesInheritance { get; set; }
+ public IReadOnlyList JsonEntitiesAllTypes { get; set; }
public static IReadOnlyList CreateJsonEntitiesBasic()
{
@@ -704,6 +706,56 @@ public static IReadOnlyList CreateJsonEntitiesInherit
return new List { baseEntity, derivedEntity };
}
+ public static IReadOnlyList CreateJsonEntitiesAllTypes()
+ {
+ var r = new JsonOwnedAllTypes
+ {
+ TestInt16 = -1234,
+ TestInt32 = -123456789,
+ TestInt64 = -1234567890123456789L,
+ TestDouble = -1.23456789,
+ TestDecimal = -1234567890.01M,
+ TestDateTime = DateTime.Parse("01/01/2000 12:34:56"),
+ TestDateTimeOffset = new DateTimeOffset(DateTime.Parse("01/01/2000 12:34:56"), TimeSpan.FromHours(-8.0)),
+ TestTimeSpan = new TimeSpan(0, 10, 9, 8, 7),
+ TestSingle = -1.234F,
+ TestBoolean = true,
+ TestByte = 255,
+ TestGuid = new Guid("12345678-1234-4321-7777-987654321000"),
+ TestUnsignedInt16 = 1234,
+ TestUnsignedInt32 = 1234565789U,
+ TestUnsignedInt64 = 1234567890123456789UL,
+ TestCharacter = 'a',
+ TestSignedByte = -128,
+ };
+
+ var c = new JsonOwnedAllTypes
+ {
+ TestInt16 = -12,
+ TestInt32 = -12345,
+ TestInt64 = -1234567890L,
+ TestDouble = -1.2345,
+ TestDecimal = -123450.01M,
+ TestDateTime = DateTime.Parse("11/11/2100 12:34:56"),
+ TestDateTimeOffset = new DateTimeOffset(DateTime.Parse("11/11/2200 12:34:56"), TimeSpan.FromHours(-5.0)),
+ TestTimeSpan = new TimeSpan(0, 6, 5, 4, 3),
+ TestSingle = -1.4F,
+ TestBoolean = false,
+ TestByte = 25,
+ TestGuid = new Guid("00000000-0000-0000-0000-000000000000"),
+ TestUnsignedInt16 = 12,
+ TestUnsignedInt32 = 12345U,
+ TestUnsignedInt64 = 1234567867UL,
+ TestCharacter = 'h',
+ TestSignedByte = -18,
+ };
+
+ return new List
+ {
+ new JsonEntityAllTypes { Id = 1, Reference = r, Collection = new List { c } }
+ };
+ }
+
public IQueryable Set()
where TEntity : class
{
@@ -732,6 +784,11 @@ public IQueryable Set()
return (IQueryable)JsonEntitiesInheritance.OfType().AsQueryable();
}
+ if (typeof(TEntity) == typeof(JsonEntityAllTypes))
+ {
+ return (IQueryable)JsonEntitiesAllTypes.OfType().AsQueryable();
+ }
+
throw new InvalidOperationException("Invalid entity type: " + typeof(TEntity));
}
}
diff --git a/test/EFCore.Relational.Specification.Tests/Update/JsonUpdateFixtureBase.cs b/test/EFCore.Relational.Specification.Tests/Update/JsonUpdateFixtureBase.cs
index 8d2a773a636..323eb2e57be 100644
--- a/test/EFCore.Relational.Specification.Tests/Update/JsonUpdateFixtureBase.cs
+++ b/test/EFCore.Relational.Specification.Tests/Update/JsonUpdateFixtureBase.cs
@@ -97,6 +97,18 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con
modelBuilder.Ignore();
modelBuilder.Ignore();
+ modelBuilder.Entity().Property(x => x.Id).ValueGeneratedNever();
+ modelBuilder.Entity().OwnsOne(x => x.Reference, b =>
+ {
+ b.ToJson();
+ b.Property(x => x.TestDecimal).HasPrecision(18, 3);
+ });
+ modelBuilder.Entity().OwnsMany(x => x.Collection, b =>
+ {
+ b.ToJson();
+ b.Property(x => x.TestDecimal).HasPrecision(18, 3);
+ });
+
base.OnModelCreating(modelBuilder, context);
}
@@ -104,9 +116,11 @@ protected override void Seed(JsonQueryContext context)
{
var jsonEntitiesBasic = JsonQueryData.CreateJsonEntitiesBasic();
var jsonEntitiesInheritance = JsonQueryData.CreateJsonEntitiesInheritance();
+ var jsonEntitiesAllTypes = JsonQueryData.CreateJsonEntitiesAllTypes();
context.JsonEntitiesBasic.AddRange(jsonEntitiesBasic);
context.JsonEntitiesInheritance.AddRange(jsonEntitiesInheritance);
+ context.JsonEntitiesAllTypes.AddRange(jsonEntitiesAllTypes);
context.SaveChanges();
}
}
diff --git a/test/EFCore.Relational.Specification.Tests/Update/JsonUpdateTestBase.cs b/test/EFCore.Relational.Specification.Tests/Update/JsonUpdateTestBase.cs
index 50ca05af8aa..1af7ea7db8c 100644
--- a/test/EFCore.Relational.Specification.Tests/Update/JsonUpdateTestBase.cs
+++ b/test/EFCore.Relational.Specification.Tests/Update/JsonUpdateTestBase.cs
@@ -605,6 +605,541 @@ public virtual Task Edit_collection_element_and_reference_at_once()
Assert.Equal("edit2", result.OwnedReferenceRoot.OwnedCollectionBranch[1].OwnedReferenceLeaf.SomethingSomething);
});
+ [ConditionalFact]
+ public virtual Task Edit_single_enum_property()
+ => TestHelpers.ExecuteWithStrategyInTransactionAsync(
+ CreateContext,
+ UseTransaction,
+ async context =>
+ {
+ var query = await context.JsonEntitiesBasic.ToListAsync();
+ var entity = query.Single();
+ entity.OwnedReferenceRoot.OwnedReferenceBranch.Enum = JsonEnum.Two;
+ entity.OwnedCollectionRoot[1].OwnedCollectionBranch[1].Enum = JsonEnum.Two;
+
+ ClearLog();
+ await context.SaveChangesAsync();
+ },
+ async context =>
+ {
+ var result = await context.Set().SingleAsync();
+ Assert.Equal(JsonEnum.Two, result.OwnedReferenceRoot.OwnedReferenceBranch.Enum);
+ Assert.Equal(JsonEnum.Two, result.OwnedCollectionRoot[1].OwnedCollectionBranch[1].Enum);
+ });
+
+ [ConditionalFact]
+ public virtual Task Edit_single_numeric_property()
+ => TestHelpers.ExecuteWithStrategyInTransactionAsync(
+ CreateContext,
+ UseTransaction,
+ async context =>
+ {
+ var query = await context.JsonEntitiesBasic.ToListAsync();
+ var entity = query.Single();
+ entity.OwnedReferenceRoot.Number = 999;
+ entity.OwnedCollectionRoot[1].Number = 1024;
+
+ ClearLog();
+ await context.SaveChangesAsync();
+ },
+ async context =>
+ {
+ var result = await context.Set().SingleAsync();
+ Assert.Equal(999, result.OwnedReferenceRoot.Number);
+ Assert.Equal(1024, result.OwnedCollectionRoot[1].Number);
+ });
+
+ [ConditionalFact]
+ public virtual Task Edit_single_property_bool()
+ => TestHelpers.ExecuteWithStrategyInTransactionAsync(
+ CreateContext,
+ UseTransaction,
+ async context =>
+ {
+ var query = await context.JsonEntitiesAllTypes.ToListAsync();
+ var entity = query.Single();
+ entity.Reference.TestBoolean = false;
+ entity.Collection[0].TestBoolean = true;
+
+ ClearLog();
+ await context.SaveChangesAsync();
+ },
+ async context =>
+ {
+ var result = await context.Set().SingleAsync();
+ Assert.Equal(false, result.Reference.TestBoolean);
+ Assert.Equal(true, result.Collection[0].TestBoolean);
+ });
+
+ [ConditionalFact]
+ public virtual Task Edit_single_property_byte()
+ => TestHelpers.ExecuteWithStrategyInTransactionAsync(
+ CreateContext,
+ UseTransaction,
+ async context =>
+ {
+ var query = await context.JsonEntitiesAllTypes.ToListAsync();
+ var entity = query.Single();
+ entity.Reference.TestByte = 25;
+ entity.Collection[0].TestByte = 14;
+
+ ClearLog();
+ await context.SaveChangesAsync();
+ },
+ async context =>
+ {
+ var result = await context.Set().SingleAsync();
+ Assert.Equal(25, result.Reference.TestByte);
+ Assert.Equal(14, result.Collection[0].TestByte);
+ });
+
+ [ConditionalFact]
+ public virtual Task Edit_single_property_char()
+ => TestHelpers.ExecuteWithStrategyInTransactionAsync(
+ CreateContext,
+ UseTransaction,
+ async context =>
+ {
+ var query = await context.JsonEntitiesAllTypes.ToListAsync();
+ var entity = query.Single();
+ entity.Reference.TestCharacter = 't';
+ entity.Collection[0].TestCharacter = 'h';
+
+ ClearLog();
+ await context.SaveChangesAsync();
+ },
+ async context =>
+ {
+ var result = await context.Set().SingleAsync();
+ Assert.Equal('t', result.Reference.TestCharacter);
+ Assert.Equal('h', result.Collection[0].TestCharacter);
+ });
+
+ [ConditionalFact]
+ public virtual Task Edit_single_property_datetime()
+ => TestHelpers.ExecuteWithStrategyInTransactionAsync(
+ CreateContext,
+ UseTransaction,
+ async context =>
+ {
+ var query = await context.JsonEntitiesAllTypes.ToListAsync();
+ var entity = query.Single();
+ entity.Reference.TestDateTime = DateTime.Parse("01/01/3000 12:34:56");
+ entity.Collection[0].TestDateTime = DateTime.Parse("01/01/3000 12:34:56");
+
+ ClearLog();
+ await context.SaveChangesAsync();
+ },
+ async context =>
+ {
+ var result = await context.Set().SingleAsync();
+ Assert.Equal(DateTime.Parse("01/01/3000 12:34:56"), result.Reference.TestDateTime);
+ Assert.Equal(DateTime.Parse("01/01/3000 12:34:56"), result.Collection[0].TestDateTime);
+ });
+
+ [ConditionalFact]
+ public virtual Task Edit_single_property_datetimeoffset()
+ => TestHelpers.ExecuteWithStrategyInTransactionAsync(
+ CreateContext,
+ UseTransaction,
+ async context =>
+ {
+ var query = await context.JsonEntitiesAllTypes.ToListAsync();
+ var entity = query.Single();
+ entity.Reference.TestDateTimeOffset = new DateTimeOffset(DateTime.Parse("01/01/3000 12:34:56"), TimeSpan.FromHours(-4.0));
+ entity.Collection[0].TestDateTimeOffset = new DateTimeOffset(DateTime.Parse("01/01/3000 12:34:56"), TimeSpan.FromHours(-4.0));
+
+ ClearLog();
+ await context.SaveChangesAsync();
+ },
+ async context =>
+ {
+ var result = await context.Set().SingleAsync();
+ Assert.Equal(new DateTimeOffset(DateTime.Parse("01/01/3000 12:34:56"), TimeSpan.FromHours(-4.0)), result.Reference.TestDateTimeOffset);
+ Assert.Equal(new DateTimeOffset(DateTime.Parse("01/01/3000 12:34:56"), TimeSpan.FromHours(-4.0)), result.Collection[0].TestDateTimeOffset);
+ });
+
+ [ConditionalFact]
+ public virtual Task Edit_single_property_decimal()
+ => TestHelpers.ExecuteWithStrategyInTransactionAsync(
+ CreateContext,
+ UseTransaction,
+ async context =>
+ {
+ var query = await context.JsonEntitiesAllTypes.ToListAsync();
+ var entity = query.Single();
+ entity.Reference.TestDecimal = -13579.01M;
+ entity.Collection[0].TestDecimal = -13579.01M;
+
+ ClearLog();
+ await context.SaveChangesAsync();
+ },
+ async context =>
+ {
+ var result = await context.Set().SingleAsync();
+ Assert.Equal(-13579.01M, result.Reference.TestDecimal);
+ Assert.Equal(-13579.01M, result.Collection[0].TestDecimal);
+ });
+
+ [ConditionalFact]
+ public virtual Task Edit_single_property_double()
+ => TestHelpers.ExecuteWithStrategyInTransactionAsync(
+ CreateContext,
+ UseTransaction,
+ async context =>
+ {
+ var query = await context.JsonEntitiesAllTypes.ToListAsync();
+ var entity = query.Single();
+ entity.Reference.TestDouble = -1.23579;
+ entity.Collection[0].TestDouble = -1.23579;
+
+ ClearLog();
+ await context.SaveChangesAsync();
+ },
+ async context =>
+ {
+ var result = await context.Set().SingleAsync();
+ Assert.Equal(-1.23579, result.Reference.TestDouble);
+ Assert.Equal(-1.23579, result.Collection[0].TestDouble);
+ });
+
+ [ConditionalFact]
+ public virtual Task Edit_single_property_guid()
+ => TestHelpers.ExecuteWithStrategyInTransactionAsync(
+ CreateContext,
+ UseTransaction,
+ async context =>
+ {
+ var query = await context.JsonEntitiesAllTypes.ToListAsync();
+ var entity = query.Single();
+ entity.Reference.TestGuid = new Guid("12345678-1234-4321-5555-987654321000");
+ entity.Collection[0].TestGuid = new Guid("12345678-1234-4321-5555-987654321000");
+
+ ClearLog();
+ await context.SaveChangesAsync();
+ },
+ async context =>
+ {
+ var result = await context.Set().SingleAsync();
+ Assert.Equal(new Guid("12345678-1234-4321-5555-987654321000"), result.Reference.TestGuid);
+ Assert.Equal(new Guid("12345678-1234-4321-5555-987654321000"), result.Collection[0].TestGuid);
+ });
+
+ [ConditionalFact]
+ public virtual Task Edit_single_property_int16()
+ => TestHelpers.ExecuteWithStrategyInTransactionAsync(
+ CreateContext,
+ UseTransaction,
+ async context =>
+ {
+ var query = await context.JsonEntitiesAllTypes.ToListAsync();
+ var entity = query.Single();
+ entity.Reference.TestInt16 = -3234;
+ entity.Collection[0].TestInt16 = -3234;
+
+ ClearLog();
+ await context.SaveChangesAsync();
+ },
+ async context =>
+ {
+ var result = await context.Set().SingleAsync();
+ Assert.Equal(-3234, result.Reference.TestInt16);
+ Assert.Equal(-3234, result.Collection[0].TestInt16);
+ });
+
+
+ [ConditionalFact]
+ public virtual Task Edit_single_property_int32()
+ => TestHelpers.ExecuteWithStrategyInTransactionAsync(
+ CreateContext,
+ UseTransaction,
+ async context =>
+ {
+ var query = await context.JsonEntitiesAllTypes.ToListAsync();
+ var entity = query.Single();
+ entity.Reference.TestInt32 = -3234;
+ entity.Collection[0].TestInt32 = -3234;
+
+ ClearLog();
+ await context.SaveChangesAsync();
+ },
+ async context =>
+ {
+ var result = await context.Set().SingleAsync();
+ Assert.Equal(-3234, result.Reference.TestInt32);
+ Assert.Equal(-3234, result.Collection[0].TestInt32);
+ });
+
+ [ConditionalFact]
+ public virtual Task Edit_single_property_int64()
+ => TestHelpers.ExecuteWithStrategyInTransactionAsync(
+ CreateContext,
+ UseTransaction,
+ async context =>
+ {
+ var query = await context.JsonEntitiesAllTypes.ToListAsync();
+ var entity = query.Single();
+ entity.Reference.TestInt64 = -3234;
+ entity.Collection[0].TestInt64 = -3234;
+
+ ClearLog();
+ await context.SaveChangesAsync();
+ },
+ async context =>
+ {
+ var result = await context.Set().SingleAsync();
+ Assert.Equal(-3234, result.Reference.TestInt64);
+ Assert.Equal(-3234, result.Collection[0].TestInt64);
+ });
+
+ [ConditionalFact]
+ public virtual Task Edit_single_property_signed_byte()
+ => TestHelpers.ExecuteWithStrategyInTransactionAsync(
+ CreateContext,
+ UseTransaction,
+ async context =>
+ {
+ var query = await context.JsonEntitiesAllTypes.ToListAsync();
+ var entity = query.Single();
+ entity.Reference.TestSignedByte = -108;
+ entity.Collection[0].TestSignedByte = -108;
+
+ ClearLog();
+ await context.SaveChangesAsync();
+ },
+ async context =>
+ {
+ var result = await context.Set().SingleAsync();
+ Assert.Equal(-108, result.Reference.TestSignedByte);
+ Assert.Equal(-108, result.Collection[0].TestSignedByte);
+ });
+
+ [ConditionalFact]
+ public virtual Task Edit_single_property_single()
+ => TestHelpers.ExecuteWithStrategyInTransactionAsync(
+ CreateContext,
+ UseTransaction,
+ async context =>
+ {
+ var query = await context.JsonEntitiesAllTypes.ToListAsync();
+ var entity = query.Single();
+ entity.Reference.TestSingle = -7.234F;
+ entity.Collection[0].TestSingle = -7.234F;
+
+ ClearLog();
+ await context.SaveChangesAsync();
+ },
+ async context =>
+ {
+ var result = await context.Set().SingleAsync();
+ Assert.Equal(-7.234F, result.Reference.TestSingle);
+ Assert.Equal(-7.234F, result.Collection[0].TestSingle);
+ });
+
+ [ConditionalFact]
+ public virtual Task Edit_single_property_timespan()
+ => TestHelpers.ExecuteWithStrategyInTransactionAsync(
+ CreateContext,
+ UseTransaction,
+ async context =>
+ {
+ var query = await context.JsonEntitiesAllTypes.ToListAsync();
+ var entity = query.Single();
+ entity.Reference.TestTimeSpan = new TimeSpan(0, 10, 1, 1, 7);
+ entity.Collection[0].TestTimeSpan = new TimeSpan(0, 10, 1, 1, 7);
+
+ ClearLog();
+ await context.SaveChangesAsync();
+ },
+ async context =>
+ {
+ var result = await context.Set().SingleAsync();
+ Assert.Equal(new TimeSpan(0, 10, 1, 1, 7), result.Reference.TestTimeSpan);
+ Assert.Equal(new TimeSpan(0, 10, 1, 1, 7), result.Collection[0].TestTimeSpan);
+ });
+
+ [ConditionalFact]
+ public virtual Task Edit_single_property_uint16()
+ => TestHelpers.ExecuteWithStrategyInTransactionAsync(
+ CreateContext,
+ UseTransaction,
+ async context =>
+ {
+ var query = await context.JsonEntitiesAllTypes.ToListAsync();
+ var entity = query.Single();
+ entity.Reference.TestUnsignedInt16 = 1534;
+ entity.Collection[0].TestUnsignedInt16 = 1534;
+
+ ClearLog();
+ await context.SaveChangesAsync();
+ },
+ async context =>
+ {
+ var result = await context.Set().SingleAsync();
+ Assert.Equal(1534, result.Reference.TestUnsignedInt16);
+ Assert.Equal(1534, result.Collection[0].TestUnsignedInt16);
+ });
+
+ [ConditionalFact]
+ public virtual Task Edit_single_property_uint32()
+ => TestHelpers.ExecuteWithStrategyInTransactionAsync(
+ CreateContext,
+ UseTransaction,
+ async context =>
+ {
+ var query = await context.JsonEntitiesAllTypes.ToListAsync();
+ var entity = query.Single();
+ entity.Reference.TestUnsignedInt32 = 1237775789U;
+ entity.Collection[0].TestUnsignedInt32 = 1237775789U;
+
+ ClearLog();
+ await context.SaveChangesAsync();
+ },
+ async context =>
+ {
+ var result = await context.Set().SingleAsync();
+ Assert.Equal(1237775789U, result.Reference.TestUnsignedInt32);
+ Assert.Equal(1237775789U, result.Collection[0].TestUnsignedInt32);
+ });
+
+ [ConditionalFact]
+ public virtual Task Edit_single_property_uint64()
+ => TestHelpers.ExecuteWithStrategyInTransactionAsync(
+ CreateContext,
+ UseTransaction,
+ async context =>
+ {
+ var query = await context.JsonEntitiesAllTypes.ToListAsync();
+ var entity = query.Single();
+ entity.Reference.TestUnsignedInt64 = 1234555555123456789UL;
+ entity.Collection[0].TestUnsignedInt64 = 1234555555123456789UL;
+
+ ClearLog();
+ await context.SaveChangesAsync();
+ },
+ async context =>
+ {
+ var result = await context.Set().SingleAsync();
+ Assert.Equal(1234555555123456789UL, result.Reference.TestUnsignedInt64);
+ Assert.Equal(1234555555123456789UL, result.Collection[0].TestUnsignedInt64);
+ });
+
+ [ConditionalFact]
+ public virtual Task Edit_two_properties_on_same_entity_updates_the_entire_entity()
+ => TestHelpers.ExecuteWithStrategyInTransactionAsync(
+ CreateContext,
+ UseTransaction,
+ async context =>
+ {
+ var query = await context.JsonEntitiesAllTypes.ToListAsync();
+ var entity = query.Single();
+ entity.Reference.TestInt32 = 32;
+ entity.Reference.TestInt64 = 64;
+ entity.Collection[0].TestInt32 = 32;
+ entity.Collection[0].TestInt64 = 64;
+
+ ClearLog();
+ await context.SaveChangesAsync();
+ },
+ async context =>
+ {
+ var result = await context.Set().SingleAsync();
+ Assert.Equal(32, result.Reference.TestInt32);
+ Assert.Equal(64, result.Reference.TestInt64);
+ Assert.Equal(32, result.Collection[0].TestInt32);
+ Assert.Equal(64, result.Collection[0].TestInt64);
+ });
+
+ [ConditionalFact]
+ public virtual Task Edit_a_scalar_property_and_reference_navigation_on_the_same_entity()
+ => TestHelpers.ExecuteWithStrategyInTransactionAsync(
+ CreateContext,
+ UseTransaction,
+ async context =>
+ {
+ var query = await context.JsonEntitiesBasic.ToListAsync();
+ var entity = query.Single();
+ entity.OwnedReferenceRoot.OwnedReferenceBranch.Fraction = 123.532M;
+ entity.OwnedReferenceRoot.OwnedReferenceBranch.OwnedReferenceLeaf = null;
+ await context.SaveChangesAsync();
+ },
+
+ async context =>
+ {
+ var query = await context.JsonEntitiesBasic.ToListAsync();
+ var entity = query.Single();
+ entity.OwnedReferenceRoot.OwnedReferenceBranch.Fraction = 523.532M;
+ entity.OwnedReferenceRoot.OwnedReferenceBranch.OwnedReferenceLeaf = new JsonOwnedLeaf { SomethingSomething = "edit" };
+
+ ClearLog();
+ await context.SaveChangesAsync();
+ },
+
+ async context =>
+ {
+ var result = await context.Set().SingleAsync();
+ Assert.Equal(523.532M, result.OwnedReferenceRoot.OwnedReferenceBranch.Fraction);
+ Assert.Equal("edit", result.OwnedReferenceRoot.OwnedReferenceBranch.OwnedReferenceLeaf.SomethingSomething);
+ });
+
+ [ConditionalFact]
+ public virtual Task Edit_a_scalar_property_and_collection_navigation_on_the_same_entity()
+ => TestHelpers.ExecuteWithStrategyInTransactionAsync(
+ CreateContext,
+ UseTransaction,
+ async context =>
+ {
+ var query = await context.JsonEntitiesBasic.ToListAsync();
+ var entity = query.Single();
+ entity.OwnedReferenceRoot.OwnedReferenceBranch.Fraction = 123.532M;
+ entity.OwnedReferenceRoot.OwnedReferenceBranch.OwnedCollectionLeaf = null;
+ await context.SaveChangesAsync();
+ },
+
+ async context =>
+ {
+ var query = await context.JsonEntitiesBasic.ToListAsync();
+ var entity = query.Single();
+ entity.OwnedReferenceRoot.OwnedReferenceBranch.Fraction = 523.532M;
+ entity.OwnedReferenceRoot.OwnedReferenceBranch.OwnedCollectionLeaf = new List
+ {
+ new JsonOwnedLeaf { SomethingSomething = "edit" }
+ };
+
+ ClearLog();
+ await context.SaveChangesAsync();
+ },
+
+ async context =>
+ {
+ var result = await context.Set().SingleAsync();
+ Assert.Equal(523.532M, result.OwnedReferenceRoot.OwnedReferenceBranch.Fraction);
+ Assert.Equal("edit", result.OwnedReferenceRoot.OwnedReferenceBranch.OwnedCollectionLeaf[0].SomethingSomething);
+ });
+
+ [ConditionalFact]
+ public virtual Task Edit_a_scalar_property_and_another_property_behind_reference_navigation_on_the_same_entity()
+ => TestHelpers.ExecuteWithStrategyInTransactionAsync(
+ CreateContext,
+ UseTransaction,
+ async context =>
+ {
+ var query = await context.JsonEntitiesBasic.ToListAsync();
+ var entity = query.Single();
+ entity.OwnedReferenceRoot.OwnedReferenceBranch.Fraction = 523.532M;
+ entity.OwnedReferenceRoot.OwnedReferenceBranch.OwnedReferenceLeaf.SomethingSomething = "edit";
+
+ ClearLog();
+ await context.SaveChangesAsync();
+ },
+
+ async context =>
+ {
+ var result = await context.Set().SingleAsync();
+ Assert.Equal(523.532M, result.OwnedReferenceRoot.OwnedReferenceBranch.Fraction);
+ Assert.Equal("edit", result.OwnedReferenceRoot.OwnedReferenceBranch.OwnedReferenceLeaf.SomethingSomething);
+ });
+
public void UseTransaction(DatabaseFacade facade, IDbContextTransaction transaction)
=> facade.UseTransaction(transaction.GetDbTransaction());
diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/JsonQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/JsonQuerySqlServerTest.cs
index c1653415489..54a09c29c37 100644
--- a/test/EFCore.SqlServer.FunctionalTests/Query/JsonQuerySqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/Query/JsonQuerySqlServerTest.cs
@@ -595,6 +595,24 @@ FROM [JsonEntitiesBasic] AS [j]
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], JSON_QUERY([j].[Collection],'$'), JSON_QUERY([j].[Reference],'$')
+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 CAST(JSON_VALUE([j].[Reference],'$.TestBoolean') AS bit) AS [TestBoolean], CAST(JSON_VALUE([j].[Reference],'$.TestByte') AS tinyint) AS [TestByte], CAST(JSON_VALUE([j].[Reference],'$.TestCharacter') AS nvarchar(1)) AS [TestCharacter], CAST(JSON_VALUE([j].[Reference],'$.TestDateTime') AS datetime2) AS [TestDateTime], CAST(JSON_VALUE([j].[Reference],'$.TestDateTimeOffset') AS datetimeoffset) AS [TestDateTimeOffset], CAST(JSON_VALUE([j].[Reference],'$.TestDecimal') AS decimal(18,3)) AS [TestDecimal], CAST(JSON_VALUE([j].[Reference],'$.TestDouble') AS float) AS [TestDouble], CAST(JSON_VALUE([j].[Reference],'$.TestGuid') AS uniqueidentifier) AS [TestGuid], CAST(JSON_VALUE([j].[Reference],'$.TestInt16') AS smallint) AS [TestInt16], CAST(JSON_VALUE([j].[Reference],'$.TestInt32') AS int) AS [TestInt32], CAST(JSON_VALUE([j].[Reference],'$.TestInt64') AS bigint) AS [TestInt64], CAST(JSON_VALUE([j].[Reference],'$.TestSignedByte') AS smallint) AS [TestSignedByte], CAST(JSON_VALUE([j].[Reference],'$.TestSingle') AS real) AS [TestSingle], CAST(JSON_VALUE([j].[Reference],'$.TestTimeSpan') AS time) AS [TestTimeSpan], CAST(JSON_VALUE([j].[Reference],'$.TestUnsignedInt16') AS int) AS [TestUnsignedInt16], CAST(JSON_VALUE([j].[Reference],'$.TestUnsignedInt32') AS bigint) AS [TestUnsignedInt32], CAST(JSON_VALUE([j].[Reference],'$.TestUnsignedInt64') AS decimal(20,0)) AS [TestUnsignedInt64]
+FROM [JsonEntitiesAllTypes] AS [j]");
+ }
+
private void AssertSql(params string[] expected)
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
}
diff --git a/test/EFCore.SqlServer.FunctionalTests/Update/JsonUpdateSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Update/JsonUpdateSqlServerTest.cs
index 4cb48b4afda..0d931d7c2c7 100644
--- a/test/EFCore.SqlServer.FunctionalTests/Update/JsonUpdateSqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/Update/JsonUpdateSqlServerTest.cs
@@ -232,12 +232,12 @@ 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)
+ @"@p0='[""2111-11-11T00:00:00""]' (Nullable = false) (Size = 23)
@p1='1'
SET IMPLICIT_TRANSACTIONS OFF;
SET NOCOUNT ON;
-UPDATE [JsonEntitiesBasic] SET [OwnedCollectionRoot] = JSON_MODIFY([OwnedCollectionRoot], 'strict $[0].OwnedCollectionBranch[0]', JSON_QUERY(@p0))
+UPDATE [JsonEntitiesBasic] SET [OwnedCollectionRoot] = JSON_MODIFY([OwnedCollectionRoot], 'strict $[0].OwnedCollectionBranch[0].Date', JSON_VALUE(@p0, '$[0]'))
OUTPUT 1
WHERE [Id] = @p1;",
//
@@ -250,12 +250,12 @@ 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)
+ @"@p0='[""Modified""]' (Nullable = false) (Size = 12)
@p1='1'
SET IMPLICIT_TRANSACTIONS OFF;
SET NOCOUNT ON;
-UPDATE [JsonEntitiesBasic] SET [OwnedCollectionRoot] = JSON_MODIFY([OwnedCollectionRoot], 'strict $[0]', JSON_QUERY(@p0))
+UPDATE [JsonEntitiesBasic] SET [OwnedCollectionRoot] = JSON_MODIFY([OwnedCollectionRoot], 'strict $[0].Name', JSON_VALUE(@p0, '$[0]'))
OUTPUT 1
WHERE [Id] = @p1;",
//
@@ -268,12 +268,12 @@ 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)
+ @"@p0='[""Modified""]' (Nullable = false) (Size = 12)
@p1='1'
SET IMPLICIT_TRANSACTIONS OFF;
SET NOCOUNT ON;
-UPDATE [JsonEntitiesBasic] SET [OwnedCollectionRoot] = JSON_MODIFY([OwnedCollectionRoot], 'strict $[1]', JSON_QUERY(@p0))
+UPDATE [JsonEntitiesBasic] SET [OwnedCollectionRoot] = JSON_MODIFY([OwnedCollectionRoot], 'strict $[1].Name', JSON_VALUE(@p0, '$[0]'))
OUTPUT 1
WHERE [Id] = @p1;",
//
@@ -372,6 +372,439 @@ OUTPUT 1
FROM [JsonEntitiesBasic] AS [j]");
}
+ public override async Task Edit_single_enum_property()
+ {
+ await base.Edit_single_enum_property();
+
+ AssertSql(
+ @"@p0='[""Two""]' (Nullable = false) (Size = 7)
+@p1='[""Two""]' (Nullable = false) (Size = 7)
+@p2='1'
+
+SET IMPLICIT_TRANSACTIONS OFF;
+SET NOCOUNT ON;
+UPDATE [JsonEntitiesBasic] SET [OwnedCollectionRoot] = JSON_MODIFY([OwnedCollectionRoot], 'strict $[1].OwnedCollectionBranch[1].Enum', JSON_VALUE(@p0, '$[0]')), [OwnedReferenceRoot] = JSON_MODIFY([OwnedReferenceRoot], 'strict $.OwnedReferenceBranch.Enum', JSON_VALUE(@p1, '$[0]'))
+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_single_numeric_property()
+ {
+ await base.Edit_single_numeric_property();
+
+ AssertSql(
+ @"@p0='[1024]' (Nullable = false) (Size = 6)
+@p1='[999]' (Nullable = false) (Size = 5)
+@p2='1'
+
+SET IMPLICIT_TRANSACTIONS OFF;
+SET NOCOUNT ON;
+UPDATE [JsonEntitiesBasic] SET [OwnedCollectionRoot] = JSON_MODIFY([OwnedCollectionRoot], 'strict $[1].Number', CAST(JSON_VALUE(@p0, '$[0]') AS int)), [OwnedReferenceRoot] = JSON_MODIFY([OwnedReferenceRoot], 'strict $.Number', CAST(JSON_VALUE(@p1, '$[0]') AS int))
+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_single_property_bool()
+ {
+ await base.Edit_single_property_bool();
+
+ AssertSql(
+ @"@p0='[true]' (Nullable = false) (Size = 6)
+@p1='[false]' (Nullable = false) (Size = 7)
+@p2='1'
+
+SET IMPLICIT_TRANSACTIONS OFF;
+SET NOCOUNT ON;
+UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestBoolean', CAST(JSON_VALUE(@p0, '$[0]') AS bit)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestBoolean', CAST(JSON_VALUE(@p1, '$[0]') AS bit))
+OUTPUT 1
+WHERE [Id] = @p2;",
+ //
+ @"SELECT TOP(2) [j].[Id], JSON_QUERY([j].[Collection],'$'), JSON_QUERY([j].[Reference],'$')
+FROM [JsonEntitiesAllTypes] AS [j]");
+ }
+
+ public override async Task Edit_single_property_byte()
+ {
+ await base.Edit_single_property_byte();
+
+ AssertSql(
+ @"@p0='[14]' (Nullable = false) (Size = 4)
+@p1='[25]' (Nullable = false) (Size = 4)
+@p2='1'
+
+SET IMPLICIT_TRANSACTIONS OFF;
+SET NOCOUNT ON;
+UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestByte', CAST(JSON_VALUE(@p0, '$[0]') AS tinyint)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestByte', CAST(JSON_VALUE(@p1, '$[0]') AS tinyint))
+OUTPUT 1
+WHERE [Id] = @p2;",
+ //
+ @"SELECT TOP(2) [j].[Id], JSON_QUERY([j].[Collection],'$'), JSON_QUERY([j].[Reference],'$')
+FROM [JsonEntitiesAllTypes] AS [j]");
+ }
+
+ public override async Task Edit_single_property_char()
+ {
+ await base.Edit_single_property_char();
+
+ AssertSql(
+ @"@p0='[""t""]' (Nullable = false) (Size = 5)
+@p1='1'
+
+SET IMPLICIT_TRANSACTIONS OFF;
+SET NOCOUNT ON;
+UPDATE [JsonEntitiesAllTypes] SET [Reference] = JSON_MODIFY([Reference], 'strict $.TestCharacter', CAST(JSON_VALUE(@p0, '$[0]') AS nvarchar(1)))
+OUTPUT 1
+WHERE [Id] = @p1;",
+ //
+ @"SELECT TOP(2) [j].[Id], JSON_QUERY([j].[Collection],'$'), JSON_QUERY([j].[Reference],'$')
+FROM [JsonEntitiesAllTypes] AS [j]");
+ }
+
+ public override async Task Edit_single_property_datetime()
+ {
+ await base.Edit_single_property_datetime();
+
+ AssertSql(
+ @"@p0='[""3000-01-01T12:34:56""]' (Nullable = false) (Size = 23)
+@p1='[""3000-01-01T12:34:56""]' (Nullable = false) (Size = 23)
+@p2='1'
+
+SET IMPLICIT_TRANSACTIONS OFF;
+SET NOCOUNT ON;
+UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestDateTime', JSON_VALUE(@p0, '$[0]')), [Reference] = JSON_MODIFY([Reference], 'strict $.TestDateTime', JSON_VALUE(@p1, '$[0]'))
+OUTPUT 1
+WHERE [Id] = @p2;",
+ //
+ @"SELECT TOP(2) [j].[Id], JSON_QUERY([j].[Collection],'$'), JSON_QUERY([j].[Reference],'$')
+FROM [JsonEntitiesAllTypes] AS [j]");
+ }
+
+ 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 = 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].TestDateTimeOffset', JSON_VALUE(@p0, '$[0]')), [Reference] = JSON_MODIFY([Reference], 'strict $.TestDateTimeOffset', JSON_VALUE(@p1, '$[0]'))
+OUTPUT 1
+WHERE [Id] = @p2;",
+ //
+ @"SELECT TOP(2) [j].[Id], JSON_QUERY([j].[Collection],'$'), JSON_QUERY([j].[Reference],'$')
+FROM [JsonEntitiesAllTypes] AS [j]");
+ }
+
+ public override async Task Edit_single_property_decimal()
+ {
+ await base.Edit_single_property_decimal();
+
+ 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].TestDecimal', CAST(JSON_VALUE(@p0, '$[0]') AS decimal(18,3))), [Reference] = JSON_MODIFY([Reference], 'strict $.TestDecimal', CAST(JSON_VALUE(@p1, '$[0]') AS decimal(18,3)))
+OUTPUT 1
+WHERE [Id] = @p2;",
+ //
+ @"SELECT TOP(2) [j].[Id], JSON_QUERY([j].[Collection],'$'), JSON_QUERY([j].[Reference],'$')
+FROM [JsonEntitiesAllTypes] AS [j]");
+ }
+
+ public override async Task Edit_single_property_double()
+ {
+ await base.Edit_single_property_double();
+
+ AssertSql(
+ @"@p0='[-1.23579]' (Nullable = false) (Size = 10)
+@p1='[-1.23579]' (Nullable = false) (Size = 10)
+@p2='1'
+
+SET IMPLICIT_TRANSACTIONS OFF;
+SET NOCOUNT ON;
+UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestDouble', CAST(JSON_VALUE(@p0, '$[0]') AS float)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestDouble', CAST(JSON_VALUE(@p1, '$[0]') AS float))
+OUTPUT 1
+WHERE [Id] = @p2;",
+ //
+ @"SELECT TOP(2) [j].[Id], JSON_QUERY([j].[Collection],'$'), JSON_QUERY([j].[Reference],'$')
+FROM [JsonEntitiesAllTypes] AS [j]");
+ }
+
+ public override async Task Edit_single_property_guid()
+ {
+ await base.Edit_single_property_guid();
+
+ 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].TestGuid', JSON_VALUE(@p0, '$[0]')), [Reference] = JSON_MODIFY([Reference], 'strict $.TestGuid', JSON_VALUE(@p1, '$[0]'))
+OUTPUT 1
+WHERE [Id] = @p2;",
+ //
+ @"SELECT TOP(2) [j].[Id], JSON_QUERY([j].[Collection],'$'), JSON_QUERY([j].[Reference],'$')
+FROM [JsonEntitiesAllTypes] AS [j]");
+ }
+
+ public override async Task Edit_single_property_int16()
+ {
+ await base.Edit_single_property_int16();
+
+ 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].TestInt16', CAST(JSON_VALUE(@p0, '$[0]') AS smallint)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestInt16', CAST(JSON_VALUE(@p1, '$[0]') AS smallint))
+OUTPUT 1
+WHERE [Id] = @p2;",
+ //
+ @"SELECT TOP(2) [j].[Id], JSON_QUERY([j].[Collection],'$'), JSON_QUERY([j].[Reference],'$')
+FROM [JsonEntitiesAllTypes] AS [j]");
+ }
+
+ public override async Task Edit_single_property_int32()
+ {
+ await base.Edit_single_property_int32();
+
+ 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].TestInt32', CAST(JSON_VALUE(@p0, '$[0]') AS int)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestInt32', CAST(JSON_VALUE(@p1, '$[0]') AS int))
+OUTPUT 1
+WHERE [Id] = @p2;",
+ //
+ @"SELECT TOP(2) [j].[Id], JSON_QUERY([j].[Collection],'$'), JSON_QUERY([j].[Reference],'$')
+FROM [JsonEntitiesAllTypes] AS [j]");
+ }
+
+ public override async Task Edit_single_property_int64()
+ {
+ await base.Edit_single_property_int64();
+
+ 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].TestInt64', CAST(JSON_VALUE(@p0, '$[0]') AS bigint)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestInt64', CAST(JSON_VALUE(@p1, '$[0]') AS bigint))
+OUTPUT 1
+WHERE [Id] = @p2;",
+ //
+ @"SELECT TOP(2) [j].[Id], JSON_QUERY([j].[Collection],'$'), JSON_QUERY([j].[Reference],'$')
+FROM [JsonEntitiesAllTypes] AS [j]");
+ }
+
+ public override async Task Edit_single_property_signed_byte()
+ {
+ await base.Edit_single_property_signed_byte();
+
+ 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].TestSignedByte', CAST(JSON_VALUE(@p0, '$[0]') AS smallint)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestSignedByte', CAST(JSON_VALUE(@p1, '$[0]') AS smallint))
+OUTPUT 1
+WHERE [Id] = @p2;",
+ //
+ @"SELECT TOP(2) [j].[Id], JSON_QUERY([j].[Collection],'$'), JSON_QUERY([j].[Reference],'$')
+FROM [JsonEntitiesAllTypes] AS [j]");
+ }
+
+ public override async Task Edit_single_property_single()
+ {
+ await base.Edit_single_property_single();
+
+ AssertSql(
+ @"@p0='[-7.234]' (Nullable = false) (Size = 8)
+@p1='[-7.234]' (Nullable = false) (Size = 8)
+@p2='1'
+
+SET IMPLICIT_TRANSACTIONS OFF;
+SET NOCOUNT ON;
+UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestSingle', CAST(JSON_VALUE(@p0, '$[0]') AS real)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestSingle', CAST(JSON_VALUE(@p1, '$[0]') AS real))
+OUTPUT 1
+WHERE [Id] = @p2;",
+ //
+ @"SELECT TOP(2) [j].[Id], JSON_QUERY([j].[Collection],'$'), JSON_QUERY([j].[Reference],'$')
+FROM [JsonEntitiesAllTypes] AS [j]");
+ }
+
+ public override async Task Edit_single_property_timespan()
+ {
+ await base.Edit_single_property_timespan();
+
+ AssertSql(
+ @"@p0='[""10:01:01.0070000""]' (Nullable = false) (Size = 20)
+@p1='[""10:01:01.0070000""]' (Nullable = false) (Size = 20)
+@p2='1'
+
+SET IMPLICIT_TRANSACTIONS OFF;
+SET NOCOUNT ON;
+UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestTimeSpan', JSON_VALUE(@p0, '$[0]')), [Reference] = JSON_MODIFY([Reference], 'strict $.TestTimeSpan', JSON_VALUE(@p1, '$[0]'))
+OUTPUT 1
+WHERE [Id] = @p2;",
+ //
+ @"SELECT TOP(2) [j].[Id], JSON_QUERY([j].[Collection],'$'), JSON_QUERY([j].[Reference],'$')
+FROM [JsonEntitiesAllTypes] AS [j]");
+ }
+
+ public override async Task Edit_single_property_uint16()
+ {
+ await base.Edit_single_property_uint16();
+
+ 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].TestUnsignedInt16', CAST(JSON_VALUE(@p0, '$[0]') AS int)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestUnsignedInt16', CAST(JSON_VALUE(@p1, '$[0]') AS int))
+OUTPUT 1
+WHERE [Id] = @p2;",
+ //
+ @"SELECT TOP(2) [j].[Id], JSON_QUERY([j].[Collection],'$'), JSON_QUERY([j].[Reference],'$')
+FROM [JsonEntitiesAllTypes] AS [j]");
+ }
+
+ public override async Task Edit_single_property_uint32()
+ {
+ await base.Edit_single_property_uint32();
+
+ 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].TestUnsignedInt32', CAST(JSON_VALUE(@p0, '$[0]') AS bigint)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestUnsignedInt32', CAST(JSON_VALUE(@p1, '$[0]') AS bigint))
+OUTPUT 1
+WHERE [Id] = @p2;",
+ //
+ @"SELECT TOP(2) [j].[Id], JSON_QUERY([j].[Collection],'$'), JSON_QUERY([j].[Reference],'$')
+FROM [JsonEntitiesAllTypes] AS [j]");
+ }
+
+ public override async Task Edit_single_property_uint64()
+ {
+ await base.Edit_single_property_uint64();
+
+ 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].TestUnsignedInt64', CAST(JSON_VALUE(@p0, '$[0]') AS decimal(20,0))), [Reference] = JSON_MODIFY([Reference], 'strict $.TestUnsignedInt64', CAST(JSON_VALUE(@p1, '$[0]') AS decimal(20,0)))
+OUTPUT 1
+WHERE [Id] = @p2;",
+ //
+ @"SELECT TOP(2) [j].[Id], JSON_QUERY([j].[Collection],'$'), JSON_QUERY([j].[Reference],'$')
+FROM [JsonEntitiesAllTypes] AS [j]");
+ }
+
+ public override async Task Edit_two_properties_on_same_entity_updates_the_entire_entity()
+ {
+ await base.Edit_two_properties_on_same_entity_updates_the_entire_entity();
+
+ AssertSql(
+ @"@p0='{""TestBoolean"":false,""TestByte"":25,""TestCharacter"":""h"",""TestDateTime"":""2100-11-11T12:34:56"",""TestDateTimeOffset"":""2200-11-11T12:34:56-05:00"",""TestDecimal"":-123450.01,""TestDouble"":-1.2345,""TestGuid"":""00000000-0000-0000-0000-000000000000"",""TestInt16"":-12,""TestInt32"":32,""TestInt64"":64,""TestSignedByte"":-18,""TestSingle"":-1.4,""TestTimeSpan"":""06:05:04.0030000"",""TestUnsignedInt16"":12,""TestUnsignedInt32"":12345,""TestUnsignedInt64"":1234567867}' (Nullable = false) (Size = 436)
+@p1='{""TestBoolean"":true,""TestByte"":255,""TestCharacter"":""a"",""TestDateTime"":""2000-01-01T12:34:56"",""TestDateTimeOffset"":""2000-01-01T12:34:56-08:00"",""TestDecimal"":-1234567890.01,""TestDouble"":-1.23456789,""TestGuid"":""12345678-1234-4321-7777-987654321000"",""TestInt16"":-1234,""TestInt32"":32,""TestInt64"":64,""TestSignedByte"":-128,""TestSingle"":-1.234,""TestTimeSpan"":""10:09:08.0070000"",""TestUnsignedInt16"":1234,""TestUnsignedInt32"":1234565789,""TestUnsignedInt64"":1234567890123456789}' (Nullable = false) (Size = 465)
+@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;",
+ //
+ @"SELECT TOP(2) [j].[Id], JSON_QUERY([j].[Collection],'$'), JSON_QUERY([j].[Reference],'$')
+FROM [JsonEntitiesAllTypes] AS [j]");
+ }
+
+ public override async Task Edit_a_scalar_property_and_reference_navigation_on_the_same_entity()
+ {
+ await base.Edit_a_scalar_property_and_reference_navigation_on_the_same_entity();
+
+ AssertSql(
+ @"@p0='{""Date"":""2100-01-01T00:00:00"",""Enum"":""One"",""Fraction"":523.532,""OwnedCollectionLeaf"":[{""SomethingSomething"":""e1_r_r_c1""},{""SomethingSomething"":""e1_r_r_c2""}],""OwnedReferenceLeaf"":{""SomethingSomething"":""edit""}}' (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;",
+ //
+ @"SELECT TOP(2) [j].[Id], [j].[Name], JSON_QUERY([j].[OwnedCollectionRoot],'$'), JSON_QUERY([j].[OwnedReferenceRoot],'$')
+FROM [JsonEntitiesBasic] AS [j]");
+ }
+
+ public override async Task Edit_a_scalar_property_and_collection_navigation_on_the_same_entity()
+ {
+ await base.Edit_a_scalar_property_and_collection_navigation_on_the_same_entity();
+
+ AssertSql(
+ @"@p0='{""Date"":""2100-01-01T00:00:00"",""Enum"":""One"",""Fraction"":523.532,""OwnedCollectionLeaf"":[{""SomethingSomething"":""edit""}],""OwnedReferenceLeaf"":{""SomethingSomething"":""e1_r_r_r""}}' (Nullable = false) (Size = 171)
+@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;",
+ //
+ @"SELECT TOP(2) [j].[Id], [j].[Name], JSON_QUERY([j].[OwnedCollectionRoot],'$'), JSON_QUERY([j].[OwnedReferenceRoot],'$')
+FROM [JsonEntitiesBasic] AS [j]");
+ }
+
+ public override async Task Edit_a_scalar_property_and_another_property_behind_reference_navigation_on_the_same_entity()
+ {
+ await base.Edit_a_scalar_property_and_another_property_behind_reference_navigation_on_the_same_entity();
+
+ AssertSql(
+ @"@p0='{""Date"":""2100-01-01T00:00:00"",""Enum"":""One"",""Fraction"":523.532,""OwnedCollectionLeaf"":[{""SomethingSomething"":""e1_r_r_c1""},{""SomethingSomething"":""e1_r_r_c2""}],""OwnedReferenceLeaf"":{""SomethingSomething"":""edit""}}' (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;",
+ //
+ @"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)