Skip to content

Commit 9c49c1e

Browse files
jeremydmillerclaude
andcommitted
#270 step 7: extract DatabaseProvider memo-lookup helpers
The five provider ResolveDatabaseType + ResolveXxxDbType methods were all carrying the same memo-lookup prologue: try DatabaseTypeMemo / ParameterTypeMemo; if absent and the type is Nullable<T>, look up T instead, copy the result to the nullable's slot for next-time, and return. Only the fallback after that miss differs per provider (PG → Npgsql plugin type map; SS/Oracle/MySQL → NotSupportedException; SQLite → null/Text). Move the lookup-with-nullable-promote into DatabaseProvider as two shared protected helpers — ResolveDatabaseTypeFromMemo and ResolveParameterTypeFromMemo — and rewrite each provider's two Resolve* methods as one-or-two-liners that combine the helper with the provider- specific fallback. The audit also suggested templating determineParameterType but I held back: in SS/Oracle/MySQL/SQLite the post-Resolve branches (IsEnum, IsArray, DBNull, IsConstructedGenericType) are largely unreachable because their Resolve always returns a sentinel fallback rather than null. Removing that dead code would technically preserve behaviour but risks edge cases that aren't covered by the current tests, so I left those bodies alone. Net diff: -97 LOC across the five provider files. No behaviour change. Tests pass: - PostgreSQL: 69/69 (Provider + TypeMapping + ToParameterType filter) - SQL Server: 8/8 (+8 pre-existing skips) - SQLite: 45/45 - MySQL: 37/37 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 02a6440 commit 9c49c1e

6 files changed

Lines changed: 76 additions & 113 deletions

File tree

src/Weasel.Core/DatabaseProvider.cs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System.Data.Common;
22
using ImTools;
33
using JasperFx.Core;
4+
using JasperFx.Core.Reflection;
45
using Weasel.Core.Names;
56

67
namespace Weasel.Core;
@@ -170,6 +171,53 @@ public void RegisterMapping(Type type, string databaseType, TParameterType? para
170171
ParameterTypeMemo.Swap(d => d.AddOrUpdate(type, parameterType));
171172
}
172173

174+
/// <summary>
175+
/// Shared memo lookup for the database-type string of a CLR type, with the
176+
/// standard nullable-promote fallback (if <c>T?</c> is asked but <c>T</c> is
177+
/// in the memo, copy the entry to <c>T?</c> and return it). Returns null
178+
/// when neither the type nor its inner-nullable is mapped — subclasses then
179+
/// apply provider-specific fallbacks (PG queries Npgsql's plugin type map,
180+
/// SS/Oracle/MySQL throw, SQLite returns null to signal "TEXT/JSON").
181+
/// </summary>
182+
protected string? ResolveDatabaseTypeFromMemo(Type type)
183+
{
184+
if (DatabaseTypeMemo.Value.TryFind(type, out var value))
185+
{
186+
return value;
187+
}
188+
189+
if (type.IsNullable() && DatabaseTypeMemo.Value.TryFind(type.GetInnerTypeFromNullable(), out var inner))
190+
{
191+
DatabaseTypeMemo.Swap(d => d.AddOrUpdate(type, inner));
192+
return inner;
193+
}
194+
195+
return null;
196+
}
197+
198+
/// <summary>
199+
/// Shared memo lookup for the provider parameter-type enum of a CLR type,
200+
/// mirror of <see cref="ResolveDatabaseTypeFromMemo" />. Subclasses apply
201+
/// their own fallback when this returns null (PG queries Npgsql plugin;
202+
/// SS returns <c>Variant</c>; Oracle returns <c>Varchar2</c>; MySQL returns
203+
/// <c>VarChar</c>; SQLite returns <c>Text</c>).
204+
/// </summary>
205+
protected TParameterType? ResolveParameterTypeFromMemo(Type type)
206+
{
207+
if (ParameterTypeMemo.Value.TryFind(type, out var value))
208+
{
209+
return value;
210+
}
211+
212+
if (type.IsNullable() && ParameterTypeMemo.Value.TryFind(type.GetInnerTypeFromNullable(), out var inner))
213+
{
214+
ParameterTypeMemo.Swap(d => d.AddOrUpdate(type, inner));
215+
return inner;
216+
}
217+
218+
return null;
219+
}
220+
173221
protected abstract Type[] determineClrTypesForParameterType(TParameterType dbType);
174222

175223
public TParameter AddParameter(TCommand command, object? value, TParameterType? dbType = null)

src/Weasel.MySql/MySqlProvider.cs

Lines changed: 4 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -36,35 +36,14 @@ protected override void storeMappings()
3636

3737
private string? ResolveDatabaseType(Type type)
3838
{
39-
if (DatabaseTypeMemo.Value.TryFind(type, out var value))
40-
{
41-
return value;
42-
}
43-
44-
if (!type.IsNullable() ||
45-
!DatabaseTypeMemo.Value.TryFind(type.GetInnerTypeFromNullable(), out var databaseType))
46-
throw new NotSupportedException(
47-
$"Weasel.MySql does not (yet) support database type mapping to {type.FullNameInCode()}");
48-
49-
DatabaseTypeMemo.Swap(d => d.AddOrUpdate(type, databaseType));
50-
return databaseType;
39+
return ResolveDatabaseTypeFromMemo(type)
40+
?? throw new NotSupportedException(
41+
$"Weasel.MySql does not (yet) support database type mapping to {type.FullNameInCode()}");
5142
}
5243

5344
private MySqlDbType? ResolveMySqlDbType(Type type)
5445
{
55-
if (ParameterTypeMemo.Value.TryFind(type, out var value))
56-
{
57-
return value;
58-
}
59-
60-
if (type.IsNullable() &&
61-
ParameterTypeMemo.Value.TryFind(type.GetInnerTypeFromNullable(), out var parameterType))
62-
{
63-
ParameterTypeMemo.Swap(d => d.AddOrUpdate(type, parameterType));
64-
return parameterType;
65-
}
66-
67-
return MySqlDbType.VarChar;
46+
return ResolveParameterTypeFromMemo(type) ?? MySqlDbType.VarChar;
6847
}
6948

7049
protected override Type[] determineClrTypesForParameterType(MySqlDbType dbType)

src/Weasel.Oracle/OracleProvider.cs

Lines changed: 4 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -36,35 +36,14 @@ protected override void storeMappings()
3636

3737
private string? ResolveDatabaseType(Type type)
3838
{
39-
if (DatabaseTypeMemo.Value.TryFind(type, out var value))
40-
{
41-
return value;
42-
}
43-
44-
if (!type.IsNullable() ||
45-
!DatabaseTypeMemo.Value.TryFind(type.GetInnerTypeFromNullable(), out var databaseType))
46-
throw new NotSupportedException(
47-
$"Weasel.Oracle does not (yet) support database type mapping to {type.FullNameInCode()}");
48-
49-
DatabaseTypeMemo.Swap(d => d.AddOrUpdate(type, databaseType));
50-
return databaseType;
39+
return ResolveDatabaseTypeFromMemo(type)
40+
?? throw new NotSupportedException(
41+
$"Weasel.Oracle does not (yet) support database type mapping to {type.FullNameInCode()}");
5142
}
5243

5344
private OracleDbType? ResolveOracleDbType(Type type)
5445
{
55-
if (ParameterTypeMemo.Value.TryFind(type, out var value))
56-
{
57-
return value;
58-
}
59-
60-
if (type.IsNullable() &&
61-
ParameterTypeMemo.Value.TryFind(type.GetInnerTypeFromNullable(), out var parameterType))
62-
{
63-
ParameterTypeMemo.Swap(d => d.AddOrUpdate(type, parameterType));
64-
return parameterType;
65-
}
66-
67-
return OracleDbType.Varchar2;
46+
return ResolveParameterTypeFromMemo(type) ?? OracleDbType.Varchar2;
6847
}
6948

7049
protected override Type[] determineClrTypesForParameterType(OracleDbType dbType)

src/Weasel.Postgresql/PostgresqlProvider.cs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -51,29 +51,31 @@ protected override void storeMappings()
5151
// custom npgsql mappings prior to execution.
5252
private string? ResolveDatabaseType(Type type)
5353
{
54-
if (DatabaseTypeMemo.Value.TryFind(type, out var value))
54+
// Try the shared base memo (with nullable-promote) first; on a miss, ask
55+
// Npgsql's type-mapping plugin (handles JsonB, NpgsqlRange<T>, …) and
56+
// cache the answer so subsequent lookups are O(1).
57+
var cached = ResolveDatabaseTypeFromMemo(type);
58+
if (cached != null)
5559
{
56-
return value;
60+
return cached;
5761
}
5862

59-
value = GetTypeMapping(type)?.DataTypeName;
60-
63+
var value = GetTypeMapping(type)?.DataTypeName;
6164
DatabaseTypeMemo.Swap(d => d.AddOrUpdate(type, value));
62-
6365
return value;
6466
}
6567

6668
private NpgsqlDbType? ResolveNpgsqlDbType(Type type)
6769
{
68-
if (ParameterTypeMemo.Value.TryFind(type, out var value))
70+
// Same pattern as ResolveDatabaseType but for the NpgsqlDbType enum.
71+
var cached = ResolveParameterTypeFromMemo(type);
72+
if (cached != null)
6973
{
70-
return value;
74+
return cached;
7175
}
7276

73-
value = GetTypeMapping(type)?.NpgsqlDbType;
74-
77+
var value = GetTypeMapping(type)?.NpgsqlDbType;
7578
ParameterTypeMemo.Swap(d => d.AddOrUpdate(type, value));
76-
7779
return value;
7880
}
7981

src/Weasel.SqlServer/SqlServerProvider.cs

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -37,36 +37,14 @@ protected override void storeMappings()
3737
// custom Sql mappings prior to execution.
3838
private string? ResolveDatabaseType(Type type)
3939
{
40-
if (DatabaseTypeMemo.Value.TryFind(type, out var value))
41-
{
42-
return value;
43-
}
44-
45-
if (!type.IsNullable() ||
46-
!DatabaseTypeMemo.Value.TryFind(type.GetInnerTypeFromNullable(), out var databaseType))
47-
throw new NotSupportedException(
48-
$"Weasel.SqlServer does not (yet) support database type mapping to {type.FullNameInCode()}");
49-
50-
DatabaseTypeMemo.Swap(d => d.AddOrUpdate(type, databaseType));
51-
return databaseType;
52-
40+
return ResolveDatabaseTypeFromMemo(type)
41+
?? throw new NotSupportedException(
42+
$"Weasel.SqlServer does not (yet) support database type mapping to {type.FullNameInCode()}");
5343
}
5444

5545
private SqlDbType? ResolveSqlDbType(Type type)
5646
{
57-
if (ParameterTypeMemo.Value.TryFind(type, out var value))
58-
{
59-
return value;
60-
}
61-
62-
if (type.IsNullable() &&
63-
ParameterTypeMemo.Value.TryFind(type.GetInnerTypeFromNullable(), out var parameterType))
64-
{
65-
ParameterTypeMemo.Swap(d => d.AddOrUpdate(type, parameterType));
66-
return parameterType;
67-
}
68-
69-
return SqlDbType.Variant;
47+
return ResolveParameterTypeFromMemo(type) ?? SqlDbType.Variant;
7048
}
7149

7250

src/Weasel.Sqlite/SqliteProvider.cs

Lines changed: 4 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -47,37 +47,14 @@ protected override void storeMappings()
4747

4848
private string? ResolveDatabaseType(Type type)
4949
{
50-
if (DatabaseTypeMemo.Value.TryFind(type, out var value))
51-
{
52-
return value;
53-
}
54-
55-
if (!type.IsNullable() ||
56-
!DatabaseTypeMemo.Value.TryFind(type.GetInnerTypeFromNullable(), out var databaseType))
57-
{
58-
// For unmapped types, default to TEXT for JSON storage
59-
return null;
60-
}
61-
62-
DatabaseTypeMemo.Swap(d => d.AddOrUpdate(type, databaseType));
63-
return databaseType;
50+
// Unmapped types fall through to TEXT in GetDatabaseType (for JSON storage),
51+
// so we just return null on a memo miss and let the caller decide.
52+
return ResolveDatabaseTypeFromMemo(type);
6453
}
6554

6655
private SqliteType? ResolveSqliteType(Type type)
6756
{
68-
if (ParameterTypeMemo.Value.TryFind(type, out var value))
69-
{
70-
return value;
71-
}
72-
73-
if (type.IsNullable() &&
74-
ParameterTypeMemo.Value.TryFind(type.GetInnerTypeFromNullable(), out var parameterType))
75-
{
76-
ParameterTypeMemo.Swap(d => d.AddOrUpdate(type, parameterType));
77-
return parameterType;
78-
}
79-
80-
return SqliteType.Text;
57+
return ResolveParameterTypeFromMemo(type) ?? SqliteType.Text;
8158
}
8259

8360
protected override Type[] determineClrTypesForParameterType(SqliteType dbType)

0 commit comments

Comments
 (0)