diff --git a/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs b/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs
index 43401bfa320..70e9454f8f2 100644
--- a/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs
+++ b/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs
@@ -334,24 +334,19 @@ protected virtual void ValidateSharedTableCompatibility(
continue;
}
- if (mappedType.FindPrimaryKey() != null
- && mappedType.FindForeignKeys(mappedType.FindPrimaryKey().Properties)
- .Any(
- fk => fk.PrincipalKey.IsPrimaryKey()
- && unvalidatedTypes.Contains(fk.PrincipalEntityType)))
+ var primaryKey = mappedType.FindPrimaryKey();
+ if (primaryKey != null
+ && (mappedType.FindForeignKeys(primaryKey.Properties)
+ .FirstOrDefault(fk => fk.PrincipalKey.IsPrimaryKey()
+ && unvalidatedTypes.Contains(fk.PrincipalEntityType)) is IForeignKey linkingFK))
{
if (mappedType.BaseType != null)
{
- var principalType = mappedType.FindForeignKeys(mappedType.FindPrimaryKey().Properties)
- .First(
- fk => fk.PrincipalKey.IsPrimaryKey()
- && unvalidatedTypes.Contains(fk.PrincipalEntityType))
- .PrincipalEntityType;
throw new InvalidOperationException(
RelationalStrings.IncompatibleTableDerivedRelationship(
storeObject.DisplayName(),
mappedType.DisplayName(),
- principalType.DisplayName()));
+ linkingFK.PrincipalEntityType.DisplayName()));
}
continue;
@@ -388,18 +383,21 @@ protected virtual void ValidateSharedTableCompatibility(
foreach (var nextEntityType in directlyConnectedTypes)
{
- var otherKey = nextEntityType.FindPrimaryKey();
- if (key?.GetName(storeObject) != otherKey?.GetName(storeObject))
+ if (key != null)
{
- throw new InvalidOperationException(
- RelationalStrings.IncompatibleTableKeyNameMismatch(
- storeObject.DisplayName(),
- entityType.DisplayName(),
- nextEntityType.DisplayName(),
- key?.GetName(storeObject),
- key?.Properties.Format(),
- otherKey?.GetName(storeObject),
- otherKey?.Properties.Format()));
+ var otherKey = nextEntityType.FindPrimaryKey();
+ if (key.GetName(storeObject) != otherKey.GetName(storeObject))
+ {
+ throw new InvalidOperationException(
+ RelationalStrings.IncompatibleTableKeyNameMismatch(
+ storeObject.DisplayName(),
+ entityType.DisplayName(),
+ nextEntityType.DisplayName(),
+ key.GetName(storeObject),
+ key.Properties.Format(),
+ otherKey.GetName(storeObject),
+ otherKey.Properties.Format()));
+ }
}
var nextComment = nextEntityType.GetComment();
@@ -599,8 +597,7 @@ protected virtual void ValidateSharedViewCompatibility(
private static bool IsIdentifyingPrincipal(IEntityType dependentEntityType, IEntityType principalEntityType)
=> dependentEntityType.FindForeignKeys(dependentEntityType.FindPrimaryKey().Properties)
- .Any(
- fk => fk.PrincipalKey.IsPrimaryKey()
+ .Any(fk => fk.PrincipalKey.IsPrimaryKey()
&& fk.PrincipalEntityType == principalEntityType);
///
diff --git a/src/EFCore/ChangeTrackingStrategy.cs b/src/EFCore/ChangeTrackingStrategy.cs
index 64adfc61582..4cfdf11155d 100644
--- a/src/EFCore/ChangeTrackingStrategy.cs
+++ b/src/EFCore/ChangeTrackingStrategy.cs
@@ -30,14 +30,12 @@ public enum ChangeTrackingStrategy
///
/// To use this strategy, the entity class must implement and
/// .
- /// Original values are recorded when the entity raises the event. Properties
- /// are
- /// marked as modified when the entity raises the event.
+ /// Original values are recorded when the entity raises the event.
+ /// Properties are marked as modified when the entity raises the event.
///
///
- /// Original values are only recorded when they are required to save changes to the entity. For example, properties that are configured
- /// as
- /// concurrency tokens.
+ /// Original values are only recorded when they are required to save changes to the entity. For example, properties that are
+ /// configured as concurrency tokens.
///
///
ChangingAndChangedNotifications,
@@ -46,8 +44,8 @@ public enum ChangeTrackingStrategy
///
/// To use this strategy, the entity class must implement and
/// .
- /// Original values are recorded when the entity raises the . Properties are
- /// marked as modified when the entity raises the event.
+ /// Original values are recorded when the entity raises the .
+ /// Properties are marked as modified when the entity raises the event.
///
///
/// Original values are recorded for all properties, regardless of whether they are required to save changes to the entity.
diff --git a/src/EFCore/Metadata/Conventions/ForeignKeyPropertyDiscoveryConvention.cs b/src/EFCore/Metadata/Conventions/ForeignKeyPropertyDiscoveryConvention.cs
index f3780c4ec27..7632f2e4195 100644
--- a/src/EFCore/Metadata/Conventions/ForeignKeyPropertyDiscoveryConvention.cs
+++ b/src/EFCore/Metadata/Conventions/ForeignKeyPropertyDiscoveryConvention.cs
@@ -38,21 +38,21 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Conventions
///
///
public class ForeignKeyPropertyDiscoveryConvention :
- IForeignKeyAddedConvention,
- INavigationAddedConvention,
- IPropertyAddedConvention,
IEntityTypeMemberIgnoredConvention,
- IPropertyNullabilityChangedConvention,
- IPropertyFieldChangedConvention,
+ IEntityTypePrimaryKeyChangedConvention,
+ IForeignKeyAddedConvention,
IForeignKeyPropertiesChangedConvention,
IForeignKeyPrincipalEndChangedConvention,
IForeignKeyUniquenessChangedConvention,
IForeignKeyRequirednessChangedConvention,
- ISkipNavigationForeignKeyChangedConvention,
- ISkipNavigationInverseChangedConvention,
IKeyAddedConvention,
IKeyRemovedConvention,
- IEntityTypePrimaryKeyChangedConvention,
+ INavigationAddedConvention,
+ ISkipNavigationForeignKeyChangedConvention,
+ ISkipNavigationInverseChangedConvention,
+ IPropertyAddedConvention,
+ IPropertyNullabilityChangedConvention,
+ IPropertyFieldChangedConvention,
IModelFinalizingConvention
{
///
diff --git a/src/EFCore/Metadata/Conventions/RelationshipDiscoveryConvention.cs b/src/EFCore/Metadata/Conventions/RelationshipDiscoveryConvention.cs
index 35c6fe73a71..6f217c3c55b 100644
--- a/src/EFCore/Metadata/Conventions/RelationshipDiscoveryConvention.cs
+++ b/src/EFCore/Metadata/Conventions/RelationshipDiscoveryConvention.cs
@@ -24,8 +24,8 @@ public class RelationshipDiscoveryConvention :
IEntityTypeAddedConvention,
IEntityTypeIgnoredConvention,
IEntityTypeBaseTypeChangedConvention,
- INavigationRemovedConvention,
IEntityTypeMemberIgnoredConvention,
+ INavigationRemovedConvention,
INavigationAddedConvention,
IForeignKeyOwnershipChangedConvention
{
diff --git a/test/EFCore.SqlServer.FunctionalTests/ConnectionSpecificationTest.cs b/test/EFCore.SqlServer.FunctionalTests/ConnectionSpecificationTest.cs
index 80074803b29..69f751ac251 100644
--- a/test/EFCore.SqlServer.FunctionalTests/ConnectionSpecificationTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/ConnectionSpecificationTest.cs
@@ -3,7 +3,9 @@
using System;
using System.Collections.Generic;
+using System.Data;
using System.Linq;
+using System.Threading.Tasks;
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.TestUtilities;
@@ -450,5 +452,90 @@ private class Customer
public string Fax { get; set; }
// ReSharper restore UnusedMember.Local
}
+
+ [ConditionalTheory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public async Task Can_use_an_existing_closed_connection_test(bool openConnection)
+ {
+ var serviceProvider = new ServiceCollection()
+ .AddEntityFrameworkSqlServer()
+ .BuildServiceProvider();
+
+ using var store = SqlServerTestStore.GetNorthwindStore();
+ store.CloseConnection();
+
+ var openCount = 0;
+ var closeCount = 0;
+ var disposeCount = 0;
+
+ using var connection = new SqlConnection(store.ConnectionString);
+ if (openConnection)
+ {
+ await connection.OpenAsync();
+ }
+
+ connection.StateChange += (_, a) =>
+ {
+ switch (a.CurrentState)
+ {
+ case ConnectionState.Open:
+ openCount++;
+ break;
+ case ConnectionState.Closed:
+ closeCount++;
+ break;
+ }
+ };
+ connection.Disposed += (_, __) => disposeCount++;
+
+ using (var context = new NorthwindContext(serviceProvider, connection))
+ {
+ Assert.Equal(91, await context.Customers.CountAsync());
+ }
+
+ if (openConnection)
+ {
+ Assert.Equal(ConnectionState.Open, connection.State);
+ Assert.Equal(0, openCount);
+ Assert.Equal(0, closeCount);
+ }
+ else
+ {
+ Assert.Equal(ConnectionState.Closed, connection.State);
+ Assert.Equal(1, openCount);
+ Assert.Equal(1, closeCount);
+ }
+
+ Assert.Equal(0, disposeCount);
+ }
+
+ private class NorthwindContext : DbContext
+ {
+ private readonly IServiceProvider _serviceProvider;
+ private readonly SqlConnection _connection;
+
+ public NorthwindContext(IServiceProvider serviceProvider, SqlConnection connection)
+ {
+ _serviceProvider = serviceProvider;
+ _connection = connection;
+ }
+
+ // ReSharper disable once UnusedAutoPropertyAccessor.Local
+ public DbSet Customers { get; set; }
+
+ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
+ => optionsBuilder
+ .UseSqlServer(_connection, b => b.ApplyConfiguration())
+ .UseInternalServiceProvider(_serviceProvider);
+
+ protected override void OnModelCreating(ModelBuilder modelBuilder)
+ => modelBuilder.Entity(
+ b =>
+ {
+ b.HasKey(c => c.CustomerID);
+ b.ToTable("Customers");
+ });
+ }
}
}
diff --git a/test/EFCore.SqlServer.FunctionalTests/ExistingConnectionTest.cs b/test/EFCore.SqlServer.FunctionalTests/ExistingConnectionTest.cs
deleted file mode 100644
index a15432e7416..00000000000
--- a/test/EFCore.SqlServer.FunctionalTests/ExistingConnectionTest.cs
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System;
-using System.Data;
-using System.Threading.Tasks;
-using Microsoft.Data.SqlClient;
-using Microsoft.EntityFrameworkCore.TestUtilities;
-using Microsoft.Extensions.DependencyInjection;
-using Xunit;
-
-// ReSharper disable InconsistentNaming
-namespace Microsoft.EntityFrameworkCore
-{
- public class ExistingConnectionTest
- {
- // See aspnet/Data#135
- [ConditionalFact]
- public Task Can_use_an_existing_closed_connection()
- {
- return Can_use_an_existing_closed_connection_test(openConnection: false);
- }
-
- [ConditionalFact]
- public Task Can_use_an_existing_open_connection()
- {
- return Can_use_an_existing_closed_connection_test(openConnection: true);
- }
-
- private static async Task Can_use_an_existing_closed_connection_test(bool openConnection)
- {
- var serviceProvider = new ServiceCollection()
- .AddEntityFrameworkSqlServer()
- .BuildServiceProvider();
-
- using var store = SqlServerTestStore.GetNorthwindStore();
- store.CloseConnection();
-
- var openCount = 0;
- var closeCount = 0;
- var disposeCount = 0;
-
- using var connection = new SqlConnection(store.ConnectionString);
- if (openConnection)
- {
- await connection.OpenAsync();
- }
-
- connection.StateChange += (_, a) =>
- {
- switch (a.CurrentState)
- {
- case ConnectionState.Open:
- openCount++;
- break;
- case ConnectionState.Closed:
- closeCount++;
- break;
- }
- };
- connection.Disposed += (_, __) => disposeCount++;
-
- using (var context = new NorthwindContext(serviceProvider, connection))
- {
- Assert.Equal(91, await context.Customers.CountAsync());
- }
-
- if (openConnection)
- {
- Assert.Equal(ConnectionState.Open, connection.State);
- Assert.Equal(0, openCount);
- Assert.Equal(0, closeCount);
- }
- else
- {
- Assert.Equal(ConnectionState.Closed, connection.State);
- Assert.Equal(1, openCount);
- Assert.Equal(1, closeCount);
- }
-
- Assert.Equal(0, disposeCount);
- }
-
- private class NorthwindContext : DbContext
- {
- private readonly IServiceProvider _serviceProvider;
- private readonly SqlConnection _connection;
-
- public NorthwindContext(IServiceProvider serviceProvider, SqlConnection connection)
- {
- _serviceProvider = serviceProvider;
- _connection = connection;
- }
-
- // ReSharper disable once UnusedAutoPropertyAccessor.Local
- public DbSet Customers { get; set; }
-
- protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
- => optionsBuilder
- .UseSqlServer(_connection, b => b.ApplyConfiguration())
- .UseInternalServiceProvider(_serviceProvider);
-
- protected override void OnModelCreating(ModelBuilder modelBuilder)
- => modelBuilder.Entity(
- b =>
- {
- b.HasKey(c => c.CustomerID);
- b.ToTable("Customers");
- });
- }
-
- // ReSharper disable once ClassNeverInstantiated.Local
- private class Customer
- {
- // ReSharper disable once UnusedAutoPropertyAccessor.Local
- public string CustomerID { get; set; }
-
- // ReSharper disable UnusedMember.Local
- public string CompanyName { get; set; }
-
- public string Fax { get; set; }
- }
- }
-}