From 107de662cdae8fead41871cd69df350bef9ddb31 Mon Sep 17 00:00:00 2001 From: Andriy Svyryd Date: Fri, 4 Sep 2020 17:26:59 -0700 Subject: [PATCH] Merge ExistingConnectionTest with ConnectionSpecificationTest --- .../RelationalModelValidator.cs | 45 +++---- src/EFCore/ChangeTrackingStrategy.cs | 14 +- .../ForeignKeyPropertyDiscoveryConvention.cs | 16 +-- .../RelationshipDiscoveryConvention.cs | 2 +- .../ConnectionSpecificationTest.cs | 87 ++++++++++++ .../ExistingConnectionTest.cs | 124 ------------------ 6 files changed, 123 insertions(+), 165 deletions(-) delete mode 100644 test/EFCore.SqlServer.FunctionalTests/ExistingConnectionTest.cs 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; } - } - } -}