diff --git a/Directory.Build.props b/Directory.Build.props
index b26432baa..7fd283bf2 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -12,7 +12,7 @@
Copyright 2021 © The Npgsql Development Team
Npgsql
- 6.0.0-preview7
+ 6.0.0-rc.1
true
PostgreSQL
https://github.com/npgsql/efcore.pg
diff --git a/Directory.Packages.props b/Directory.Packages.props
index f78076e1f..84469498a 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -1,7 +1,7 @@
- 6.0.0-preview.7.21378.4
- 6.0.0-preview.7.21377.19
+ 6.0.0-rc.1.21406.1
+ 6.0.0-rc.1.21401.3
6.0.0-preview6
diff --git a/src/EFCore.PG.NTS/Infrastructure/Internal/NpgsqlNetTopologySuiteOptionsExtension.cs b/src/EFCore.PG.NTS/Infrastructure/Internal/NpgsqlNetTopologySuiteOptionsExtension.cs
index 53bc8cf8f..245721c8b 100644
--- a/src/EFCore.PG.NTS/Infrastructure/Internal/NpgsqlNetTopologySuiteOptionsExtension.cs
+++ b/src/EFCore.PG.NTS/Infrastructure/Internal/NpgsqlNetTopologySuiteOptionsExtension.cs
@@ -72,7 +72,10 @@ public ExtensionInfo(IDbContextOptionsExtension extension)
public override bool IsDatabaseProvider => false;
- public override long GetServiceProviderHashCode() => Extension.IsGeographyDefault.GetHashCode();
+ public override int GetServiceProviderHashCode() => Extension.IsGeographyDefault.GetHashCode();
+
+ public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other)
+ => true;
public override void PopulateDebugInfo(IDictionary debugInfo)
{
diff --git a/src/EFCore.PG.NodaTime/Infrastructure/Internal/NpgsqlNodaTimeOptionsExtension.cs b/src/EFCore.PG.NodaTime/Infrastructure/Internal/NpgsqlNodaTimeOptionsExtension.cs
index 2816a0bfe..f85930647 100644
--- a/src/EFCore.PG.NodaTime/Infrastructure/Internal/NpgsqlNodaTimeOptionsExtension.cs
+++ b/src/EFCore.PG.NodaTime/Infrastructure/Internal/NpgsqlNodaTimeOptionsExtension.cs
@@ -48,7 +48,10 @@ public ExtensionInfo(IDbContextOptionsExtension extension)
public override bool IsDatabaseProvider => false;
- public override long GetServiceProviderHashCode() => 0;
+ public override int GetServiceProviderHashCode() => 0;
+
+ public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other)
+ => true;
public override void PopulateDebugInfo(IDictionary debugInfo)
=> debugInfo["Npgsql:" + nameof(NpgsqlNodaTimeDbContextOptionsBuilderExtensions.UseNodaTime)] = "1";
diff --git a/src/EFCore.PG/Extensions/MetadataExtensions/NpgsqlPropertyExtensions.cs b/src/EFCore.PG/Extensions/MetadataExtensions/NpgsqlPropertyExtensions.cs
index 6bd127d4f..5911cc388 100644
--- a/src/EFCore.PG/Extensions/MetadataExtensions/NpgsqlPropertyExtensions.cs
+++ b/src/EFCore.PG/Extensions/MetadataExtensions/NpgsqlPropertyExtensions.cs
@@ -406,12 +406,12 @@ private static void CheckValueGenerationStrategy(IReadOnlyProperty property, Npg
/// if compatible.
public static bool IsCompatibleWithValueGeneration(IReadOnlyProperty property)
{
- var type = property.ClrType;
+ var valueConverter = property.GetValueConverter()
+ ?? property.FindTypeMapping()?.Converter;
- return type.IsInteger()
- && (property.GetValueConverter()
- ?? property.FindTypeMapping()?.Converter)
- == null;
+ var type = (valueConverter?.ProviderClrType ?? property.ClrType).UnwrapNullableType();
+
+ return type.IsInteger();
}
private static bool IsCompatibleWithValueGeneration(
@@ -419,13 +419,13 @@ private static bool IsCompatibleWithValueGeneration(
in StoreObjectIdentifier storeObject,
ITypeMappingSource? typeMappingSource)
{
- var type = property.ClrType;
+ var valueConverter = property.GetValueConverter()
+ ?? (property.FindRelationalTypeMapping(storeObject)
+ ?? typeMappingSource?.FindMapping((IProperty)property))?.Converter;
+
+ var type = (valueConverter?.ProviderClrType ?? property.ClrType).UnwrapNullableType();
- return type.IsInteger()
- && (property.GetValueConverter()
- ?? (property.FindRelationalTypeMapping(storeObject)
- ?? typeMappingSource?.FindMapping((IProperty)property))?.Converter)
- == null;
+ return type.IsInteger();
}
#endregion Value generation
diff --git a/src/EFCore.PG/Infrastructure/Internal/NpgsqlOptionsExtension.cs b/src/EFCore.PG/Infrastructure/Internal/NpgsqlOptionsExtension.cs
index ec50268d7..744efa803 100644
--- a/src/EFCore.PG/Infrastructure/Internal/NpgsqlOptionsExtension.cs
+++ b/src/EFCore.PG/Infrastructure/Internal/NpgsqlOptionsExtension.cs
@@ -242,7 +242,7 @@ public override DbContextOptionsExtensionInfo Info
private sealed class ExtensionInfo : RelationalExtensionInfo
{
- private long? _serviceProviderHash;
+ private int? _serviceProviderHash;
private string? _logFragment;
public ExtensionInfo(IDbContextOptionsExtension extension)
@@ -324,26 +324,29 @@ public override string LogFragment
}
}
- public override long GetServiceProviderHashCode()
+ public override int GetServiceProviderHashCode()
{
- unchecked
+ if (_serviceProviderHash == null)
{
- if (_serviceProviderHash == null)
+ var hashCode = new HashCode();
+
+ foreach (var userRangeDefinition in Extension._userRangeDefinitions)
{
- _serviceProviderHash = Extension._userRangeDefinitions.Aggregate(
- base.GetServiceProviderHashCode(),
- (h, ud) => (h * 397) ^ ud.GetHashCode());
- _serviceProviderHash = (_serviceProviderHash * 397) ^ Extension.AdminDatabase?.GetHashCode() ?? 0L;
- _serviceProviderHash = (_serviceProviderHash * 397) ^ (Extension.PostgresVersion?.GetHashCode() ?? 0L);
- _serviceProviderHash = (_serviceProviderHash * 397) ^ Extension.UseRedshift.GetHashCode();
- _serviceProviderHash = (_serviceProviderHash * 397) ^ (Extension.ProvideClientCertificatesCallback?.GetHashCode() ?? 0L);
- _serviceProviderHash = (_serviceProviderHash * 397) ^ (Extension.RemoteCertificateValidationCallback?.GetHashCode() ?? 0L);
- _serviceProviderHash = (_serviceProviderHash * 397) ^ (Extension.ProvidePasswordCallback?.GetHashCode() ?? 0L);
- _serviceProviderHash = (_serviceProviderHash * 397) ^ Extension.ReverseNullOrdering.GetHashCode();
+ hashCode.Add(userRangeDefinition);
}
- return _serviceProviderHash.Value;
+ hashCode.Add(Extension.AdminDatabase);
+ hashCode.Add(Extension.PostgresVersion);
+ hashCode.Add(Extension.UseRedshift);
+ hashCode.Add(Extension.ProvideClientCertificatesCallback);
+ hashCode.Add(Extension.RemoteCertificateValidationCallback);
+ hashCode.Add(Extension.ProvidePasswordCallback);
+ hashCode.Add(Extension.ReverseNullOrdering);
+
+ _serviceProviderHash = hashCode.ToHashCode();
}
+
+ return _serviceProviderHash.Value;
}
///
diff --git a/src/EFCore.PG/Update/Internal/NpgsqlModificationCommandBatch.cs b/src/EFCore.PG/Update/Internal/NpgsqlModificationCommandBatch.cs
index 9fe7eb754..8be9a60d6 100644
--- a/src/EFCore.PG/Update/Internal/NpgsqlModificationCommandBatch.cs
+++ b/src/EFCore.PG/Update/Internal/NpgsqlModificationCommandBatch.cs
@@ -52,12 +52,12 @@ public NpgsqlModificationCommandBatch(
protected override int GetParameterCount() => _parameterCount;
- protected override bool CanAddCommand(ModificationCommand modificationCommand)
+ protected override bool CanAddCommand(IReadOnlyModificationCommand modificationCommand)
{
if (ModificationCommands.Count >= _maxBatchSize)
return false;
- var newParamCount = (long)_parameterCount + (long)modificationCommand.ColumnModifications.Count;
+ var newParamCount = (long)_parameterCount + modificationCommand.ColumnModifications.Count;
if (newParamCount > int.MaxValue)
return false;
diff --git a/src/EFCore.PG/Update/Internal/NpgsqlUpdateSqlGenerator.cs b/src/EFCore.PG/Update/Internal/NpgsqlUpdateSqlGenerator.cs
index 9c6af05a4..821b5dd80 100644
--- a/src/EFCore.PG/Update/Internal/NpgsqlUpdateSqlGenerator.cs
+++ b/src/EFCore.PG/Update/Internal/NpgsqlUpdateSqlGenerator.cs
@@ -16,13 +16,13 @@ public NpgsqlUpdateSqlGenerator(UpdateSqlGeneratorDependencies dependencies)
public override ResultSetMapping AppendInsertOperation(
StringBuilder commandStringBuilder,
- ModificationCommand command,
+ IReadOnlyModificationCommand command,
int commandPosition)
=> AppendInsertOperation(commandStringBuilder, command, commandPosition, false);
public virtual ResultSetMapping AppendInsertOperation(
StringBuilder commandStringBuilder,
- ModificationCommand command,
+ IReadOnlyModificationCommand command,
int commandPosition,
bool overridingSystemValue)
{
@@ -51,7 +51,7 @@ public virtual ResultSetMapping AppendInsertOperation(
public override ResultSetMapping AppendUpdateOperation(
StringBuilder commandStringBuilder,
- ModificationCommand command,
+ IReadOnlyModificationCommand command,
int commandPosition)
{
Check.NotNull(commandStringBuilder, nameof(commandStringBuilder));
@@ -78,7 +78,7 @@ public override ResultSetMapping AppendUpdateOperation(
// ReSharper disable once ParameterTypeCanBeEnumerable.Local
private void AppendReturningClause(
StringBuilder commandStringBuilder,
- IReadOnlyList operations)
+ IReadOnlyList operations)
{
commandStringBuilder
.AppendLine()
@@ -95,19 +95,16 @@ public override void AppendNextSequenceValueOperation(StringBuilder commandStrin
public override void AppendBatchHeader(StringBuilder commandStringBuilder)
{
- Check.NotNull(commandStringBuilder, nameof(commandStringBuilder));
-
- // TODO: Npgsql
}
- protected override void AppendIdentityWhereCondition(StringBuilder commandStringBuilder, ColumnModification columnModification)
+ protected override void AppendIdentityWhereCondition(StringBuilder commandStringBuilder, IColumnModification columnModification)
{
- throw new NotImplementedException();
+ throw new NotSupportedException();
}
protected override void AppendRowsAffectedWhereCondition(StringBuilder commandStringBuilder, int expectedRowsAffected)
{
- throw new NotImplementedException();
+ throw new NotSupportedException();
}
public enum ResultsGrouping
diff --git a/test/EFCore.PG.FunctionalTests/Migrations/MigrationsNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Migrations/MigrationsNpgsqlTest.cs
index 5b0d228f1..fb03dcec5 100644
--- a/test/EFCore.PG.FunctionalTests/Migrations/MigrationsNpgsqlTest.cs
+++ b/test/EFCore.PG.FunctionalTests/Migrations/MigrationsNpgsqlTest.cs
@@ -1491,6 +1491,35 @@ public override async Task Drop_column_primary_key()
@"ALTER TABLE ""People"" DROP COLUMN ""Id"";");
}
+ [ConditionalFact]
+ public override async Task Drop_column_computed_and_non_computed_with_dependency()
+ {
+ if (TestEnvironment.PostgresVersion.IsUnder(12))
+ {
+ return;
+ }
+
+ await Test(
+ builder => builder.Entity("People").Property("Id"),
+ builder => builder.Entity(
+ "People", e =>
+ {
+ e.Property("X");
+ e.Property("Y").HasComputedColumnSql($"{DelimitIdentifier("X")} + 1", stored: true);
+ }),
+ builder => { },
+ model =>
+ {
+ var table = Assert.Single(model.Tables);
+ Assert.Equal("Id", Assert.Single(table.Columns).Name);
+ });
+
+ AssertSql(
+ @"ALTER TABLE ""People"" DROP COLUMN ""Y"";",
+ //
+ @"ALTER TABLE ""People"" DROP COLUMN ""X"";");
+ }
+
[Fact]
public virtual async Task Drop_column_system()
{
diff --git a/test/EFCore.PG.FunctionalTests/NpgsqlValueGenerationScenariosTest.cs b/test/EFCore.PG.FunctionalTests/NpgsqlValueGenerationScenariosTest.cs
index 98f2525b9..6feccda34 100644
--- a/test/EFCore.PG.FunctionalTests/NpgsqlValueGenerationScenariosTest.cs
+++ b/test/EFCore.PG.FunctionalTests/NpgsqlValueGenerationScenariosTest.cs
@@ -3,6 +3,8 @@
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.TestUtilities;
+using Microsoft.EntityFrameworkCore.ValueGeneration.Internal;
using Npgsql.EntityFrameworkCore.PostgreSQL.TestUtilities;
using Xunit;
@@ -150,6 +152,12 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
.HasDefaultValue();
}
+ public class BlogWithStringKey
+ {
+ public string Id { get; set; }
+ public string Name { get; set; }
+ }
+
[Fact]
public void Insert_with_key_default_value_from_sequence()
{
@@ -194,6 +202,105 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
}
}
+ [ConditionalFact]
+ public void Insert_uint_to_Identity_column_using_value_converter()
+ {
+ using var testStore = NpgsqlTestStore.CreateInitialized(DatabaseName);
+ using (var context = new BlogContextUIntToIdentityUsingValueConverter(testStore.Name))
+ {
+ context.Database.EnsureCreatedResiliently();
+
+ context.AddRange(
+ new BlogWithUIntKey { Name = "One Unicorn" }, new BlogWithUIntKey { Name = "Two Unicorns" });
+
+ context.SaveChanges();
+ }
+
+ using (var context = new BlogContextUIntToIdentityUsingValueConverter(testStore.Name))
+ {
+ var blogs = context.UnsignedBlogs.OrderBy(e => e.Id).ToList();
+
+ Assert.Equal((uint)1, blogs[0].Id);
+ Assert.Equal((uint)2, blogs[1].Id);
+ }
+ }
+
+ public class BlogContextUIntToIdentityUsingValueConverter : ContextBase
+ {
+ public BlogContextUIntToIdentityUsingValueConverter(string databaseName)
+ : base(databaseName)
+ {
+ }
+
+ public DbSet UnsignedBlogs { get; set; }
+
+ protected override void OnModelCreating(ModelBuilder modelBuilder)
+ {
+ base.OnModelCreating(modelBuilder);
+
+ modelBuilder
+ .Entity()
+ .Property(e => e.Id)
+ .HasConversion();
+ }
+ }
+
+ public class BlogWithUIntKey
+ {
+ public uint Id { get; set; }
+ public string Name { get; set; }
+ }
+
+ [ConditionalFact]
+ public void Insert_string_to_Identity_column_using_value_converter()
+ {
+ using var testStore = NpgsqlTestStore.CreateInitialized(DatabaseName);
+ using (var context = new BlogContextStringToIdentityUsingValueConverter(testStore.Name))
+ {
+ context.Database.EnsureCreatedResiliently();
+
+ context.AddRange(
+ new BlogWithStringKey { Name = "One Unicorn" }, new BlogWithStringKey { Name = "Two Unicorns" });
+
+ context.SaveChanges();
+ }
+
+ using (var context = new BlogContextStringToIdentityUsingValueConverter(testStore.Name))
+ {
+ var blogs = context.StringyBlogs.OrderBy(e => e.Id).ToList();
+
+ Assert.Equal("1", blogs[0].Id);
+ Assert.Equal("2", blogs[1].Id);
+ }
+ }
+
+ public class BlogContextStringToIdentityUsingValueConverter : ContextBase
+ {
+ public BlogContextStringToIdentityUsingValueConverter(string databaseName)
+ : base(databaseName)
+ {
+ }
+
+ public DbSet StringyBlogs { get; set; }
+
+ protected override void OnModelCreating(ModelBuilder modelBuilder)
+ {
+ base.OnModelCreating(modelBuilder);
+
+ Guid guid;
+ modelBuilder
+ .Entity()
+ .Property(e => e.Id)
+ .HasValueGenerator()
+ .HasConversion(
+ v => Guid.TryParse(v, out guid)
+ ? default
+ : int.Parse(v),
+ v => v.ToString())
+ .ValueGeneratedOnAdd();
+ }
+ }
+
[Fact]
public void Insert_with_explicit_non_default_keys()
{
diff --git a/test/EFCore.PG.Tests/TestUtilities/FakeRelationalCommandDiagnosticsLogger.cs b/test/EFCore.PG.Tests/TestUtilities/FakeRelationalCommandDiagnosticsLogger.cs
index dd382c323..9cd3fdf24 100644
--- a/test/EFCore.PG.Tests/TestUtilities/FakeRelationalCommandDiagnosticsLogger.cs
+++ b/test/EFCore.PG.Tests/TestUtilities/FakeRelationalCommandDiagnosticsLogger.cs
@@ -19,7 +19,8 @@ public InterceptionResult CommandCreating(
DbContext? context,
Guid commandId,
Guid connectionId,
- DateTimeOffset startTime)
+ DateTimeOffset startTime,
+ CommandSource commandSource)
=> default;
public DbCommand CommandCreated(
@@ -30,6 +31,7 @@ public DbCommand CommandCreated(
Guid commandId,
Guid connectionId,
DateTimeOffset startTime,
+ CommandSource commandSource,
TimeSpan duration)
=> command;
@@ -39,7 +41,8 @@ public InterceptionResult CommandReaderExecuting(
DbContext? context,
Guid commandId,
Guid connectionId,
- DateTimeOffset startTime)
+ DateTimeOffset startTime,
+ CommandSource commandSource)
=> default;
public InterceptionResult