From 659cf4876c03457928e70b463a39f14cfb5b755a Mon Sep 17 00:00:00 2001 From: David Korfkamp Date: Thu, 21 Nov 2024 13:38:34 +0100 Subject: [PATCH] Fix #2028 --- Dapper/SqlMapper.Async.cs | 3 +- .../Dapper.Tests/Providers/SQLServerTests.cs | 89 +++++++++++++++++++ 2 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 tests/Dapper.Tests/Providers/SQLServerTests.cs diff --git a/Dapper/SqlMapper.Async.cs b/Dapper/SqlMapper.Async.cs index 9408d5735..611a2ac1b 100644 --- a/Dapper/SqlMapper.Async.cs +++ b/Dapper/SqlMapper.Async.cs @@ -605,8 +605,7 @@ private static async Task ExecuteMultiImplAsync(IDbConnection cnn, CommandD while (pending.Count != 0) { var pair = pending.Dequeue(); - using (pair.Command) { /* dispose commands */ } - total += await pair.Task.ConfigureAwait(false); + using (pair.Command) { total += await pair.Task.ConfigureAwait(false); } } } finally diff --git a/tests/Dapper.Tests/Providers/SQLServerTests.cs b/tests/Dapper.Tests/Providers/SQLServerTests.cs new file mode 100644 index 000000000..e884acad2 --- /dev/null +++ b/tests/Dapper.Tests/Providers/SQLServerTests.cs @@ -0,0 +1,89 @@ +using System; +using System.Data.Common; +using System.Threading.Tasks; + +namespace Dapper.Tests +{ + /// + /// If Docker Desktop is installed, run the following command to start a container suitable for the tests. + /// + /// docker run -d -p 1433:1433 --name Dapper.Tests.SqlServer -e "ACCEPT_EULA=y" -e "MSSQL_SA_PASSWORD=Pass1234" mcr.microsoft.com/mssql/server:2022-latest + /// docker exec Dapper.Tests.SqlServer /opt/mssql-tools18/bin/sqlcmd -Q "CREATE DATABASE tests;" -C -U sa -P Pass1234 + /// docker exec Dapper.Tests.SqlServer /opt/mssql-tools18/bin/sqlcmd -Q "CREATE LOGIN test WITH PASSWORD='Pass1234';" -C -d tests -U sa -P Pass1234 + /// docker exec Dapper.Tests.SqlServer /opt/mssql-tools18/bin/sqlcmd -Q "CREATE USER test FOR LOGIN test" -C -d tests -U sa -P Pass1234 + /// docker exec Dapper.Tests.SqlServer /opt/mssql-tools18/bin/sqlcmd -Q "EXEC sp_addrolemember 'db_owner', 'test';" -C -d tests -U sa -P Pass1234 + /// + /// + public sealed class SqlServerProvider : DatabaseProvider + { + public override DbProviderFactory Factory => Microsoft.Data.SqlClient.SqlClientFactory.Instance; + + public override string GetConnectionString() => + GetConnectionString("SqlServerConnectionString", + "Server=localhost;Database=tests;User ID=test;Password=Pass1234;TrustServerCertificate=true;MultipleActiveResultSets=true"); + + public DbConnection GetSqlServerConnection(bool open = true) + { + string cs = GetConnectionString(); + var csb = Factory.CreateConnectionStringBuilder()!; + csb.ConnectionString = cs; + var conn = Factory.CreateConnection()!; + conn.ConnectionString = csb.ConnectionString; + if (open) conn.Open(); + return conn; + } + } + + public class SQLServerTests : TestBase + { + [FactSqlServer] + public async Task Issue2028_MARS_on_MicrosoftDataSqlClient() + { + using var conn = Provider.GetSqlServerConnection(); + + + try + { + conn.Execute("drop table Issue2028_Test"); + } + catch + { + /* don't care */ + } + + conn.Execute("create table Issue2028_Test (Id int not null);"); + + // insert multiple rows does not throw when using Microsoft.Data.SqlClient and having MultipleActiveRecordsets activated + var cmd = new CommandDefinition("insert into Issue2028_Test (Id) values (@id)", + new[] { new { id = 1 }, new { id = 2 } }, flags: CommandFlags.Pipelined); + await conn.ExecuteAsync(cmd); + } + + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] + public class FactSqlServerAttribute : FactAttribute + { + public override string? Skip + { + get { return unavailable ?? base.Skip; } + set { base.Skip = value; } + } + + private static readonly string? unavailable; + + static FactSqlServerAttribute() + { + try + { + using (DatabaseProvider.Instance.GetSqlServerConnection(true)) + { + /* just trying to see if it works */ + } + } + catch (Exception ex) + { + unavailable = $"SqlServer is unavailable: {ex.Message}"; + } + } + } + } +}