diff --git a/src/EFCore.MySql/Extensions/MySqlDbContextOptionsBuilderExtensions.cs b/src/EFCore.MySql/Extensions/MySqlDbContextOptionsBuilderExtensions.cs index 582345df0..34c4da90a 100644 --- a/src/EFCore.MySql/Extensions/MySqlDbContextOptionsBuilderExtensions.cs +++ b/src/EFCore.MySql/Extensions/MySqlDbContextOptionsBuilderExtensions.cs @@ -173,10 +173,10 @@ public static DbContextOptionsBuilder UseMySql( connection.ConnectionString = csb.ConnectionString; } - catch (MySqlException e) + catch (Exception e) { throw new InvalidOperationException( - @"The connection string used with Pomelo.EntityFrameworkCore.MySql must contain ""AllowUserVariables=true;UseAffectedRows=false"".", + @"The connection string of a connection used by Pomelo.EntityFrameworkCore.MySql must contain ""AllowUserVariables=true;UseAffectedRows=false"".", e); } } diff --git a/test/EFCore.MySql.FunctionalTests/ExistingConnectionMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/ExistingConnectionMySqlTest.cs new file mode 100644 index 000000000..6385011db --- /dev/null +++ b/test/EFCore.MySql.FunctionalTests/ExistingConnectionMySqlTest.cs @@ -0,0 +1,213 @@ +using System; +using System.Data; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using MySqlConnector; +using Pomelo.EntityFrameworkCore.MySql.FunctionalTests.TestUtilities; +using Pomelo.EntityFrameworkCore.MySql.Tests; +using Xunit; + +namespace Pomelo.EntityFrameworkCore.MySql.FunctionalTests; + +public class ExistingConnectionMySqlTest +{ + [ConditionalTheory] + [InlineData(false)] + [InlineData(true)] + private static async Task Can_use_an_existing_closed_connection(bool openConnection) + { + var serviceProvider = new ServiceCollection() + .AddEntityFrameworkMySql() + .BuildServiceProvider(); + + using (var store = (MySqlTestStore)MySqlNorthwindTestStoreFactory.Instance + .GetOrCreate(null) + .Initialize(null, (Func)null)) + { + store.CloseConnection(); + + var openCount = 0; + var closeCount = 0; + + using (var connection = new MySqlConnection(store.ConnectionString)) + { + if (openConnection) + { + await connection.OpenAsync(); + } + + connection.StateChange += (_, a) => + { + if (a.CurrentState == ConnectionState.Open) + { + openCount++; + } + else if (a.CurrentState == ConnectionState.Closed) + { + closeCount++; + } + }; + + var options = new DbContextOptionsBuilder() + .UseMySql(connection, AppConfig.ServerVersion) + .UseInternalServiceProvider(serviceProvider) + .Options; + + using (var context = new NorthwindContext(options)) + { + 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); + } + } + } + } + + [Fact] + private static async Task Closed_connection_missing_AllowUserVariables_true_in_original_connection_string() + { + var serviceProvider = new ServiceCollection() + .AddEntityFrameworkMySql() + .BuildServiceProvider(); + + await using (var store = (MySqlTestStore)MySqlNorthwindTestStoreFactory.Instance + .GetOrCreate(null) + .Initialize(null, (Func)null)) + { + store.CloseConnection(); + + var csb = new MySqlConnectionStringBuilder(store.ConnectionString) { AllowUserVariables = false }; + + await using (var connection = new MySqlConnection(csb.ConnectionString)) + { + var options = new DbContextOptionsBuilder() + .UseMySql(connection, AppConfig.ServerVersion) + .UseInternalServiceProvider(serviceProvider) + .Options; + + using var context = new NorthwindContext(options); + + Assert.Equal(91, await context.Customers.CountAsync()); + } + } + } + + [Fact] + private static async Task Opened_connection_missing_AllowUserVariables_true_in_original_connection_string_throws() + { + await using (var store = (MySqlTestStore)MySqlNorthwindTestStoreFactory.Instance + .GetOrCreate(null) + .Initialize(null, (Func)null)) + { + store.CloseConnection(); + + var csb = new MySqlConnectionStringBuilder(store.ConnectionString) { AllowUserVariables = false }; + + await using (var connection = new MySqlConnection(csb.ConnectionString)) + { + await connection.OpenAsync(); + + Assert.Equal( + Assert.Throws( + () => + { + new DbContextOptionsBuilder() + .UseMySql(connection, AppConfig.ServerVersion); + }).Message, + @"The connection string of a connection used by Pomelo.EntityFrameworkCore.MySql must contain ""AllowUserVariables=true;UseAffectedRows=false""."); + } + } + } + + [Fact] + private static async Task Closed_connection_missing_UseAffectedRows_false_in_original_connection_string() + { + var serviceProvider = new ServiceCollection() + .AddEntityFrameworkMySql() + .BuildServiceProvider(); + + await using (var store = (MySqlTestStore)MySqlNorthwindTestStoreFactory.Instance + .GetOrCreate(null) + .Initialize(null, (Func)null)) + { + store.CloseConnection(); + + var csb = new MySqlConnectionStringBuilder(store.ConnectionString) { UseAffectedRows = true }; + + await using (var connection = new MySqlConnection(csb.ConnectionString)) + { + var options = new DbContextOptionsBuilder() + .UseMySql(connection, AppConfig.ServerVersion) + .UseInternalServiceProvider(serviceProvider) + .Options; + + using var context = new NorthwindContext(options); + + Assert.Equal(91, await context.Customers.CountAsync()); + } + } + } + + [Fact] + private static async Task Opened_connection_missing_UseAffectedRows_false_in_original_connection_string_throws() + { + await using (var store = (MySqlTestStore)MySqlNorthwindTestStoreFactory.Instance + .GetOrCreate(null) + .Initialize(null, (Func)null)) + { + store.CloseConnection(); + + var csb = new MySqlConnectionStringBuilder(store.ConnectionString) { UseAffectedRows = true }; + + await using (var connection = new MySqlConnection(csb.ConnectionString)) + { + await connection.OpenAsync(); + + Assert.Equal( + Assert.Throws( + () => + { + new DbContextOptionsBuilder() + .UseMySql(connection, AppConfig.ServerVersion); + }).Message, + @"The connection string of a connection used by Pomelo.EntityFrameworkCore.MySql must contain ""AllowUserVariables=true;UseAffectedRows=false""."); + } + } + } + + private class NorthwindContext : DbContext + { + public NorthwindContext(DbContextOptions options) + : base(options) + { + } + + public DbSet Customers { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + => modelBuilder.Entity(b => + { + b.HasKey(c => c.CustomerId); + b.ToTable("Customers"); + }); + } + + private class Customer + { + public string CustomerId { get; set; } + public string CompanyName { get; set; } + public string Fax { get; set; } + } +} diff --git a/test/EFCore.MySql.FunctionalTests/TestUtilities/MySqlNorthwindTestStoreFactory.cs b/test/EFCore.MySql.FunctionalTests/TestUtilities/MySqlNorthwindTestStoreFactory.cs index 1c7ac9cf4..f2511c0fe 100644 --- a/test/EFCore.MySql.FunctionalTests/TestUtilities/MySqlNorthwindTestStoreFactory.cs +++ b/test/EFCore.MySql.FunctionalTests/TestUtilities/MySqlNorthwindTestStoreFactory.cs @@ -5,6 +5,8 @@ namespace Pomelo.EntityFrameworkCore.MySql.FunctionalTests.TestUtilities { public class MySqlNorthwindTestStoreFactory : MySqlTestStoreFactory { + public const string DefaultName = "Northwind"; + public static new MySqlNorthwindTestStoreFactory Instance => InstanceCi; public static MySqlNorthwindTestStoreFactory InstanceCi { get; } = new MySqlNorthwindTestStoreFactory(databaseCollation: AppConfig.ServerVersion.DefaultUtf8CiCollation); public static MySqlNorthwindTestStoreFactory InstanceCs { get; } = new MySqlNorthwindTestStoreFactory(databaseCollation: AppConfig.ServerVersion.DefaultUtf8CsCollation); @@ -16,6 +18,6 @@ protected MySqlNorthwindTestStoreFactory(bool noBackslashEscapes = false, string } public override TestStore GetOrCreate(string storeName) - => MySqlTestStore.GetOrCreate(storeName, "Northwind.sql", noBackslashEscapes: NoBackslashEscapes, databaseCollation: DatabaseCollation); + => MySqlTestStore.GetOrCreate(storeName ?? DefaultName, "Northwind.sql", noBackslashEscapes: NoBackslashEscapes, databaseCollation: DatabaseCollation); } }