From d01866ab7a9c28d5fd2090d699ca2d4d2c242b53 Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Fri, 27 Oct 2023 23:10:27 +0200 Subject: [PATCH] Various stuff from EFCore.PG JSON work --- .../Properties/RelationalStrings.Designer.cs | 8 +++ .../Properties/RelationalStrings.resx | 3 + ...sitor.ShaperProcessingExpressionVisitor.cs | 11 ++- .../Update/ModificationCommand.cs | 10 ++- ...yableMethodTranslatingExpressionVisitor.cs | 1 + .../InternalEntityEntry.OriginalValues.cs | 31 ++++----- .../Internal/InternalEntityEntry.cs | 3 +- .../Internal/SnapshotFactoryFactory.cs | 38 +++++------ .../JsonTypesRelationalTestBase.cs | 53 +++++++++++++++ .../Query/JsonQueryFixtureBase.cs | 9 +-- .../TestUtilities/TestSqlLoggerFactory.cs | 2 +- .../Query/Ef6GroupByTestBase.cs | 9 +-- .../JsonTypesSqlServerTestBase.cs | 49 ------------- .../Query/JsonQueryAdHocSqlServerTest.cs | 68 ++++++++++++------- 14 files changed, 158 insertions(+), 137 deletions(-) diff --git a/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs b/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs index 2b9713407ab..44d3a756ef5 100644 --- a/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs +++ b/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs @@ -1183,6 +1183,14 @@ public static string JsonReaderInvalidTokenType(object? tokenType) GetString("JsonReaderInvalidTokenType", nameof(tokenType)), tokenType); + /// + /// Type mapping type '{typeMapping}', which is being used on property '{property}' on entity type '{entityType}' in a JSON document, has not defined a JsonValueReaderWriter. + /// + public static string JsonValueReadWriterMissingOnTypeMapping(object? typeMapping, object? property, object? entityType) + => string.Format( + GetString("JsonValueReadWriterMissingOnTypeMapping", nameof(typeMapping), nameof(property), nameof(entityType)), + typeMapping, property, entityType); + /// /// Entity {entity} is required but the JSON element containing it is null. /// diff --git a/src/EFCore.Relational/Properties/RelationalStrings.resx b/src/EFCore.Relational/Properties/RelationalStrings.resx index f426a92d9d0..806923d113b 100644 --- a/src/EFCore.Relational/Properties/RelationalStrings.resx +++ b/src/EFCore.Relational/Properties/RelationalStrings.resx @@ -565,6 +565,9 @@ Invalid token type: '{tokenType}'. + + Type mapping type '{typeMapping}', which is being used on property '{property}' on entity type '{entityType}' in a JSON document, has not defined a JsonValueReaderWriter. + Entity {entity} is required but the JSON element containing it is null. diff --git a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.cs index 50a2ca24f90..d3ea92d5912 100644 --- a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.cs @@ -2528,8 +2528,15 @@ private Expression CreateReadJsonPropertyValueExpression( var nullable = property.IsNullable; var typeMapping = property.GetTypeMapping(); - var jsonReaderWriterExpression = Constant( - property.GetJsonValueReaderWriter() ?? property.GetTypeMapping().JsonValueReaderWriter!); + var jsonValueReaderWriter = property.GetJsonValueReaderWriter() ?? property.GetTypeMapping().JsonValueReaderWriter; + if (jsonValueReaderWriter is null) + { + throw new InvalidOperationException( + RelationalStrings.JsonValueReadWriterMissingOnTypeMapping( + property.GetTypeMapping().GetType().Name, property.Name, property.DeclaringType.DisplayName())); + } + + var jsonReaderWriterExpression = Constant(jsonValueReaderWriter); var fromJsonMethod = jsonReaderWriterExpression.Type.GetMethod( nameof(JsonValueReaderWriter.FromJsonTyped), diff --git a/src/EFCore.Relational/Update/ModificationCommand.cs b/src/EFCore.Relational/Update/ModificationCommand.cs index 9338966f332..ad8108d2091 100644 --- a/src/EFCore.Relational/Update/ModificationCommand.cs +++ b/src/EFCore.Relational/Update/ModificationCommand.cs @@ -873,7 +873,15 @@ private void WriteJson( if (value is not null) { - (property.GetJsonValueReaderWriter() ?? property.GetTypeMapping().JsonValueReaderWriter)!.ToJson(writer, value); + var jsonValueReaderWriter = property.GetJsonValueReaderWriter() ?? property.GetTypeMapping().JsonValueReaderWriter; + if (jsonValueReaderWriter is null) + { + throw new InvalidOperationException( + RelationalStrings.JsonValueReadWriterMissingOnTypeMapping( + property.GetTypeMapping().GetType().Name, property.Name, entityType.DisplayName())); + } + + jsonValueReaderWriter.ToJson(writer, value); } else { diff --git a/src/EFCore.SqlServer/Query/Internal/SqlServerQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerQueryableMethodTranslatingExpressionVisitor.cs index be2c39fcfe2..4cf4811e572 100644 --- a/src/EFCore.SqlServer/Query/Internal/SqlServerQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.SqlServer/Query/Internal/SqlServerQueryableMethodTranslatingExpressionVisitor.cs @@ -250,6 +250,7 @@ protected override ShapedQueryExpression TransformJsonQueryToTable(JsonQueryExpr } } + // Navigations represent nested JSON owned entities, which we also add to the OPENJSON WITH clause, but with AS JSON. foreach (var navigation in GetAllNavigationsInHierarchy(jsonQueryExpression.EntityType) .Where( n => n.ForeignKey.IsOwnership diff --git a/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.OriginalValues.cs b/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.OriginalValues.cs index 8aad9eb21dc..1e1b15bb8de 100644 --- a/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.OriginalValues.cs +++ b/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.OriginalValues.cs @@ -17,27 +17,20 @@ public OriginalValues(InternalEntityEntry entry) } public object? GetValue(InternalEntityEntry entry, IProperty property) - { - var index = property.GetOriginalValueIndex(); - if (index == -1) - { - throw new InvalidOperationException( - CoreStrings.OriginalValueNotTracked(property.Name, property.DeclaringType.DisplayName())); - } - - return IsEmpty ? entry[property] : _values[index]; - } + => property.GetOriginalValueIndex() is var index && index == -1 + ? throw new InvalidOperationException( + CoreStrings.OriginalValueNotTracked(property.Name, property.DeclaringType.DisplayName())) + : IsEmpty + ? entry[property] + : _values[index]; public T GetValue(InternalEntityEntry entry, IProperty property, int index) - { - if (index == -1) - { - throw new InvalidOperationException( - CoreStrings.OriginalValueNotTracked(property.Name, property.DeclaringType.DisplayName())); - } - - return IsEmpty ? entry.GetCurrentValue(property) : _values.GetValue(index); - } + => index == -1 + ? throw new InvalidOperationException( + CoreStrings.OriginalValueNotTracked(property.Name, property.DeclaringType.DisplayName())) + : IsEmpty + ? entry.GetCurrentValue(property) + : _values.GetValue(index); public void SetValue(IProperty property, object? value, int index) { diff --git a/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.cs b/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.cs index 846a23acc36..3e1b08c2a5d 100644 --- a/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.cs +++ b/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.cs @@ -922,8 +922,7 @@ public T ReadTemporaryValue(int storeGeneratedIndex) => _temporaryValues.GetValue(storeGeneratedIndex); private static readonly MethodInfo GetCurrentValueMethod - = typeof(InternalEntityEntry).GetTypeInfo().GetDeclaredMethods(nameof(GetCurrentValue)).Single( - m => m.IsGenericMethod); + = typeof(InternalEntityEntry).GetTypeInfo().GetDeclaredMethods(nameof(GetCurrentValue)).Single(m => m.IsGenericMethod); [UnconditionalSuppressMessage( "ReflectionAnalysis", "IL2060", diff --git a/src/EFCore/ChangeTracking/Internal/SnapshotFactoryFactory.cs b/src/EFCore/ChangeTracking/Internal/SnapshotFactoryFactory.cs index aba7539759d..2ef9109cc37 100644 --- a/src/EFCore/ChangeTracking/Internal/SnapshotFactoryFactory.cs +++ b/src/EFCore/ChangeTracking/Internal/SnapshotFactoryFactory.cs @@ -106,29 +106,25 @@ protected virtual Expression CreateSnapshotExpression( for (var i = 0; i < count; i++) { var propertyBase = propertyBases[i]; - if (propertyBase == null) - { - arguments[i] = Expression.Constant(null); - types[i] = typeof(object); - continue; - } - - if (propertyBase is IProperty property) - { - arguments[i] = CreateSnapshotValueExpression(CreateReadValueExpression(parameter, property), property); - continue; - } - - if (propertyBase is IComplexProperty complexProperty) - { - arguments[i] = CreateSnapshotValueExpression(CreateReadValueExpression(parameter, complexProperty), complexProperty); - continue; - } - if (propertyBase.IsShadowProperty()) + switch (propertyBase) { - arguments[i] = CreateSnapshotValueExpression(CreateReadShadowValueExpression(parameter, propertyBase), propertyBase); - continue; + case null: + arguments[i] = Expression.Constant(null); + types[i] = typeof(object); + continue; + + case IProperty property: + arguments[i] = CreateSnapshotValueExpression(CreateReadValueExpression(parameter, property), property); + continue; + + case IComplexProperty complexProperty: + arguments[i] = CreateSnapshotValueExpression(CreateReadValueExpression(parameter, complexProperty), complexProperty); + continue; + + case var _ when propertyBase.IsShadowProperty(): + arguments[i] = CreateSnapshotValueExpression(CreateReadShadowValueExpression(parameter, propertyBase), propertyBase); + continue; } var memberInfo = propertyBase.GetMemberInfo(forMaterialization: false, forSet: false); diff --git a/test/EFCore.Relational.Specification.Tests/JsonTypesRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/JsonTypesRelationalTestBase.cs index 227948d01fd..a549180e00f 100644 --- a/test/EFCore.Relational.Specification.Tests/JsonTypesRelationalTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/JsonTypesRelationalTestBase.cs @@ -47,6 +47,59 @@ public virtual void Can_read_write_collection_of_ASCII_string_JSON_values(object { RelationalAnnotationNames.StoreType, storeType }, { CoreAnnotationNames.Unicode, false } }); + public override void Can_read_write_ulong_enum_JSON_values(EnumU64 value, string json) + { + // Relational databases don't support unsigned numeric types, so ulong is value-converted to long + if (value == EnumU64.Max) + { + json = """{"Prop":-1}"""; + } + + base.Can_read_write_ulong_enum_JSON_values(value, json); + } + + public override void Can_read_write_nullable_ulong_enum_JSON_values(object? value, string json) + { + // Relational databases don't support unsigned numeric types, so ulong is value-converted to long + if (Equals(value, ulong.MaxValue)) + { + json = """{"Prop":-1}"""; + } + + base.Can_read_write_nullable_ulong_enum_JSON_values(value, json); + } + + public override void Can_read_write_collection_of_ulong_enum_JSON_values() + => Can_read_and_write_JSON_value>( + nameof(EnumU64CollectionType.EnumU64), + new List + { + EnumU64.Min, + EnumU64.Max, + EnumU64.Default, + EnumU64.One, + (EnumU64)8 + }, + // Relational databases don't support unsigned numeric types, so ulong is value-converted to long + """{"Prop":[0,-1,0,1,8]}""", + mappedCollection: true); + + public override void Can_read_write_collection_of_nullable_ulong_enum_JSON_values() + => Can_read_and_write_JSON_value>( + nameof(NullableEnumU64CollectionType.EnumU64), + new List + { + EnumU64.Min, + null, + EnumU64.Max, + EnumU64.Default, + EnumU64.One, + (EnumU64?)8 + }, + // Relational databases don't support unsigned numeric types, so ulong is value-converted to long + """{"Prop":[0,null,-1,0,1,8]}""", + mappedCollection: true); + protected override void AssertElementFacets(IElementType element, Dictionary? facets) { base.AssertElementFacets(element, facets); diff --git a/test/EFCore.Relational.Specification.Tests/Query/JsonQueryFixtureBase.cs b/test/EFCore.Relational.Specification.Tests/Query/JsonQueryFixtureBase.cs index a6d6e12fd46..a78303d3971 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/JsonQueryFixtureBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/JsonQueryFixtureBase.cs @@ -13,14 +13,7 @@ public Func GetContextCreator() => () => CreateContext(); public virtual ISetSource GetExpectedData() - { - if (_expectedData == null) - { - _expectedData = new JsonQueryData(); - } - - return _expectedData; - } + => _expectedData ??= new JsonQueryData(); public IReadOnlyDictionary EntitySorters { get; } = new Dictionary> { diff --git a/test/EFCore.Relational.Specification.Tests/TestUtilities/TestSqlLoggerFactory.cs b/test/EFCore.Relational.Specification.Tests/TestUtilities/TestSqlLoggerFactory.cs index bf57b50d51e..12eb14f7557 100644 --- a/test/EFCore.Relational.Specification.Tests/TestUtilities/TestSqlLoggerFactory.cs +++ b/test/EFCore.Relational.Specification.Tests/TestUtilities/TestSqlLoggerFactory.cs @@ -254,7 +254,7 @@ void RewriteSourceWithNewBaseline(string fileName, int lineNumber) indentBuilder.Append(" "); var indent = indentBuilder.ToString(); var newBaseLine = $@"Assert{(forUpdate ? "ExecuteUpdate" : "")}Sql( -{string.Join("," + Environment.NewLine + indent + "//" + Environment.NewLine, SqlStatements.Skip(offset).Take(count).Select(sql => "\"\"\"" + Environment.NewLine + sql + Environment.NewLine + "\"\"\""))})"; +{string.Join("," + Environment.NewLine + indent + "//" + Environment.NewLine, SqlStatements.Skip(offset).Take(count).Select(sql => indent + "\"\"\"" + Environment.NewLine + sql + Environment.NewLine + "\"\"\""))})"; var numNewlinesInRewritten = newBaseLine.Count(c => c is '\n' or '\r'); writer.Write(newBaseLine); diff --git a/test/EFCore.Specification.Tests/Query/Ef6GroupByTestBase.cs b/test/EFCore.Specification.Tests/Query/Ef6GroupByTestBase.cs index 1be89215b40..c67ddb8862f 100644 --- a/test/EFCore.Specification.Tests/Query/Ef6GroupByTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/Ef6GroupByTestBase.cs @@ -808,14 +808,7 @@ protected override void Seed(ArubaContext context) => new ArubaData(context); public virtual ISetSource GetExpectedData() - { - if (_expectedData == null) - { - _expectedData = new ArubaData(); - } - - return _expectedData; - } + => _expectedData ??= new ArubaData(); public IReadOnlyDictionary EntitySorters { get; } = new Dictionary> { diff --git a/test/EFCore.SqlServer.FunctionalTests/JsonTypesSqlServerTestBase.cs b/test/EFCore.SqlServer.FunctionalTests/JsonTypesSqlServerTestBase.cs index 27ef33a5951..933fb04927a 100644 --- a/test/EFCore.SqlServer.FunctionalTests/JsonTypesSqlServerTestBase.cs +++ b/test/EFCore.SqlServer.FunctionalTests/JsonTypesSqlServerTestBase.cs @@ -7,55 +7,6 @@ namespace Microsoft.EntityFrameworkCore; public abstract class JsonTypesSqlServerTestBase : JsonTypesRelationalTestBase { - public override void Can_read_write_ulong_enum_JSON_values(EnumU64 value, string json) - { - if (value == EnumU64.Max) - { - json = """{"Prop":-1}"""; // Because ulong is converted to long on SQL Server - } - - base.Can_read_write_ulong_enum_JSON_values(value, json); - } - - public override void Can_read_write_nullable_ulong_enum_JSON_values(object? value, string json) - { - if (Equals(value, ulong.MaxValue)) - { - json = """{"Prop":-1}"""; // Because ulong is converted to long on SQL Server - } - - base.Can_read_write_nullable_ulong_enum_JSON_values(value, json); - } - - public override void Can_read_write_collection_of_ulong_enum_JSON_values() - => Can_read_and_write_JSON_value>( - nameof(EnumU64CollectionType.EnumU64), - new List - { - EnumU64.Min, - EnumU64.Max, - EnumU64.Default, - EnumU64.One, - (EnumU64)8 - }, - """{"Prop":[0,-1,0,1,8]}""", // Because ulong is converted to long on SQL Server - mappedCollection: true); - - public override void Can_read_write_collection_of_nullable_ulong_enum_JSON_values() - => Can_read_and_write_JSON_value>( - nameof(NullableEnumU64CollectionType.EnumU64), - new List - { - EnumU64.Min, - null, - EnumU64.Max, - EnumU64.Default, - EnumU64.One, - (EnumU64?)8 - }, - """{"Prop":[0,null,-1,0,1,8]}""", // Because ulong is converted to long on SQL Server - mappedCollection: true); - public override void Can_read_write_collection_of_fixed_length_string_JSON_values(object? storeType) => base.Can_read_write_collection_of_fixed_length_string_JSON_values("nchar(32)"); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/JsonQueryAdHocSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/JsonQueryAdHocSqlServerTest.cs index 5608c26e7ff..d34cc8fff18 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/JsonQueryAdHocSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/JsonQueryAdHocSqlServerTest.cs @@ -37,39 +37,49 @@ protected override void Seed29219(MyContext29219 ctx) ctx.SaveChanges(); ctx.Database.ExecuteSqlRaw( - @"INSERT INTO [Entities] ([Id], [Reference], [Collection]) -VALUES(3, N'{{ ""NonNullableScalar"" : 30 }}', N'[{{ ""NonNullableScalar"" : 10001 }}]')"); + """ +INSERT INTO [Entities] ([Id], [Reference], [Collection]) +VALUES(3, N'{{ "NonNullableScalar" : 30 }}', N'[{{ "NonNullableScalar" : 10001 }}]') +"""); } protected override void Seed30028(MyContext30028 ctx) { // complete ctx.Database.ExecuteSqlRaw( - @"INSERT INTO [Entities] ([Id], [Json]) + """ +INSERT INTO [Entities] ([Id], [Json]) VALUES( 1, -N'{{""RootName"":""e1"",""Collection"":[{{""BranchName"":""e1 c1"",""Nested"":{{""LeafName"":""e1 c1 l""}}}},{{""BranchName"":""e1 c2"",""Nested"":{{""LeafName"":""e1 c2 l""}}}}],""OptionalReference"":{{""BranchName"":""e1 or"",""Nested"":{{""LeafName"":""e1 or l""}}}},""RequiredReference"":{{""BranchName"":""e1 rr"",""Nested"":{{""LeafName"":""e1 rr l""}}}}}}')"); +N'{{"RootName":"e1","Collection":[{{"BranchName":"e1 c1","Nested":{{"LeafName":"e1 c1 l"}}}},{{"BranchName":"e1 c2","Nested":{{"LeafName":"e1 c2 l"}}}}],"OptionalReference":{{"BranchName":"e1 or","Nested":{{"LeafName":"e1 or l"}}}},"RequiredReference":{{"BranchName":"e1 rr","Nested":{{"LeafName":"e1 rr l"}}}}}}') +"""); // missing collection ctx.Database.ExecuteSqlRaw( - @"INSERT INTO [Entities] ([Id], [Json]) + """ +INSERT INTO [Entities] ([Id], [Json]) VALUES( 2, -N'{{""RootName"":""e2"",""OptionalReference"":{{""BranchName"":""e2 or"",""Nested"":{{""LeafName"":""e2 or l""}}}},""RequiredReference"":{{""BranchName"":""e2 rr"",""Nested"":{{""LeafName"":""e2 rr l""}}}}}}')"); +N'{{"RootName":"e2","OptionalReference":{{"BranchName":"e2 or","Nested":{{"LeafName":"e2 or l"}}}},"RequiredReference":{{"BranchName":"e2 rr","Nested":{{"LeafName":"e2 rr l"}}}}}}') +"""); // missing optional reference ctx.Database.ExecuteSqlRaw( - @"INSERT INTO [Entities] ([Id], [Json]) + """ +INSERT INTO [Entities] ([Id], [Json]) VALUES( 3, -N'{{""RootName"":""e3"",""Collection"":[{{""BranchName"":""e3 c1"",""Nested"":{{""LeafName"":""e3 c1 l""}}}},{{""BranchName"":""e3 c2"",""Nested"":{{""LeafName"":""e3 c2 l""}}}}],""RequiredReference"":{{""BranchName"":""e3 rr"",""Nested"":{{""LeafName"":""e3 rr l""}}}}}}')"); +N'{{"RootName":"e3","Collection":[{{"BranchName":"e3 c1","Nested":{{"LeafName":"e3 c1 l"}}}},{{"BranchName":"e3 c2","Nested":{{"LeafName":"e3 c2 l"}}}}],"RequiredReference":{{"BranchName":"e3 rr","Nested":{{"LeafName":"e3 rr l"}}}}}}') +"""); // missing required reference ctx.Database.ExecuteSqlRaw( - @"INSERT INTO [Entities] ([Id], [Json]) + """ +INSERT INTO [Entities] ([Id], [Json]) VALUES( 4, -N'{{""RootName"":""e4"",""Collection"":[{{""BranchName"":""e4 c1"",""Nested"":{{""LeafName"":""e4 c1 l""}}}},{{""BranchName"":""e4 c2"",""Nested"":{{""LeafName"":""e4 c2 l""}}}}],""OptionalReference"":{{""BranchName"":""e4 or"",""Nested"":{{""LeafName"":""e4 or l""}}}}}}')"); +N'{{"RootName":"e4","Collection":[{{"BranchName":"e4 c1","Nested":{{"LeafName":"e4 c1 l"}}}},{{"BranchName":"e4 c2","Nested":{{"LeafName":"e4 c2 l"}}}}],"OptionalReference":{{"BranchName":"e4 or","Nested":{{"LeafName":"e4 or l"}}}}}}') +"""); } protected override void SeedArrayOfPrimitives(MyContextArrayOfPrimitives ctx) @@ -120,24 +130,28 @@ protected override void SeedArrayOfPrimitives(MyContextArrayOfPrimitives ctx) protected override void SeedJunkInJson(MyContextJunkInJson ctx) => ctx.Database.ExecuteSqlRaw( - @"INSERT INTO [Entities] ([Collection], [CollectionWithCtor], [Reference], [ReferenceWithCtor], [Id]) + """ +INSERT INTO [Entities] ([Collection], [CollectionWithCtor], [Reference], [ReferenceWithCtor], [Id]) VALUES( -N'[{{""JunkReference"":{{""Something"":""SomeValue"" }},""Name"":""c11"",""JunkProperty1"":50,""Number"":11.5,""JunkCollection1"":[],""JunkCollection2"":[{{""Foo"":""junk value""}}],""NestedCollection"":[{{""DoB"":""2002-04-01T00:00:00"",""DummyProp"":""Dummy value""}},{{""DoB"":""2002-04-02T00:00:00"",""DummyReference"":{{""Foo"":5}}}}],""NestedReference"":{{""DoB"":""2002-03-01T00:00:00""}}}},{{""Name"":""c12"",""Number"":12.5,""NestedCollection"":[{{""DoB"":""2002-06-01T00:00:00""}},{{""DoB"":""2002-06-02T00:00:00""}}],""NestedDummy"":59,""NestedReference"":{{""DoB"":""2002-05-01T00:00:00""}}}}]', -N'[{{""MyBool"":true,""Name"":""c11 ctor"",""JunkReference"":{{""Something"":""SomeValue"",""JunkCollection"":[{{""Foo"":""junk value""}}]}},""NestedCollection"":[{{""DoB"":""2002-08-01T00:00:00""}},{{""DoB"":""2002-08-02T00:00:00""}}],""NestedReference"":{{""DoB"":""2002-07-01T00:00:00""}}}},{{""MyBool"":false,""Name"":""c12 ctor"",""NestedCollection"":[{{""DoB"":""2002-10-01T00:00:00""}},{{""DoB"":""2002-10-02T00:00:00""}}],""JunkCollection"":[{{""Foo"":""junk value""}}],""NestedReference"":{{""DoB"":""2002-09-01T00:00:00""}}}}]', -N'{{""Name"":""r1"",""JunkCollection"":[{{""Foo"":""junk value""}}],""JunkReference"":{{""Something"":""SomeValue"" }},""Number"":1.5,""NestedCollection"":[{{""DoB"":""2000-02-01T00:00:00"",""JunkReference"":{{""Something"":""SomeValue""}}}},{{""DoB"":""2000-02-02T00:00:00""}}],""NestedReference"":{{""DoB"":""2000-01-01T00:00:00""}}}}', -N'{{""MyBool"":true,""JunkCollection"":[{{""Foo"":""junk value""}}],""Name"":""r1 ctor"",""JunkReference"":{{""Something"":""SomeValue"" }},""NestedCollection"":[{{""DoB"":""2001-02-01T00:00:00""}},{{""DoB"":""2001-02-02T00:00:00""}}],""NestedReference"":{{""JunkCollection"":[{{""Foo"":""junk value""}}],""DoB"":""2001-01-01T00:00:00""}}}}', -1)"); +N'[{{"JunkReference":{{"Something":"SomeValue" }},"Name":"c11","JunkProperty1":50,"Number":11.5,"JunkCollection1":[],"JunkCollection2":[{{"Foo":"junk value"}}],"NestedCollection":[{{"DoB":"2002-04-01T00:00:00","DummyProp":"Dummy value"}},{{"DoB":"2002-04-02T00:00:00","DummyReference":{{"Foo":5}}}}],"NestedReference":{{"DoB":"2002-03-01T00:00:00"}}}},{{"Name":"c12","Number":12.5,"NestedCollection":[{{"DoB":"2002-06-01T00:00:00"}},{{"DoB":"2002-06-02T00:00:00"}}],"NestedDummy":59,"NestedReference":{{"DoB":"2002-05-01T00:00:00"}}}}]', +N'[{{"MyBool":true,"Name":"c11 ctor","JunkReference":{{"Something":"SomeValue","JunkCollection":[{{"Foo":"junk value"}}]}},"NestedCollection":[{{"DoB":"2002-08-01T00:00:00"}},{{"DoB":"2002-08-02T00:00:00"}}],"NestedReference":{{"DoB":"2002-07-01T00:00:00"}}}},{{"MyBool":false,"Name":"c12 ctor","NestedCollection":[{{"DoB":"2002-10-01T00:00:00"}},{{"DoB":"2002-10-02T00:00:00"}}],"JunkCollection":[{{"Foo":"junk value"}}],"NestedReference":{{"DoB":"2002-09-01T00:00:00"}}}}]', +N'{{"Name":"r1","JunkCollection":[{{"Foo":"junk value"}}],"JunkReference":{{"Something":"SomeValue" }},"Number":1.5,"NestedCollection":[{{"DoB":"2000-02-01T00:00:00","JunkReference":{{"Something":"SomeValue"}}}},{{"DoB":"2000-02-02T00:00:00"}}],"NestedReference":{{"DoB":"2000-01-01T00:00:00"}}}}', +N'{{"MyBool":true,"JunkCollection":[{{"Foo":"junk value"}}],"Name":"r1 ctor","JunkReference":{{"Something":"SomeValue" }},"NestedCollection":[{{"DoB":"2001-02-01T00:00:00"}},{{"DoB":"2001-02-02T00:00:00"}}],"NestedReference":{{"JunkCollection":[{{"Foo":"junk value"}}],"DoB":"2001-01-01T00:00:00"}}}}', +1) +"""); protected override void SeedShadowProperties(MyContextShadowProperties ctx) => ctx.Database.ExecuteSqlRaw( - @"INSERT INTO [Entities] ([Collection], [CollectionWithCtor], [Reference], [ReferenceWithCtor], [Id], [Name]) + """ +INSERT INTO [Entities] ([Collection], [CollectionWithCtor], [Reference], [ReferenceWithCtor], [Id], [Name]) VALUES( -N'[{{""Name"":""e1_c1"",""ShadowDouble"":5.5}},{{""ShadowDouble"":20.5,""Name"":""e1_c2""}}]', -N'[{{""Name"":""e1_c1 ctor"",""ShadowNullableByte"":6}},{{""ShadowNullableByte"":null,""Name"":""e1_c2 ctor""}}]', -N'{{""Name"":""e1_r"", ""ShadowString"":""Foo""}}', -N'{{""ShadowInt"":143,""Name"":""e1_r ctor""}}', +N'[{{"Name":"e1_c1","ShadowDouble":5.5}},{{"ShadowDouble":20.5,"Name":"e1_c2"}}]', +N'[{{"Name":"e1_c1 ctor","ShadowNullableByte":6}},{{"ShadowNullableByte":null,"Name":"e1_c2 ctor"}}]', +N'{{"Name":"e1_r", "ShadowString":"Foo"}}', +N'{{"ShadowInt":143,"Name":"e1_r ctor"}}', 1, -N'e1')"); +N'e1') +"""); #region EnumLegacyValues @@ -254,12 +268,14 @@ public virtual async Task Read_json_entity_collection_with_enum_properties_with_ private void SeedEnumLegacyValues(MyContextEnumLegacyValues ctx) => ctx.Database.ExecuteSqlRaw( - @"INSERT INTO [Entities] ([Collection], [Reference], [Id], [Name]) + """ +INSERT INTO [Entities] ([Collection], [Reference], [Id], [Name]) VALUES( -N'[{{""ByteEnum"":""Bellevue"",""IntEnum"":""Foo"",""LongEnum"":""One"",""ULongEnum"":""One"",""Name"":""e1_c1"",""NullableEnum"":""Bar""}},{{""ByteEnum"":""Seattle"",""IntEnum"":""Baz"",""LongEnum"":""Two"",""ULongEnum"":""Two"",""Name"":""e1_c2"",""NullableEnum"":null}}]', -N'{{""ByteEnum"":""Redmond"",""IntEnum"":""Foo"",""LongEnum"":""Three"",""ULongEnum"":""Three"",""Name"":""e1_r"",""NullableEnum"":""Bar""}}', +N'[{{"ByteEnum":"Bellevue","IntEnum":"Foo","LongEnum":"One","ULongEnum":"One","Name":"e1_c1","NullableEnum":"Bar"}},{{"ByteEnum":"Seattle","IntEnum":"Baz","LongEnum":"Two","ULongEnum":"Two","Name":"e1_c2","NullableEnum":null}}]', +N'{{"ByteEnum":"Redmond","IntEnum":"Foo","LongEnum":"Three","ULongEnum":"Three","Name":"e1_r","NullableEnum":"Bar"}}', 1, -N'e1')"); +N'e1') +"""); private class MyContextEnumLegacyValues : DbContext {