diff --git a/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs b/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs index c114e0553e7..55c4ea6edf5 100644 --- a/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs +++ b/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs @@ -1388,8 +1388,6 @@ private static bool IsIdentifyingPrincipal(IEntityType dependentEntityType, IEnt storeObject.DisplayName())); } - var typeMapping = property.GetRelationalTypeMapping(); - var duplicateTypeMapping = duplicateProperty.GetRelationalTypeMapping(); var currentTypeString = property.GetColumnType(storeObject); var previousTypeString = duplicateProperty.GetColumnType(storeObject); if (!string.Equals(currentTypeString, previousTypeString, StringComparison.OrdinalIgnoreCase)) @@ -1406,8 +1404,12 @@ private static bool IsIdentifyingPrincipal(IEntityType dependentEntityType, IEnt currentTypeString)); } - var currentProviderType = typeMapping.Converter?.ProviderClrType ?? typeMapping.ClrType; - var previousProviderType = duplicateTypeMapping.Converter?.ProviderClrType ?? duplicateTypeMapping.ClrType; + var typeMapping = property.GetRelationalTypeMapping(); + var duplicateTypeMapping = duplicateProperty.GetRelationalTypeMapping(); + var currentProviderType = typeMapping.Converter?.ProviderClrType.UnwrapNullableType() + ?? typeMapping.ClrType; + var previousProviderType = duplicateTypeMapping.Converter?.ProviderClrType.UnwrapNullableType() + ?? duplicateTypeMapping.ClrType; if (currentProviderType != previousProviderType) { throw new InvalidOperationException( diff --git a/src/EFCore.Relational/Metadata/Internal/ColumnBase.cs b/src/EFCore.Relational/Metadata/Internal/ColumnBase.cs index cd7fdaff3b6..0ee0d3eb1b0 100644 --- a/src/EFCore.Relational/Metadata/Internal/ColumnBase.cs +++ b/src/EFCore.Relational/Metadata/Internal/ColumnBase.cs @@ -84,7 +84,7 @@ public virtual Type ProviderClrType } var typeMapping = StoreTypeMapping; - var providerType = typeMapping.Converter?.ProviderClrType ?? typeMapping.ClrType; + var providerType = typeMapping.Converter?.ProviderClrType.UnwrapNullableType() ?? typeMapping.ClrType; return _providerClrType = providerType; } diff --git a/src/EFCore.Relational/Update/Internal/ColumnAccessorsFactory.cs b/src/EFCore.Relational/Update/Internal/ColumnAccessorsFactory.cs index 8e7f9d1ce1a..cdf4c136b2b 100644 --- a/src/EFCore.Relational/Update/Internal/ColumnAccessorsFactory.cs +++ b/src/EFCore.Relational/Update/Internal/ColumnAccessorsFactory.cs @@ -50,8 +50,7 @@ private static ColumnAccessors CreateGeneric(IColumn column) } var providerValue = entry.GetCurrentProviderValue(property); - if (providerValue == null - && !typeof(TColumn).IsNullableType()) + if (providerValue == null) { return (value!, valueFound); } @@ -93,8 +92,7 @@ private static ColumnAccessors CreateGeneric(IColumn column) } var providerValue = entry.GetOriginalProviderValue(property); - if (providerValue == null - && !typeof(TColumn).IsNullableType()) + if (providerValue == null) { return (value!, valueFound); } diff --git a/src/EFCore.Relational/Update/Internal/RowForeignKeyValueFactoryFactory.cs b/src/EFCore.Relational/Update/Internal/RowForeignKeyValueFactoryFactory.cs index bf16ff5289f..3d26c10188a 100644 --- a/src/EFCore.Relational/Update/Internal/RowForeignKeyValueFactoryFactory.cs +++ b/src/EFCore.Relational/Update/Internal/RowForeignKeyValueFactoryFactory.cs @@ -53,29 +53,29 @@ public virtual IRowForeignKeyValueFactory Create(IForeignKeyConstraint foreignKe IValueConverterSelector valueConverterSelector) where TKey : notnull { - var dependentColumn = foreignKey.Columns.Single(); - var dependentType = dependentColumn.ProviderClrType; - var principalType = foreignKey.PrincipalColumns.Single().ProviderClrType; + var dependentColumn = foreignKey.Columns.First(); + var principalColumn = foreignKey.PrincipalColumns.First(); var columnAccessors = ((Column)dependentColumn).Accessors; - if (dependentType.IsNullableType() - && principalType.IsNullableType()) + if (principalColumn.ProviderClrType.IsNullableType() + || (dependentColumn.IsNullable + && principalColumn.IsNullable)) { return new SimpleFullyNullableRowForeignKeyValueFactory( foreignKey, dependentColumn, columnAccessors, valueConverterSelector); } - if (dependentType.IsNullableType()) + if (dependentColumn.IsNullable) { return (IRowForeignKeyValueFactory)Activator.CreateInstance( typeof(SimpleNullableRowForeignKeyValueFactory<,>).MakeGenericType( typeof(TKey), typeof(TForeignKey)), foreignKey, dependentColumn, columnAccessors, valueConverterSelector)!; } - return principalType.IsNullableType() + return principalColumn.IsNullable ? (IRowForeignKeyValueFactory)Activator.CreateInstance( - typeof(SimpleNullablePrincipalRowForeignKeyValueFactory<,,>).MakeGenericType( - typeof(TKey), typeof(TKey).UnwrapNullableType(), typeof(TForeignKey)), foreignKey, dependentColumn, columnAccessors)! + typeof(SimpleNullablePrincipalRowForeignKeyValueFactory<,>).MakeGenericType( + typeof(TKey), typeof(TKey), typeof(TForeignKey)), foreignKey, dependentColumn, columnAccessors)! : new SimpleNonNullableRowForeignKeyValueFactory( foreignKey, dependentColumn, columnAccessors, valueConverterSelector); } diff --git a/src/EFCore.Relational/Update/Internal/SimpleNullablePrincipalRowForeignKeyValueFactory.cs b/src/EFCore.Relational/Update/Internal/SimpleNullablePrincipalRowForeignKeyValueFactory.cs index 5ad7e24a1fe..97f8e1d3e5f 100644 --- a/src/EFCore.Relational/Update/Internal/SimpleNullablePrincipalRowForeignKeyValueFactory.cs +++ b/src/EFCore.Relational/Update/Internal/SimpleNullablePrincipalRowForeignKeyValueFactory.cs @@ -11,9 +11,9 @@ namespace Microsoft.EntityFrameworkCore.Update.Internal; /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// -public class SimpleNullablePrincipalRowForeignKeyValueFactory +public class SimpleNullablePrincipalRowForeignKeyValueFactory : RowForeignKeyValueFactory - where TNonNullableKey : struct + where TKey : notnull { /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to diff --git a/src/EFCore.Relational/Update/Internal/SimpleRowKeyValueFactory.cs b/src/EFCore.Relational/Update/Internal/SimpleRowKeyValueFactory.cs index 5b2af2d9142..9273f1752f7 100644 --- a/src/EFCore.Relational/Update/Internal/SimpleRowKeyValueFactory.cs +++ b/src/EFCore.Relational/Update/Internal/SimpleRowKeyValueFactory.cs @@ -138,8 +138,29 @@ private sealed class NoNullsCustomEqualityComparer : IEqualityComparer public NoNullsCustomEqualityComparer(ValueComparer comparer) { - _equals = (Func)comparer.EqualsExpression.Compile(); - _hashCode = (Func)comparer.HashCodeExpression.Compile(); + var equals = comparer.EqualsExpression; + var getHashCode = comparer.HashCodeExpression; + var type = typeof(TKey); + if (type != comparer.Type) + { + var newEqualsParam1 = Expression.Parameter(type, "v1"); + var newEqualsParam2 = Expression.Parameter(type, "v2"); + equals = Expression.Lambda( + comparer.ExtractEqualsBody( + Expression.Convert(newEqualsParam1, comparer.Type), + Expression.Convert(newEqualsParam2, comparer.Type)), + newEqualsParam1, newEqualsParam2); + + + var newHashCodeParam = Expression.Parameter(type, "v"); + getHashCode = Expression.Lambda( + comparer.ExtractHashCodeBody( + Expression.Convert(newHashCodeParam, comparer.Type)), + newHashCodeParam); + } + + _equals = (Func)equals.Compile(); + _hashCode = (Func)getHashCode.Compile(); } public bool Equals(TKey? x, TKey? y) diff --git a/src/EFCore.Relational/Update/ModificationCommand.cs b/src/EFCore.Relational/Update/ModificationCommand.cs index 02b937c0465..8564e50d916 100644 --- a/src/EFCore.Relational/Update/ModificationCommand.cs +++ b/src/EFCore.Relational/Update/ModificationCommand.cs @@ -981,7 +981,14 @@ public bool TryPropagate(IColumnMappingBase mapping, IUpdateEntry entry) if (property.GetAfterSaveBehavior() == PropertySaveBehavior.Save || entry.EntityState == EntityState.Added) { - entry.SetStoreGeneratedValue(property, _currentValue); + var value = _currentValue; + var converter = property.GetTypeMapping().Converter; + if (converter != null) + { + value = converter.ConvertFromProvider(value); + } + + entry.SetStoreGeneratedValue(property, value); } return false; diff --git a/src/EFCore/ChangeTracking/Internal/CurrentValueComparerFactory.cs b/src/EFCore/ChangeTracking/Internal/CurrentValueComparerFactory.cs index 3afa347deaa..5df96374e28 100644 --- a/src/EFCore/ChangeTracking/Internal/CurrentValueComparerFactory.cs +++ b/src/EFCore/ChangeTracking/Internal/CurrentValueComparerFactory.cs @@ -50,11 +50,11 @@ public virtual IComparer Create(IPropertyBase propertyBase) if (IsGenericComparable(providerType, nonNullableProviderType)) { var comparerType = modelType.IsClass - ? typeof(NullableClassCurrentProviderValueComparer<,>).MakeGenericType(modelType, converter.ProviderClrType) + ? typeof(NullableClassCurrentProviderValueComparer<,>).MakeGenericType(modelType, providerType) : modelType == converter.ModelClrType - ? typeof(CurrentProviderValueComparer<,>).MakeGenericType(modelType, converter.ProviderClrType) + ? typeof(CurrentProviderValueComparer<,>).MakeGenericType(modelType, providerType) : typeof(NullableStructCurrentProviderValueComparer<,>).MakeGenericType( - nonNullableModelType, converter.ProviderClrType); + nonNullableModelType, providerType); return (IComparer)Activator.CreateInstance(comparerType, propertyBase, converter)!; } diff --git a/src/EFCore/ChangeTracking/ValueComparer`.cs b/src/EFCore/ChangeTracking/ValueComparer`.cs index 4712356813b..99e6fffca78 100644 --- a/src/EFCore/ChangeTracking/ValueComparer`.cs +++ b/src/EFCore/ChangeTracking/ValueComparer`.cs @@ -117,8 +117,7 @@ public ValueComparer(bool favorStructuralComparisons) || unwrappedType == typeof(Guid) || unwrappedType == typeof(bool) || unwrappedType == typeof(decimal) - || unwrappedType == typeof(object) - ) + || unwrappedType == typeof(object)) { return Expression.Lambda>( Expression.Equal(param1, param2), diff --git a/src/EFCore/Metadata/Internal/Property.cs b/src/EFCore/Metadata/Internal/Property.cs index 5ea8e5a9849..158c1025604 100644 --- a/src/EFCore/Metadata/Internal/Property.cs +++ b/src/EFCore/Metadata/Internal/Property.cs @@ -787,8 +787,8 @@ public virtual PropertySaveBehavior GetAfterSaveBehavior() => FindAnnotation(CoreAnnotationNames.ProviderClrType)?.GetConfigurationSource(); private Type GetEffectiveProviderClrType() - => TypeMapping?.Converter?.ProviderClrType - ?? ClrType.UnwrapNullableType(); + => (TypeMapping?.Converter?.ProviderClrType + ?? ClrType).UnwrapNullableType(); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -997,7 +997,7 @@ private Type GetEffectiveProviderClrType() /// public virtual ValueComparer? GetProviderValueComparer() => GetProviderValueComparer(null) - ?? (GetEffectiveProviderClrType() == ClrType + ?? (GetEffectiveProviderClrType() == ClrType.UnwrapNullableType() ? GetKeyValueComparer() : TypeMapping?.ProviderValueComparer); diff --git a/test/EFCore.Relational.Specification.Tests/UpdatesRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/UpdatesRelationalTestBase.cs index d732a305aef..e6bcbf9d2e1 100644 --- a/test/EFCore.Relational.Specification.Tests/UpdatesRelationalTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/UpdatesRelationalTestBase.cs @@ -108,6 +108,39 @@ public virtual void Save_with_shared_foreign_key() }); } + [ConditionalFact] + public virtual void Can_use_shared_columns_with_conversion() + => ExecuteWithStrategyInTransaction( + context => + { + var person = new Person("1", null) + { + Address = new Address { Country = Country.Eswatini, City = "Bulembu" }, + Country = "Eswatini" + }; + + context.Add(person); + + context.SaveChanges(); + }, + context => + { + var person = context.Set().Single(); + person.Address = new Address { Country = Country.Türkiye, City = "Konya", ZipCode = 42100 }; + + context.SaveChanges(); + }, + context => + { + var person = context.Set().Single(); + + Assert.Equal(Country.Türkiye, person.Address!.Country); + Assert.Equal("Konya", person.Address.City); + Assert.Equal(42100, person.Address.ZipCode); + Assert.Equal("Türkiye", person.Country); + Assert.Equal("42100", person.ZipCode); + }); + [ConditionalFact] public virtual void Swap_filtered_unique_index_values() { @@ -174,14 +207,19 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con modelBuilder.Entity().HasIndex(p => new { p.Name, p.Price }).IsUnique().HasFilter("Name IS NOT NULL"); - modelBuilder.Entity() - .Property(p => p.Country) - .HasColumnName("Country"); - - modelBuilder.Entity() - .OwnsOne(p => p.Address) - .Property(p => p.Country) - .HasColumnName("Country"); + modelBuilder.Entity(pb => + { + pb.Property(p => p.Country) + .HasColumnName("Country"); + pb.Property(p => p.ZipCode) + .HasColumnName("ZipCode"); + pb.OwnsOne(p => p.Address) + .Property(p => p.Country) + .HasColumnName("Country"); + pb.OwnsOne(p => p.Address) + .Property(p => p.ZipCode) + .HasColumnName("ZipCode"); + }); modelBuilder .Entity< diff --git a/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs b/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs index 265143cc7e6..e20982eaab0 100644 --- a/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs +++ b/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs @@ -421,22 +421,26 @@ public virtual void Detects_incompatible_primary_key_columns_with_shared_table() } [ConditionalFact] - public virtual void Passes_on_not_configured_shared_columns_with_shared_table() + public virtual void Passes_on_shared_columns_with_shared_table() { var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().HasOne().WithOne(b => b.A).HasForeignKey(a => a.Id).HasPrincipalKey(b => b.Id).IsRequired(); modelBuilder.Entity().Property(a => a.P0).HasColumnName(nameof(A.P0)); + modelBuilder.Entity().Property(a => a.P3).HasColumnName(nameof(A.P3)) + .HasConversion(e => (long?)e, e => (int?)e); modelBuilder.Entity().Property(a => a.P1).IsRequired(); modelBuilder.Entity().ToTable("Table"); modelBuilder.Entity().Property(b => b.P0).HasColumnName(nameof(A.P0)).HasColumnType("someInt"); + modelBuilder.Entity().Property(b => b.P3).HasColumnName(nameof(A.P3)) + .HasConversion(e => (long)e, e => (int?)e); modelBuilder.Entity().ToTable("Table"); Validate(modelBuilder); } [ConditionalFact] - public virtual void Throws_on_not_configured_shared_columns_with_shared_table_with_dependents() + public virtual void Throws_on_nullable_shared_columns_with_shared_table_with_dependents() { var modelBuilder = CreateConventionModelBuilder(); @@ -450,7 +454,7 @@ public virtual void Throws_on_not_configured_shared_columns_with_shared_table_wi } [ConditionalFact] - public virtual void Warns_on_not_configured_shared_columns_with_shared_table() + public virtual void Warns_on_no_required_columns_with_shared_table() { var modelBuilder = CreateConventionModelBuilder(); diff --git a/test/EFCore.Specification.Tests/TestModels/UpdatesModel/Address.cs b/test/EFCore.Specification.Tests/TestModels/UpdatesModel/Address.cs index 426a99c9ffb..72d972eb1ec 100644 --- a/test/EFCore.Specification.Tests/TestModels/UpdatesModel/Address.cs +++ b/test/EFCore.Specification.Tests/TestModels/UpdatesModel/Address.cs @@ -9,4 +9,5 @@ public class Address { public string City { get; set; } = null!; public Country Country { get; set; } + public int? ZipCode { get; set; } } diff --git a/test/EFCore.Specification.Tests/TestModels/UpdatesModel/Person.cs b/test/EFCore.Specification.Tests/TestModels/UpdatesModel/Person.cs index 7074270dbb9..6427850b068 100644 --- a/test/EFCore.Specification.Tests/TestModels/UpdatesModel/Person.cs +++ b/test/EFCore.Specification.Tests/TestModels/UpdatesModel/Person.cs @@ -22,6 +22,7 @@ public Person(string name, Person? parent) public string Name { get; set; } public int? ParentId { get; set; } public string? Country { get; set; } + public string? ZipCode { get; set; } public Person? Parent { get; set; } public Address? Address { get; set; } } diff --git a/test/EFCore.Specification.Tests/UpdatesTestBase.cs b/test/EFCore.Specification.Tests/UpdatesTestBase.cs index 90a74e1e10a..d5f764e176a 100644 --- a/test/EFCore.Specification.Tests/UpdatesTestBase.cs +++ b/test/EFCore.Specification.Tests/UpdatesTestBase.cs @@ -376,7 +376,11 @@ public virtual void Can_change_enums_with_conversion() => ExecuteWithStrategyInTransaction( context => { - var person = new Person("1", null) { Address = new Address { Country = Country.Eswatini, City = "Bulembu" }, Country = "Eswatini" }; + var person = new Person("1", null) + { + Address = new Address { Country = Country.Eswatini, City = "Bulembu" }, + Country = "Eswatini" + }; context.Add(person); @@ -385,8 +389,9 @@ public virtual void Can_change_enums_with_conversion() context => { var person = context.Set().Single(); - person.Address = new Address { Country = Country.Türkiye, City = "Konya" }; + person.Address = new Address { Country = Country.Türkiye, City = "Konya", ZipCode = 42100 }; person.Country = "Türkiye"; + person.ZipCode = "42100"; context.SaveChanges(); }, @@ -396,7 +401,9 @@ public virtual void Can_change_enums_with_conversion() Assert.Equal(Country.Türkiye, person.Address!.Country); Assert.Equal("Konya", person.Address.City); + Assert.Equal(42100, person.Address.ZipCode); Assert.Equal("Türkiye", person.Country); + Assert.Equal("42100", person.ZipCode); }); [ConditionalFact] @@ -644,15 +651,17 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con .HasForeignKey(e => e.DependentId) .HasPrincipalKey(e => e.PrincipalId); - modelBuilder.Entity() - .HasOne(p => p.Parent) - .WithMany() - .OnDelete(DeleteBehavior.Restrict); - - modelBuilder.Entity() - .OwnsOne(p => p.Address) - .Property(p => p.Country) - .HasConversion(); + modelBuilder.Entity(pb => + { + pb.HasOne(p => p.Parent) + .WithMany() + .OnDelete(DeleteBehavior.Restrict); + pb.OwnsOne(p => p.Address) + .Property(p => p.Country) + .HasConversion(); + pb.Property(p => p.ZipCode) + .HasConversion(v => v == null ? null : int.Parse(v), v => v == null ? null : v.ToString()!); + }); modelBuilder.Entity().HasMany(e => e.ProductCategories).WithOne(e => e.Category) .HasForeignKey(e => e.CategoryId); diff --git a/test/EFCore.SqlServer.FunctionalTests/Update/MismatchedKeyTypesSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Update/MismatchedKeyTypesSqlServerTest.cs index 6f34150ae67..6ca8415795f 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Update/MismatchedKeyTypesSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Update/MismatchedKeyTypesSqlServerTest.cs @@ -361,7 +361,7 @@ public virtual void Queries_work_but_SaveChanges_fails_when_keys_incompatible_in Assert.Equal( RelationalStrings.StoredKeyTypesNotConvertable( nameof(OptionalSingleBad.PrincipalId), "uniqueidentifier", "bigint", nameof(PrincipalBad.Id)), - Assert.Throws(() => context.SaveChanges()).InnerException!.Message); + Assert.Throws(() => context.SaveChanges()).InnerException!.InnerException!.Message); } protected class MismatchedKeyTypesContextNoFks : MismatchedKeyTypesContext diff --git a/test/EFCore.SqlServer.FunctionalTests/UpdatesSqlServerTestBase.cs b/test/EFCore.SqlServer.FunctionalTests/UpdatesSqlServerTestBase.cs index 2145d977486..dc16ecd77c6 100644 --- a/test/EFCore.SqlServer.FunctionalTests/UpdatesSqlServerTestBase.cs +++ b/test/EFCore.SqlServer.FunctionalTests/UpdatesSqlServerTestBase.cs @@ -29,61 +29,68 @@ public override void Can_add_and_remove_self_refs() @p0=NULL (Size = 4000) @p1='1' (Nullable = false) (Size = 4000) @p2=NULL (DbType = Int32) +@p3=NULL (DbType = Int32) SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; -INSERT INTO [Person] ([Country], [Name], [ParentId]) +INSERT INTO [Person] ([Country], [Name], [ParentId], [ZipCode]) OUTPUT INSERTED.[PersonId] -VALUES (@p0, @p1, @p2); +VALUES (@p0, @p1, @p2, @p3); """, - // -""" -@p3=NULL (Size = 4000) -@p4='2' (Nullable = false) (Size = 4000) -@p5='1' (Nullable = true) -@p6=NULL (Size = 4000) -@p7='3' (Nullable = false) (Size = 4000) -@p8='1' (Nullable = true) + // + """ +@p4=NULL (Size = 4000) +@p5='2' (Nullable = false) (Size = 4000) +@p6='1' (Nullable = true) +@p7=NULL (DbType = Int32) +@p8=NULL (Size = 4000) +@p9='3' (Nullable = false) (Size = 4000) +@p10='1' (Nullable = true) +@p11=NULL (DbType = Int32) SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; MERGE [Person] USING ( -VALUES (@p3, @p4, @p5, 0), -(@p6, @p7, @p8, 1)) AS i ([Country], [Name], [ParentId], _Position) ON 1=0 +VALUES (@p4, @p5, @p6, @p7, 0), +(@p8, @p9, @p10, @p11, 1)) AS i ([Country], [Name], [ParentId], [ZipCode], _Position) ON 1=0 WHEN NOT MATCHED THEN -INSERT ([Country], [Name], [ParentId]) -VALUES (i.[Country], i.[Name], i.[ParentId]) +INSERT ([Country], [Name], [ParentId], [ZipCode]) +VALUES (i.[Country], i.[Name], i.[ParentId], i.[ZipCode]) OUTPUT INSERTED.[PersonId], i._Position; """, - // -""" -@p9=NULL (Size = 4000) -@p10='4' (Nullable = false) (Size = 4000) -@p11='2' (Nullable = true) + // + """ @p12=NULL (Size = 4000) -@p13='5' (Nullable = false) (Size = 4000) +@p13='4' (Nullable = false) (Size = 4000) @p14='2' (Nullable = true) -@p15=NULL (Size = 4000) -@p16='6' (Nullable = false) (Size = 4000) -@p17='3' (Nullable = true) -@p18=NULL (Size = 4000) -@p19='7' (Nullable = false) (Size = 4000) -@p20='3' (Nullable = true) +@p15=NULL (DbType = Int32) +@p16=NULL (Size = 4000) +@p17='5' (Nullable = false) (Size = 4000) +@p18='2' (Nullable = true) +@p19=NULL (DbType = Int32) +@p20=NULL (Size = 4000) +@p21='6' (Nullable = false) (Size = 4000) +@p22='3' (Nullable = true) +@p23=NULL (DbType = Int32) +@p24=NULL (Size = 4000) +@p25='7' (Nullable = false) (Size = 4000) +@p26='3' (Nullable = true) +@p27=NULL (DbType = Int32) SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; MERGE [Person] USING ( -VALUES (@p9, @p10, @p11, 0), -(@p12, @p13, @p14, 1), -(@p15, @p16, @p17, 2), -(@p18, @p19, @p20, 3)) AS i ([Country], [Name], [ParentId], _Position) ON 1=0 +VALUES (@p12, @p13, @p14, @p15, 0), +(@p16, @p17, @p18, @p19, 1), +(@p20, @p21, @p22, @p23, 2), +(@p24, @p25, @p26, @p27, 3)) AS i ([Country], [Name], [ParentId], [ZipCode], _Position) ON 1=0 WHEN NOT MATCHED THEN -INSERT ([Country], [Name], [ParentId]) -VALUES (i.[Country], i.[Name], i.[ParentId]) +INSERT ([Country], [Name], [ParentId], [ZipCode]) +VALUES (i.[Country], i.[Name], i.[ParentId], i.[ZipCode]) OUTPUT INSERTED.[PersonId], i._Position; """, - // -""" + // + """ @p0='4' @p1='5' @p2='6' @@ -93,6 +100,7 @@ public override void Can_add_and_remove_self_refs() @p6=NULL (Size = 4000) @p7='1' (Nullable = false) (Size = 4000) @p8=NULL (DbType = Int32) +@p9=NULL (DbType = Int32) SET NOCOUNT ON; DELETE FROM [Person] @@ -113,62 +121,68 @@ OUTPUT 1 DELETE FROM [Person] OUTPUT 1 WHERE [PersonId] = @p5; -INSERT INTO [Person] ([Country], [Name], [ParentId]) +INSERT INTO [Person] ([Country], [Name], [ParentId], [ZipCode]) OUTPUT INSERTED.[PersonId] -VALUES (@p6, @p7, @p8); +VALUES (@p6, @p7, @p8, @p9); """, - // -""" -@p9='1' -@p10=NULL (Size = 4000) -@p11='2' (Nullable = false) (Size = 4000) -@p12='8' (Nullable = true) -@p13=NULL (Size = 4000) -@p14='3' (Nullable = false) (Size = 4000) -@p15='8' (Nullable = true) + // + """ +@p10='1' +@p11=NULL (Size = 4000) +@p12='2' (Nullable = false) (Size = 4000) +@p13='8' (Nullable = true) +@p14=NULL (DbType = Int32) +@p15=NULL (Size = 4000) +@p16='3' (Nullable = false) (Size = 4000) +@p17='8' (Nullable = true) +@p18=NULL (DbType = Int32) SET NOCOUNT ON; DELETE FROM [Person] OUTPUT 1 -WHERE [PersonId] = @p9; +WHERE [PersonId] = @p10; MERGE [Person] USING ( -VALUES (@p10, @p11, @p12, 0), -(@p13, @p14, @p15, 1)) AS i ([Country], [Name], [ParentId], _Position) ON 1=0 +VALUES (@p11, @p12, @p13, @p14, 0), +(@p15, @p16, @p17, @p18, 1)) AS i ([Country], [Name], [ParentId], [ZipCode], _Position) ON 1=0 WHEN NOT MATCHED THEN -INSERT ([Country], [Name], [ParentId]) -VALUES (i.[Country], i.[Name], i.[ParentId]) +INSERT ([Country], [Name], [ParentId], [ZipCode]) +VALUES (i.[Country], i.[Name], i.[ParentId], i.[ZipCode]) OUTPUT INSERTED.[PersonId], i._Position; """, - // -""" -@p16=NULL (Size = 4000) -@p17='4' (Nullable = false) (Size = 4000) -@p18='9' (Nullable = true) + // + """ @p19=NULL (Size = 4000) -@p20='5' (Nullable = false) (Size = 4000) +@p20='4' (Nullable = false) (Size = 4000) @p21='9' (Nullable = true) -@p22=NULL (Size = 4000) -@p23='6' (Nullable = false) (Size = 4000) -@p24='10' (Nullable = true) -@p25=NULL (Size = 4000) -@p26='7' (Nullable = false) (Size = 4000) -@p27='10' (Nullable = true) +@p22=NULL (DbType = Int32) +@p23=NULL (Size = 4000) +@p24='5' (Nullable = false) (Size = 4000) +@p25='9' (Nullable = true) +@p26=NULL (DbType = Int32) +@p27=NULL (Size = 4000) +@p28='6' (Nullable = false) (Size = 4000) +@p29='10' (Nullable = true) +@p30=NULL (DbType = Int32) +@p31=NULL (Size = 4000) +@p32='7' (Nullable = false) (Size = 4000) +@p33='10' (Nullable = true) +@p34=NULL (DbType = Int32) SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; MERGE [Person] USING ( -VALUES (@p16, @p17, @p18, 0), -(@p19, @p20, @p21, 1), -(@p22, @p23, @p24, 2), -(@p25, @p26, @p27, 3)) AS i ([Country], [Name], [ParentId], _Position) ON 1=0 +VALUES (@p19, @p20, @p21, @p22, 0), +(@p23, @p24, @p25, @p26, 1), +(@p27, @p28, @p29, @p30, 2), +(@p31, @p32, @p33, @p34, 3)) AS i ([Country], [Name], [ParentId], [ZipCode], _Position) ON 1=0 WHEN NOT MATCHED THEN -INSERT ([Country], [Name], [ParentId]) -VALUES (i.[Country], i.[Name], i.[ParentId]) +INSERT ([Country], [Name], [ParentId], [ZipCode]) +VALUES (i.[Country], i.[Name], i.[ParentId], i.[ZipCode]) OUTPUT INSERTED.[PersonId], i._Position; """, - // -""" -SELECT [p].[PersonId], [p].[Country], [p].[Name], [p].[ParentId], [p].[Address_City], [p].[Country], [p0].[PersonId], [p0].[Country], [p0].[Name], [p0].[ParentId], [p0].[Address_City], [p0].[Country], [p1].[PersonId], [p1].[Country], [p1].[Name], [p1].[ParentId], [p1].[Address_City], [p1].[Country], [p2].[PersonId], [p2].[Country], [p2].[Name], [p2].[ParentId], [p2].[Address_City], [p2].[Country] + // + """ +SELECT [p].[PersonId], [p].[Country], [p].[Name], [p].[ParentId], [p].[ZipCode], [p].[Address_City], [p].[Country], [p].[ZipCode], [p0].[PersonId], [p0].[Country], [p0].[Name], [p0].[ParentId], [p0].[ZipCode], [p0].[Address_City], [p0].[Country], [p0].[ZipCode], [p1].[PersonId], [p1].[Country], [p1].[Name], [p1].[ParentId], [p1].[ZipCode], [p1].[Address_City], [p1].[Country], [p1].[ZipCode], [p2].[PersonId], [p2].[Country], [p2].[Name], [p2].[ParentId], [p2].[ZipCode], [p2].[Address_City], [p2].[Country], [p2].[ZipCode] FROM [Person] AS [p] LEFT JOIN [Person] AS [p0] ON [p].[ParentId] = [p0].[PersonId] LEFT JOIN [Person] AS [p1] ON [p0].[ParentId] = [p1].[PersonId]