Skip to content

Commit e233b73

Browse files
committed
Switch to Microsoft.Data.SqlClient in http/worker host and Dibix.Testing
1 parent 945c3c3 commit e233b73

19 files changed

Lines changed: 147 additions & 94 deletions

Directory.Packages.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
-->
2121
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.11.0" />
2222
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0" />
23+
<PackageVersion Include="Microsoft.Data.SqlClient" Version="5.2.2" />
2324
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.0" />
2425
<PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.0" />
2526
<PackageVersion Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="9.0.0" />
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
using System.Collections.Generic;
2+
using System.Data;
3+
using System.Linq;
4+
5+
namespace Dibix
6+
{
7+
public sealed class MicrosoftSqlDataRecordAdapter : SqlDataRecordAdapter
8+
{
9+
protected override object CollectValue(StructuredType type)
10+
{
11+
Microsoft.Data.SqlClient.Server.SqlDataRecord[] records = MapRecords(type).ToArray();
12+
return records;
13+
}
14+
15+
protected override void SetProviderSpecificParameterProperties(IDbDataParameter parameter, SqlDbType sqlDbType, string typeName)
16+
{
17+
if (parameter is not Microsoft.Data.SqlClient.SqlParameter sqlParam)
18+
return;
19+
20+
sqlParam.SqlDbType = sqlDbType;
21+
sqlParam.TypeName = typeName;
22+
}
23+
24+
private static IEnumerable<Microsoft.Data.SqlClient.Server.SqlDataRecord> MapRecords(StructuredType type)
25+
{
26+
Microsoft.Data.SqlClient.Server.SqlMetaData[] metadata = MapMetadata(type).ToArray();
27+
28+
foreach (Microsoft.SqlServer.Server.SqlDataRecord oldRecord in type.GetRecords())
29+
{
30+
Microsoft.Data.SqlClient.Server.SqlDataRecord newRecord = new Microsoft.Data.SqlClient.Server.SqlDataRecord(metadata);
31+
for (int i = 0; i < oldRecord.FieldCount; i++)
32+
newRecord.SetValue(i, oldRecord.GetValue(i));
33+
34+
yield return newRecord;
35+
}
36+
}
37+
38+
private static IEnumerable<Microsoft.Data.SqlClient.Server.SqlMetaData> MapMetadata(StructuredType type)
39+
{
40+
foreach (Microsoft.SqlServer.Server.SqlMetaData oldMetadata in type.GetMetadata())
41+
{
42+
yield return new Microsoft.Data.SqlClient.Server.SqlMetaData
43+
(
44+
name: oldMetadata.Name
45+
, dbType: oldMetadata.SqlDbType
46+
, maxLength: oldMetadata.MaxLength
47+
, precision: oldMetadata.Precision
48+
, scale: oldMetadata.Scale
49+
, locale: default
50+
, compareOptions: default
51+
, userDefinedType: default
52+
);
53+
}
54+
}
55+
}
56+
}

shared/Hosting/Data/DefaultDatabaseConnectionFactory.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
using System.Data.Common;
2-
using System.Data.SqlClient;
2+
using Microsoft.Data.SqlClient;
33
using Microsoft.Extensions.Options;
44

55
namespace Dibix.Hosting.Abstractions.Data

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)
28+
public LoggingDapperDatabaseAccessor(DbConnection connection, ILogger logger) : base(connection, sqlDataRecordAdapter: new MicrosoftSqlDataRecordAdapter())
2929
{
3030
_logger = logger;
3131
}

src/Dibix.Dapper/DapperDatabaseAccessor.cs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,16 @@ 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;
1718
#endregion
1819

1920
#region Constructor
20-
public DapperDatabaseAccessor(DbConnection connection = null, IDbTransaction defaultTransaction = null, int? defaultCommandTimeout = null, Action onDispose = null) : base(connection)
21+
public DapperDatabaseAccessor(DbConnection connection, IDbTransaction defaultTransaction = null, int? defaultCommandTimeout = null, Action onDispose = null, SqlDataRecordAdapter sqlDataRecordAdapter = null) : base(connection)
2122
{
2223
_defaultTransaction = defaultTransaction;
2324
_defaultCommandTimeout = defaultCommandTimeout;
2425
_onDispose = onDispose;
26+
_sqlDataRecordAdapter = sqlDataRecordAdapter ?? new SystemSqlClientDataRecordAdapter();
2527
ConfigureDapper();
2628
}
2729
#endregion
@@ -93,7 +95,7 @@ protected override void DisposeConnection()
9395
#endregion
9496

9597
#region Protected Methods
96-
protected static object CollectParameters(ParametersVisitor parametersVisitor)
98+
protected object CollectParameters(ParametersVisitor parametersVisitor)
9799
{
98100
Guard.IsNotNull(parametersVisitor, nameof(parametersVisitor));
99101
DynamicParameters @params = new DynamicParameters();
@@ -110,10 +112,10 @@ protected static object CollectParameters(ParametersVisitor parametersVisitor)
110112
#endregion
111113

112114
#region Private Methods
113-
private static object NormalizeParameterValue(object value)
115+
private object NormalizeParameterValue(object value)
114116
{
115-
if (value is StructuredType tvp)
116-
return new DapperStructuredTypeParameter(tvp.TypeName, tvp.GetRecords);
117+
if (value is StructuredType udt)
118+
return new DapperStructuredTypeParameter(udt, _sqlDataRecordAdapter);
117119

118120
return value;
119121
}
Lines changed: 7 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,28 @@
1-
using System;
2-
using System.Collections.Generic;
31
using System.Data;
4-
using System.Data.SqlClient;
5-
using System.Linq;
62
using Dapper;
7-
using Microsoft.SqlServer.Server;
83

94
namespace Dibix.Dapper
105
{
116
internal sealed class DapperStructuredTypeParameter : SqlMapper.ICustomQueryParameter
127
{
138
#region Fields
14-
private readonly Func<IEnumerable<SqlDataRecord>> _recordsProvider;
15-
#endregion
16-
17-
#region Properties
18-
public string UdtName { get; }
9+
private readonly StructuredType _udt;
10+
private readonly SqlDataRecordAdapter _sqlDataRecordAdapter;
1911
#endregion
2012

2113
#region Constructor
22-
public DapperStructuredTypeParameter(string udtName, Func<IEnumerable<SqlDataRecord>> recordsProvider)
14+
public DapperStructuredTypeParameter(StructuredType udt, SqlDataRecordAdapter sqlDataRecordAdapter)
2315
{
24-
this.UdtName = udtName;
25-
this._recordsProvider = recordsProvider;
16+
_udt = udt;
17+
_sqlDataRecordAdapter = sqlDataRecordAdapter;
2618
}
2719
#endregion
2820

2921
#region ICustomQueryParameter Members
3022
void SqlMapper.ICustomQueryParameter.AddParameter(IDbCommand command, string name)
3123
{
3224
Guard.IsNotNull(command, nameof(command));
33-
command.Parameters.Add(this.ToSqlParameter(command, name));
25+
command.Parameters.Add(ToSqlParameter(command, name));
3426
}
3527
#endregion
3628

@@ -39,27 +31,10 @@ private IDbDataParameter ToSqlParameter(IDbCommand command, string parameterName
3931
{
4032
IDbDataParameter param = command.CreateParameter();
4133
param.ParameterName = parameterName;
42-
param.Value = this.GetValue();
43-
44-
if (param is SqlParameter sqlParam)
45-
{
46-
sqlParam.SqlDbType = SqlDbType.Structured;
47-
sqlParam.TypeName = this.UdtName;
48-
}
34+
_sqlDataRecordAdapter.MapStructuredTypeToParameter(param, _udt);
4935

5036
return param;
5137
}
52-
53-
private object GetValue()
54-
{
55-
ICollection<SqlDataRecord> records = this.GetRecords() as ICollection<SqlDataRecord> ?? this.GetRecords().ToArray();
56-
return records.Any() ? records : null;
57-
}
58-
59-
private IEnumerable<SqlDataRecord> GetRecords()
60-
{
61-
return this._recordsProvider();
62-
}
6338
#endregion
6439
}
6540
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
</PropertyGroup>
88

99
<ItemGroup>
10+
<Compile Include="..\..\shared\Data\MicrosoftSqlDataRecordAdapter.cs" Link="Data\%(Filename)%(Extension)" />
1011
<Compile Include="..\..\shared\Diagnostics\Guard.cs" Link="Diagnostics\%(Filename)%(Extension)" />
1112
<Compile Include="..\..\shared\Extensions\BindConfigurationExtensions.cs" Link="Extensions\%(Filename)%(Extension)" />
1213
<Compile Include="..\..\shared\Extensions\EnumerableExtensions.cs" Link="Extensions\%(Filename)%(Extension)" />
@@ -21,6 +22,7 @@
2122

2223
<ItemGroup>
2324
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" />
25+
<PackageReference Include="Microsoft.Data.SqlClient" />
2426
<PackageReference Include="Microsoft.Extensions.Hosting" />
2527
<PackageReference Include="System.IO.Packaging" />
2628
</ItemGroup>

src/Dibix.Http.Host/Program.cs

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -159,27 +159,6 @@ void ConfigureLogging(ILoggingBuilder logging)
159159

160160
app.Services.GetRequiredService<IEndpointRegistrar>().Register(app);
161161

162-
// DbConnection is registered as a scoped service, because it should stay open for the entire HTTP request and then be disposed.
163-
// To use a scoped service outside the request, a scope must be created manually.
164-
// This is a sample for future use.
165-
/*
166-
using (IServiceScope serviceScope = app.Services.CreateScope())
167-
{
168-
// Connection is created here
169-
IDatabaseAccessorFactory databaseAccessorFactory = serviceScope.ServiceProvider.GetRequiredService<IDatabaseAccessorFactory>();
170-
using (IDatabaseAccessor databaseAccessor = databaseAccessorFactory.Create())
171-
{
172-
// Connection will not be disposed
173-
}
174-
using (IDatabaseAccessor databaseAccessor = databaseAccessorFactory.Create())
175-
{
176-
// Connection will not be disposed
177-
}
178-
179-
// Connection will now be disposed
180-
}
181-
*/
182-
183162
if (hostExtensionRegistrar != null)
184163
await hostExtensionRegistrar.Configure(app).ConfigureAwait(false);
185164

src/Dibix.Testing/Data/DatabaseTestUtility.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
using System;
22
using System.Data;
33
using System.Data.Common;
4-
using System.Data.SqlClient;
54
using System.Linq;
65
using System.Reflection;
76
using System.Threading;
87
using System.Threading.Tasks;
98
using Dapper;
9+
using Microsoft.Data.SqlClient;
1010

1111
namespace Dibix.Testing.Data
1212
{
@@ -74,6 +74,10 @@ private static void OnInfoMessage(object sender, SqlInfoMessageEventArgs e)
7474

7575
SqlErrorCollection errorCollection = e.Errors;
7676
string serverVersion = null;
77+
78+
// TODO
79+
// RaiseErrorWithNoWaitBehavior.FireInfoMessageEventOnUserErrors is currently not used and solely here for documentation purposes.
80+
// Since we switched from System.Data.SqlClient to Microsoft.Data.SqlClient, it hasn't been tested again, and might not work anymore.
7781
MethodInfo createExceptionMethod = typeof(SqlException).SafeGetMethod("CreateException", BindingFlags.NonPublic | BindingFlags.Static, new[] { typeof(SqlErrorCollection), typeof(string) });
7882
SqlException exception = (SqlException)createExceptionMethod.Invoke(null, new object[] { errorCollection, serverVersion });
7983

@@ -94,7 +98,7 @@ private sealed class DapperDatabaseAccessor : Dapper.DapperDatabaseAccessor
9498
private readonly RaiseErrorWithNoWaitBehavior _raiseErrorWithNoWaitBehavior;
9599
private readonly int? _defaultCommandTimeout;
96100

97-
public DapperDatabaseAccessor(DbConnection connection, RaiseErrorWithNoWaitBehavior raiseErrorWithNoWaitBehavior, int? defaultCommandTimeout) : base(connection, defaultCommandTimeout: defaultCommandTimeout)
101+
public DapperDatabaseAccessor(DbConnection connection, RaiseErrorWithNoWaitBehavior raiseErrorWithNoWaitBehavior, int? defaultCommandTimeout) : base(connection, defaultCommandTimeout: defaultCommandTimeout, sqlDataRecordAdapter: new MicrosoftSqlDataRecordAdapter())
98102
{
99103
_raiseErrorWithNoWaitBehavior = raiseErrorWithNoWaitBehavior;
100104
_defaultCommandTimeout = defaultCommandTimeout;

src/Dibix.Testing/Dibix.Testing.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
</PropertyGroup>
66

77
<ItemGroup>
8+
<Compile Include="..\..\shared\Data\MicrosoftSqlDataRecordAdapter.cs" Link="Data\%(Filename)%(Extension)" />
89
<Compile Include="..\..\shared\Diagnostics\Guard.cs" Link="Diagnostics\%(Filename)%(Extension)" />
910
<Compile Include="..\..\shared\Extensions\EnumerableExtensions.cs" Link="Utilities\%(Filename)%(Extension)" />
1011
<Compile Include="..\..\shared\Extensions\ReflectionExtensions.cs" Link="Extensions\%(Filename)%(Extension)" />
@@ -29,6 +30,7 @@
2930
</ItemGroup>
3031

3132
<ItemGroup>
33+
<PackageReference Include="Microsoft.Data.SqlClient" />
3234
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" />
3335
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" />
3436
<PackageReference Include="Microsoft.Extensions.Configuration.Json" />

0 commit comments

Comments
 (0)