From c27368c1018242fe71bda42d63c9393baec42029 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Sun, 6 Sep 2020 19:54:21 +0200 Subject: [PATCH] Fix InvalidCastException in MySQLDatabaseCreator.HasTablesAsync The result of `ExecuteScalarAsync` on the *CreateHasTablesCommand* query is a `long`, so casting to `int` would throw an InvalidCastException: ``` System.InvalidCastException : Unable to cast object of type 'System.Int64' to type 'System.Int32'. at MySql.Data.EntityFrameworkCore.MySQLDatabaseCreator.b__24_0(MySQLServerConnection connection, CancellationToken ct) in /_/EFCore/src/Storage/Internal/MySQLDatabaseCreator.cs:line 268 at MySql.Data.EntityFrameworkCore.Storage.Internal.MySQLExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken) in /_/EFCore/src/Storage/Internal/MySQLExecutionStrategy.cs:line 88 at Microsoft.EntityFrameworkCore.Storage.RelationalDatabaseCreator.EnsureCreatedAsync(CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Storage.RelationalDatabaseCreator.EnsureCreatedAsync(CancellationToken cancellationToken) at MySql.EntityFrameworkCore.Basic.Tests.DataTests.EnsureCreatedAsyncTwice() in /_/EFCore/tests/MySql.EFCore.Basic.Tests/DataTests.cs:line 91 at NUnit.Framework.Internal.TaskAwaitAdapter.GenericAdapter`1.GetResult() ``` The code path leading to this exception is only executed [when the database alread exists][1], hence calling `EnsureCreatedAsync` twice in the new `EnsureCreatedAsyncTwice` test. Instead, we use `Convert.ToBoolean()` since the result of the *CreateHasTablesCommand* query is a boolean (returned as a long from `ExecuteScalarAsync`): `SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END FROM [...]` [1]: https://github.com/dotnet/efcore/blob/v3.1.7/src/EFCore.Relational/Storage/RelationalDatabaseCreator.cs#L263-L276 --- EFCore/src/Storage/Internal/MySQLDatabaseCreator.cs | 6 +++--- EFCore/tests/MySql.EFCore.Basic.Tests/DataTests.cs | 12 ++++++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/EFCore/src/Storage/Internal/MySQLDatabaseCreator.cs b/EFCore/src/Storage/Internal/MySQLDatabaseCreator.cs index 1cfdade5..a613cf40 100644 --- a/EFCore/src/Storage/Internal/MySQLDatabaseCreator.cs +++ b/EFCore/src/Storage/Internal/MySQLDatabaseCreator.cs @@ -265,7 +265,7 @@ public override bool HasTables() public override Task HasTablesAsync(CancellationToken cancellationToken = default(CancellationToken)) => Dependencies.ExecutionStrategyFactory.Create().ExecuteAsync( _connection, - async (connection, ct) => (int)await CreateHasTablesCommand() + async (connection, ct) => Convert.ToBoolean(await CreateHasTablesCommand() .ExecuteScalarAsync( new RelationalCommandParameterObject( connection, @@ -273,8 +273,8 @@ public override Task HasTablesAsync(CancellationToken cancellationToken = null, Dependencies.CurrentContext.Context, Dependencies.CommandLogger), - cancellationToken: ct) - != 0, cancellationToken); + cancellationToken: ct)) + , cancellationToken); private IRelationalCommand CreateHasTablesCommand() => _rawSqlCommandBuilder diff --git a/EFCore/tests/MySql.EFCore.Basic.Tests/DataTests.cs b/EFCore/tests/MySql.EFCore.Basic.Tests/DataTests.cs index d21aea37..a1024181 100644 --- a/EFCore/tests/MySql.EFCore.Basic.Tests/DataTests.cs +++ b/EFCore/tests/MySql.EFCore.Basic.Tests/DataTests.cs @@ -109,5 +109,17 @@ public void SakilaLiteTest() context.Database.EnsureCreated(); } } + + [Test] + // In version 8.0.21 and earlier, calling EnsureCreatedAsync twice would throw `System.InvalidCastException` + public async Task EnsureCreatedAsyncTwice() + { + using (var context = new MyContext()) + { + await context.Database.EnsureDeletedAsync(); + await context.Database.EnsureCreatedAsync(); + await context.Database.EnsureCreatedAsync(); + } + } } }