Skip to content

Commit 4c2a5c6

Browse files
committed
Switch to Microsoft.Data.SqlClient in http/worker host and Dibix.Testing [Pt. II]
1 parent e233b73 commit 4c2a5c6

21 files changed

Lines changed: 258 additions & 178 deletions
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
#nullable enable
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Data;
5+
using System.Data.Common;
6+
using System.Linq;
7+
using Microsoft.Data.SqlClient;
8+
using Microsoft.Data.SqlClient.Server;
9+
10+
namespace Dibix
11+
{
12+
public sealed class MicrosoftSqlClientAdapter : SqlClientAdapter
13+
{
14+
private readonly SqlConnection? _sqlConnection;
15+
16+
public override bool IsSqlClient => _sqlConnection != null;
17+
18+
public MicrosoftSqlClientAdapter(DbConnection connection)
19+
{
20+
if (connection is not SqlConnection sqlConnection)
21+
return;
22+
23+
_sqlConnection = sqlConnection;
24+
sqlConnection.InfoMessage += OnInfoMessage;
25+
}
26+
27+
public override void DetachInfoMessageHandler()
28+
{
29+
if (_sqlConnection != null)
30+
_sqlConnection.InfoMessage -= OnInfoMessage;
31+
}
32+
33+
public override int? TryGetSqlExceptionNumber(Exception exception) => exception is SqlException sqlException ? sqlException.Number : null;
34+
35+
protected override object GetStructuredTypeParameterValue(StructuredType type)
36+
{
37+
SqlDataRecord[] records = MapRecords(type).ToArray();
38+
return records;
39+
}
40+
41+
protected override void SetProviderSpecificParameterProperties(IDbDataParameter parameter, SqlDbType sqlDbType, string typeName)
42+
{
43+
if (parameter is not SqlParameter sqlParam)
44+
return;
45+
46+
sqlParam.SqlDbType = sqlDbType;
47+
sqlParam.TypeName = typeName;
48+
}
49+
50+
private static IEnumerable<SqlDataRecord> MapRecords(StructuredType type)
51+
{
52+
SqlMetaData[] metadata = MapMetadata(type).ToArray();
53+
54+
foreach (Microsoft.SqlServer.Server.SqlDataRecord oldRecord in type.GetRecords())
55+
{
56+
SqlDataRecord newRecord = new SqlDataRecord(metadata);
57+
for (int i = 0; i < oldRecord.FieldCount; i++)
58+
newRecord.SetValue(i, oldRecord.GetValue(i));
59+
60+
yield return newRecord;
61+
}
62+
}
63+
64+
private void OnInfoMessage(object sender, SqlInfoMessageEventArgs e) => OnInfoMessage(e.Message);
65+
66+
private static IEnumerable<SqlMetaData> MapMetadata(StructuredType type)
67+
{
68+
foreach (Microsoft.SqlServer.Server.SqlMetaData oldMetadata in type.GetMetadata())
69+
{
70+
yield return new SqlMetaData
71+
(
72+
name: oldMetadata.Name
73+
, dbType: oldMetadata.SqlDbType
74+
, maxLength: oldMetadata.MaxLength
75+
, precision: oldMetadata.Precision
76+
, scale: oldMetadata.Scale
77+
, locale: default
78+
, compareOptions: default
79+
, userDefinedType: default
80+
);
81+
}
82+
}
83+
}
84+
}
85+
#nullable restore

shared/Data/MicrosoftSqlDataRecordAdapter.cs

Lines changed: 0 additions & 56 deletions
This file was deleted.

shared/Hosting/Data/ScopedDatabaseAccessorFactory.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ private sealed class LoggingDapperDatabaseAccessor : DapperDatabaseAccessor
2525
{
2626
private readonly ILogger _logger;
2727

28-
public LoggingDapperDatabaseAccessor(DbConnection connection, ILogger logger) : base(connection, sqlDataRecordAdapter: new MicrosoftSqlDataRecordAdapter())
28+
public LoggingDapperDatabaseAccessor(DbConnection connection, ILogger logger) : base(connection, sqlClientAdapter: new MicrosoftSqlClientAdapter(connection))
2929
{
3030
_logger = logger;
3131
}

src/Dibix.Dapper/DapperDatabaseAccessor.cs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,14 @@ public class DapperDatabaseAccessor : DatabaseAccessor, IDatabaseAccessor, IDisp
1414
private readonly IDbTransaction _defaultTransaction;
1515
private readonly int? _defaultCommandTimeout;
1616
private readonly Action _onDispose;
17-
private readonly SqlDataRecordAdapter _sqlDataRecordAdapter;
1817
#endregion
1918

2019
#region Constructor
21-
public DapperDatabaseAccessor(DbConnection connection, IDbTransaction defaultTransaction = null, int? defaultCommandTimeout = null, Action onDispose = null, SqlDataRecordAdapter sqlDataRecordAdapter = null) : base(connection)
20+
public DapperDatabaseAccessor(DbConnection connection, IDbTransaction defaultTransaction = null, int? defaultCommandTimeout = null, Action onDispose = null, SqlClientAdapter sqlClientAdapter = null) : base(connection, sqlClientAdapter)
2221
{
2322
_defaultTransaction = defaultTransaction;
2423
_defaultCommandTimeout = defaultCommandTimeout;
2524
_onDispose = onDispose;
26-
_sqlDataRecordAdapter = sqlDataRecordAdapter ?? new SystemSqlClientDataRecordAdapter();
2725
ConfigureDapper();
2826
}
2927
#endregion
@@ -76,13 +74,13 @@ protected override Task<IEnumerable<TReturn>> QueryManyAsync<TReturn>(string com
7674
protected override IMultipleResultReader QueryMultiple(string commandText, CommandType commandType, ParametersVisitor parameters)
7775
{
7876
SqlMapper.GridReader reader = base.Connection.QueryMultiple(commandText, CollectParameters(parameters), _defaultTransaction, commandTimeout: _defaultCommandTimeout, commandType: commandType);
79-
return new DapperGridResultReader(reader, commandText, commandType, parameters);
77+
return new DapperGridResultReader(reader, commandText, commandType, parameters, SqlClientAdapter);
8078
}
8179

8280
protected override async Task<IMultipleResultReader> QueryMultipleAsync(string commandText, CommandType commandType, ParametersVisitor parameters, CancellationToken cancellationToken)
8381
{
8482
SqlMapper.GridReader reader = await base.Connection.QueryMultipleAsync(new CommandDefinition(commandText, CollectParameters(parameters), _defaultTransaction, _defaultCommandTimeout, commandType, cancellationToken: cancellationToken)).ConfigureAwait(false);
85-
return new DapperGridResultReader(reader, commandText, commandType, parameters);
83+
return new DapperGridResultReader(reader, commandText, commandType, parameters, SqlClientAdapter);
8684
}
8785

8886
protected override void DisposeConnection()
@@ -115,7 +113,7 @@ protected object CollectParameters(ParametersVisitor parametersVisitor)
115113
private object NormalizeParameterValue(object value)
116114
{
117115
if (value is StructuredType udt)
118-
return new DapperStructuredTypeParameter(udt, _sqlDataRecordAdapter);
116+
return new DapperStructuredTypeParameter(udt, SqlClientAdapter);
119117

120118
return value;
121119
}

src/Dibix.Dapper/DapperGridResultReader.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Data;
4-
using System.Data.SqlClient;
54
using System.Threading.Tasks;
65
using Dapper;
76

@@ -18,7 +17,7 @@ internal sealed class DapperGridResultReader : MultipleResultReader, IMultipleRe
1817
#endregion
1918

2019
#region Constructor
21-
public DapperGridResultReader(SqlMapper.GridReader reader, string commandText, CommandType commandType, ParametersVisitor parameters) : base(commandText, commandType, parameters, isSqlClient: reader.Command is SqlCommand)
20+
public DapperGridResultReader(SqlMapper.GridReader reader, string commandText, CommandType commandType, ParametersVisitor parameters, SqlClientAdapter sqlClientAdapter) : base(commandText, commandType, parameters, sqlClientAdapter)
2221
{
2322
_reader = reader;
2423
}

src/Dibix.Dapper/DapperStructuredTypeParameter.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ internal sealed class DapperStructuredTypeParameter : SqlMapper.ICustomQueryPara
77
{
88
#region Fields
99
private readonly StructuredType _udt;
10-
private readonly SqlDataRecordAdapter _sqlDataRecordAdapter;
10+
private readonly SqlClientAdapter _sqlClientAdapter;
1111
#endregion
1212

1313
#region Constructor
14-
public DapperStructuredTypeParameter(StructuredType udt, SqlDataRecordAdapter sqlDataRecordAdapter)
14+
public DapperStructuredTypeParameter(StructuredType udt, SqlClientAdapter sqlClientAdapter)
1515
{
1616
_udt = udt;
17-
_sqlDataRecordAdapter = sqlDataRecordAdapter;
17+
_sqlClientAdapter = sqlClientAdapter;
1818
}
1919
#endregion
2020

@@ -31,7 +31,7 @@ private IDbDataParameter ToSqlParameter(IDbCommand command, string parameterName
3131
{
3232
IDbDataParameter param = command.CreateParameter();
3333
param.ParameterName = parameterName;
34-
_sqlDataRecordAdapter.MapStructuredTypeToParameter(param, _udt);
34+
_sqlClientAdapter.MapStructuredTypeToParameter(param, _udt);
3535

3636
return param;
3737
}

src/Dibix.Http.Host/Dibix.Http.Host.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
</PropertyGroup>
88

99
<ItemGroup>
10-
<Compile Include="..\..\shared\Data\MicrosoftSqlDataRecordAdapter.cs" Link="Data\%(Filename)%(Extension)" />
10+
<Compile Include="..\..\shared\Data\MicrosoftSqlClientAdapter.cs" Link="Data\%(Filename)%(Extension)" />
1111
<Compile Include="..\..\shared\Diagnostics\Guard.cs" Link="Diagnostics\%(Filename)%(Extension)" />
1212
<Compile Include="..\..\shared\Extensions\BindConfigurationExtensions.cs" Link="Extensions\%(Filename)%(Extension)" />
1313
<Compile Include="..\..\shared\Extensions\EnumerableExtensions.cs" Link="Extensions\%(Filename)%(Extension)" />

src/Dibix.Http.Server/Runtime/SqlHttpStatusCodeParser.cs

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using System;
22
using System.Collections.Generic;
3-
using System.Data.SqlClient;
43
using System.Net;
54
using System.Text.RegularExpressions;
65

@@ -12,19 +11,15 @@ public static bool TryParse(DatabaseAccessException databaseAccessException, out
1211
{
1312
return TryParse(databaseAccessException, action: null, arguments: new Dictionary<string, object>(), out httpException);
1413
}
15-
internal static bool TryParse(DatabaseAccessException databaseAccessException, HttpActionDefinition action, IDictionary<string, object> arguments, out HttpRequestExecutionException httpException)
14+
internal static bool TryParse(DatabaseAccessException exception, HttpActionDefinition action, IDictionary<string, object> arguments, out HttpRequestExecutionException httpException)
1615
{
17-
return TryParse(databaseAccessException, databaseAccessException.InnerException as SqlException, action, arguments, out httpException);
18-
}
19-
private static bool TryParse(DatabaseAccessException originalException, SqlException rootException, HttpActionDefinition action, IDictionary<string, object> arguments, out HttpRequestExecutionException httpException)
20-
{
21-
if (rootException != null && HttpErrorResponseUtility.TryParseErrorResponse(rootException.Number, out int statusCode, out int errorCode, out bool isClientError))
16+
if (exception.SqlErrorNumber != null && exception.InnerException != null && HttpErrorResponseUtility.TryParseErrorResponse(exception.SqlErrorNumber.Value, out int statusCode, out int errorCode, out bool isClientError))
2217
{
23-
httpException = new HttpRequestExecutionException((HttpStatusCode)statusCode, errorCode, rootException.Message, isClientError, originalException);
18+
httpException = new HttpRequestExecutionException((HttpStatusCode)statusCode, errorCode, exception.InnerException.Message, isClientError, exception);
2419
return true;
2520
}
2621

27-
if (HttpStatusCodeDetectionMap.TryGetStatusCode(originalException.AdditionalErrorCode, out HttpErrorResponse defaultResponse))
22+
if (HttpStatusCodeDetectionMap.TryGetStatusCode(exception.AdditionalErrorCode, out HttpErrorResponse defaultResponse))
2823
{
2924
HttpErrorResponse error = defaultResponse;
3025
if (action != null && action.StatusCodeDetectionResponses.TryGetValue(error.StatusCode, out HttpErrorResponse userResponse))
@@ -37,7 +32,7 @@ private static bool TryParse(DatabaseAccessException originalException, SqlExcep
3732
string parameterName = x.Groups["ParameterName"].Value;
3833
return caseInsensitiveArguments.TryGetValue(parameterName, out object value) ? value?.ToString() : x.Value;
3934
});
40-
httpException = new HttpRequestExecutionException((HttpStatusCode)error.StatusCode, error.ErrorCode, formattedErrorMessage, isClientError, originalException);
35+
httpException = new HttpRequestExecutionException((HttpStatusCode)error.StatusCode, error.ErrorCode, formattedErrorMessage, isClientError, exception);
4136
return true;
4237
}
4338

src/Dibix.Testing/Data/DatabaseTestUtility.cs

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
using System;
22
using System.Data;
33
using System.Data.Common;
4-
using System.Linq;
5-
using System.Reflection;
64
using System.Threading;
75
using System.Threading.Tasks;
86
using Dapper;
9-
using Microsoft.Data.SqlClient;
107

118
namespace Dibix.Testing.Data
129
{
@@ -37,35 +34,38 @@ private sealed class DapperDatabaseAccessorFactory : IDisposableDatabaseAccessor
3734
{
3835
private readonly RaiseErrorWithNoWaitBehavior _raiseErrorWithNoWaitBehavior;
3936
private readonly int? _defaultCommandTimeout;
40-
private readonly Lazy<SqlConnection> _connectionAccessor;
37+
private readonly Lazy<DbConnection> _connectionAccessor;
4138

4239
public DapperDatabaseAccessorFactory(string connectionString, RaiseErrorWithNoWaitBehavior raiseErrorWithNoWaitBehavior, int? defaultCommandTimeout)
4340
{
4441
_raiseErrorWithNoWaitBehavior = raiseErrorWithNoWaitBehavior;
4542
_defaultCommandTimeout = defaultCommandTimeout;
46-
_connectionAccessor = new Lazy<SqlConnection>(() =>
43+
_connectionAccessor = new Lazy<DbConnection>(() =>
4744
{
48-
SqlConnection connection = new SqlConnection(connectionString);
45+
DbConnection connection = CreateConnection(connectionString);
4946
connection.Open();
5047
return connection;
5148
});
5249
}
5350

5451
public IDatabaseAccessor Create()
5552
{
56-
SqlConnection connection = _connectionAccessor.Value;
53+
DbConnection connection = _connectionAccessor.Value;
5754

55+
/*
5856
if (_raiseErrorWithNoWaitBehavior == RaiseErrorWithNoWaitBehavior.FireInfoMessageEventOnUserErrors)
5957
{
6058
connection.FireInfoMessageEventOnUserErrors = true;
6159
connection.InfoMessage += OnInfoMessage;
6260
}
61+
*/
6362

6463
return new DapperDatabaseAccessor(connection, _raiseErrorWithNoWaitBehavior, _defaultCommandTimeout);
6564
}
6665

6766
// When FireInfoMessageEventOnUserErrors is true, errors will trigger an info message event aswell, without throwing an exception.
6867
// To restore the original behavior for errors, we have to throw ourselves.
68+
/*
6969
private static void OnInfoMessage(object sender, SqlInfoMessageEventArgs e)
7070
{
7171
bool isError = e.Errors.Cast<SqlError>().Aggregate(false, (current, sqlError) => current || sqlError.Class > 10);
@@ -85,6 +85,16 @@ private static void OnInfoMessage(object sender, SqlInfoMessageEventArgs e)
8585
// Unless they are of a severe exception type.
8686
throw new AccessViolationException(exception.Message, exception);
8787
}
88+
*/
89+
90+
private static DbConnection CreateConnection(string connectionString)
91+
{
92+
#if NETFRAMEWORK
93+
return new System.Data.SqlClient.SqlConnection(connectionString);
94+
#else
95+
return new Microsoft.Data.SqlClient.SqlConnection(connectionString);
96+
#endif
97+
}
8898

8999
void IDisposable.Dispose()
90100
{
@@ -98,7 +108,7 @@ private sealed class DapperDatabaseAccessor : Dapper.DapperDatabaseAccessor
98108
private readonly RaiseErrorWithNoWaitBehavior _raiseErrorWithNoWaitBehavior;
99109
private readonly int? _defaultCommandTimeout;
100110

101-
public DapperDatabaseAccessor(DbConnection connection, RaiseErrorWithNoWaitBehavior raiseErrorWithNoWaitBehavior, int? defaultCommandTimeout) : base(connection, defaultCommandTimeout: defaultCommandTimeout, sqlDataRecordAdapter: new MicrosoftSqlDataRecordAdapter())
111+
public DapperDatabaseAccessor(DbConnection connection, RaiseErrorWithNoWaitBehavior raiseErrorWithNoWaitBehavior, int? defaultCommandTimeout) : base(connection, defaultCommandTimeout: defaultCommandTimeout, sqlClientAdapter: CreateSqlClientAdapter(connection))
102112
{
103113
_raiseErrorWithNoWaitBehavior = raiseErrorWithNoWaitBehavior;
104114
_defaultCommandTimeout = defaultCommandTimeout;
@@ -128,6 +138,15 @@ protected override void DisposeConnection()
128138
{
129139
// Will be disposed at the end of the test
130140
}
141+
142+
private static SqlClientAdapter CreateSqlClientAdapter(DbConnection connection)
143+
{
144+
#if NETFRAMEWORK
145+
return new SystemSqlClientAdapter(connection);
146+
#else
147+
return new MicrosoftSqlClientAdapter(connection);
148+
#endif
149+
}
131150
}
132151
}
133152

src/Dibix.Testing/Dibix.Testing.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
</PropertyGroup>
66

77
<ItemGroup>
8-
<Compile Include="..\..\shared\Data\MicrosoftSqlDataRecordAdapter.cs" Link="Data\%(Filename)%(Extension)" />
8+
<Compile Include="..\..\shared\Data\MicrosoftSqlClientAdapter.cs" Link="Data\%(Filename)%(Extension)" Condition="'$(TargetFramework)' != 'net48'" />
99
<Compile Include="..\..\shared\Diagnostics\Guard.cs" Link="Diagnostics\%(Filename)%(Extension)" />
1010
<Compile Include="..\..\shared\Extensions\EnumerableExtensions.cs" Link="Utilities\%(Filename)%(Extension)" />
1111
<Compile Include="..\..\shared\Extensions\ReflectionExtensions.cs" Link="Extensions\%(Filename)%(Extension)" />

0 commit comments

Comments
 (0)