Skip to content

Commit 8fbb01c

Browse files
committed
treat IEnumerable<string,object> as dynamic parameters
1 parent 65cb7d5 commit 8fbb01c

6 files changed

Lines changed: 74 additions & 48 deletions

File tree

src/Dapper.AOT.Analyzers/CodeAnalysis/DapperAnalyzer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -683,7 +683,7 @@ internal static Location SharedParseArgsAndFlags(in ParseState ctx, IInvocationO
683683
flags |= OperationFlags.DoNotGenerate;
684684
reportDiagnostic?.Invoke(Diagnostic.Create(Diagnostics.GenericTypeParameter, argLocation, paramType!.ToDisplayString()));
685685
}
686-
else if (IsMissingOrObjectOrDynamic(paramType) || IsDynamicParameters(paramType))
686+
else if (IsMissingOrObjectOrDynamic(paramType) || IsDynamicParameters(paramType, out _))
687687
{
688688
flags |= OperationFlags.DoNotGenerate;
689689
reportDiagnostic?.Invoke(Diagnostic.Create(Diagnostics.UntypedParameter, argLocation));

src/Dapper.AOT.Analyzers/Internal/Inspection.cs

Lines changed: 58 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -159,15 +159,42 @@ public static bool IsDapperAttribute(AttributeData attrib)
159159

160160
public static bool IsMissingOrObjectOrDynamic(ITypeSymbol? type) => type is null || type.SpecialType == SpecialType.System_Object || type.TypeKind == TypeKind.Dynamic;
161161

162-
internal static bool IsDynamicParameters(ITypeSymbol? type)
162+
internal static bool IsDynamicParameters(ITypeSymbol? type, out bool needsConstruction)
163163
{
164+
needsConstruction = false;
164165
if (type is null || type.SpecialType != SpecialType.None) return false;
165166
if (IsBasicDapperType(type, Types.DynamicParameters)
166167
|| IsNestedSqlMapperType(type, Types.IDynamicParameters, TypeKind.Interface)) return true;
167168
foreach (var i in type.AllInterfaces)
168169
{
169170
if (IsNestedSqlMapperType(i, Types.IDynamicParameters, TypeKind.Interface)) return true;
170171
}
172+
173+
// Dapper also treats anything IEnumerable<string,object[?]> as dynamic parameters
174+
if (type.ImplementsIEnumerable(out var found) && found is INamedTypeSymbol { Arity: 1 } iet
175+
&& iet.TypeArguments[0] is INamedTypeSymbol
176+
{
177+
Name: "KeyValuePair", Arity: 2, ContainingType: null,
178+
ContainingNamespace:
179+
{
180+
Name: "Generic",
181+
ContainingNamespace:
182+
{
183+
Name: "Collections",
184+
ContainingNamespace:
185+
{
186+
Name: "System",
187+
ContainingNamespace.IsGlobalNamespace: true
188+
}
189+
}
190+
}
191+
} kvp
192+
&& kvp.TypeArguments[0].SpecialType == SpecialType.System_String
193+
&& kvp.TypeArguments[1].SpecialType == SpecialType.System_Object)
194+
{
195+
needsConstruction = true;
196+
return true;
197+
}
171198
return false;
172199
}
173200

@@ -419,7 +446,7 @@ public readonly struct ElementMember
419446
/// Order of member in constructor parameter list (starts from 0).
420447
/// </summary>
421448
public int? ConstructorParameterOrder { get; }
422-
449+
423450
/// <summary>
424451
/// Order of member in factory method parameter list (starts from 0).
425452
/// </summary>
@@ -452,10 +479,10 @@ public ElementMember(
452479
{
453480
_dbValue = dbValue;
454481
_flags = flags;
455-
482+
456483
Member = member;
457484
Kind = kind;
458-
485+
459486
ConstructorParameterOrder = constructorParameterOrder;
460487
FactoryMethodParameterOrder = factoryMethodParameterOrder;
461488
}
@@ -493,7 +520,7 @@ public enum ConstructorResult
493520
FailMultipleExplicit,
494521
FailMultipleImplicit
495522
}
496-
523+
497524
public enum FactoryMethodResult
498525
{
499526
// note that implicit isn't a thing here
@@ -513,12 +540,12 @@ internal static FactoryMethodResult ChooseFactoryMethod(ITypeSymbol? typeSymbol,
513540
{
514541
return FactoryMethodResult.NoneFound;
515542
}
516-
543+
517544
var staticMethods = typeSymbol
518-
.GetMethods(method =>
545+
.GetMethods(method =>
519546
method.IsStatic &&
520547
SymbolEqualityComparer.Default.Equals(method.ReturnType, typeSymbol) &&
521-
method.DeclaredAccessibility == Accessibility.Public
548+
method.DeclaredAccessibility == Accessibility.Public
522549
)
523550
?.ToArray();
524551
if (staticMethods?.Length == 0)
@@ -541,7 +568,7 @@ internal static FactoryMethodResult ChooseFactoryMethod(ITypeSymbol? typeSymbol,
541568

542569
return factoryMethod is null ? FactoryMethodResult.NoneFound : FactoryMethodResult.SuccessSingleExplicit;
543570
}
544-
571+
545572
/// <summary>
546573
/// Builds a collection of type constructors, which are NOT:
547574
/// a) parameterless
@@ -613,7 +640,8 @@ internal static ConstructorResult ChooseConstructor(ITypeSymbol? typeSymbol, out
613640
ContainingNamespace:
614641
{
615642
Name: "Runtime",
616-
ContainingNamespace: {
643+
ContainingNamespace:
644+
{
617645
Name: "System",
618646
ContainingNamespace.IsGlobalNamespace: true
619647
}
@@ -630,7 +658,7 @@ internal static ConstructorResult ChooseConstructor(ITypeSymbol? typeSymbol, out
630658
// ruled out by signature
631659
continue;
632660
}
633-
661+
634662
if (constructor is not null)
635663
{
636664
return ConstructorResult.FailMultipleImplicit;
@@ -704,7 +732,7 @@ internal static ImmutableArray<ElementMember> GetMembers(bool forParameters, ITy
704732
int? constructorParameterOrder = constructorParameters?.TryGetValue(member.Name, out var constructorParameter) == true
705733
? constructorParameter.Order
706734
: null;
707-
735+
708736
int? factoryMethodParamOrder = factoryMethodParameters?.TryGetValue(member.Name, out var factoryMethodParam) == true
709737
? factoryMethodParam.Order
710738
: null;
@@ -1018,12 +1046,12 @@ public static bool TryGetConstantValueWithSyntax<T>(IOperation val, out T? value
10181046
case StringSyntaxKind.ConcatenatedString:
10191047
case StringSyntaxKind.InterpolatedString:
10201048
case StringSyntaxKind.FormatString:
1021-
{
1022-
value = default!;
1023-
syntax = null;
1024-
syntaxKind = stringSyntaxKind;
1025-
return false;
1026-
}
1049+
{
1050+
value = default!;
1051+
syntax = null;
1052+
syntaxKind = stringSyntaxKind;
1053+
return false;
1054+
}
10271055
}
10281056
}
10291057

@@ -1096,25 +1124,25 @@ internal static bool IsCommand(INamedTypeSymbol type)
10961124
while (type.SpecialType != SpecialType.System_Object)
10971125
{
10981126
if (type is
1099-
{
1100-
Name: nameof(DbCommand),
1101-
ContainingType: null,
1102-
Arity: 0,
1103-
IsGenericType: false,
1104-
ContainingNamespace:
11051127
{
1106-
Name: "Common",
1128+
Name: nameof(DbCommand),
1129+
ContainingType: null,
1130+
Arity: 0,
1131+
IsGenericType: false,
11071132
ContainingNamespace:
11081133
{
1109-
Name: "Data",
1134+
Name: "Common",
11101135
ContainingNamespace:
11111136
{
1112-
Name: "System",
1113-
ContainingNamespace.IsGlobalNamespace: true,
1137+
Name: "Data",
1138+
ContainingNamespace:
1139+
{
1140+
Name: "System",
1141+
ContainingNamespace.IsGlobalNamespace: true,
1142+
}
11141143
}
11151144
}
1116-
}
1117-
})
1145+
})
11181146
{
11191147
return true;
11201148
}

src/Dapper.AOT.Analyzers/Internal/Roslyn/TypeSymbolExtensions.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -208,17 +208,17 @@ public static bool ImplementsIReadOnlyList(this ITypeSymbol? typeSymbol, out ITy
208208
private static bool ImplementsInterface(
209209
this ITypeSymbol? typeSymbol,
210210
SpecialType interfaceType,
211-
out ITypeSymbol? searchedInterface,
211+
out ITypeSymbol? found,
212212
bool searchFromStart = true)
213213
{
214214
if (typeSymbol is null)
215215
{
216-
searchedInterface = null;
216+
found = null;
217217
return false;
218218
}
219219
if (typeSymbol.SpecialType == interfaceType || typeSymbol.OriginalDefinition?.SpecialType == interfaceType)
220220
{
221-
searchedInterface = typeSymbol;
221+
found = typeSymbol;
222222
return true;
223223
}
224224

@@ -231,7 +231,7 @@ private static bool ImplementsInterface(
231231
if (currentSymbol.SpecialType == interfaceType
232232
|| currentSymbol.OriginalDefinition?.SpecialType == interfaceType)
233233
{
234-
searchedInterface = currentSymbol;
234+
found = currentSymbol;
235235
return true;
236236
}
237237
}
@@ -245,13 +245,13 @@ private static bool ImplementsInterface(
245245
if (currentSymbol.SpecialType == interfaceType
246246
|| currentSymbol.OriginalDefinition?.SpecialType == interfaceType)
247247
{
248-
searchedInterface = currentSymbol;
248+
found = currentSymbol;
249249
return true;
250250
}
251251
}
252252
}
253253

254-
searchedInterface = null;
254+
found = null;
255255
return false;
256256
}
257257
}

test/Dapper.AOT.Test/Interceptors/Techempower.input.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
using System.Threading.Tasks;
99
using System.Linq;
1010

11-
[module: DapperAot]
11+
[module:DapperAot]
1212

1313
var connectionString = new SqlConnectionStringBuilder
1414
{
@@ -32,7 +32,6 @@ public BenchRunner(string connectionString, DbProviderFactory dbProviderFactory)
3232
_connectionString = connectionString;
3333
}
3434

35-
[DapperAot]
3635
public void Create()
3736
{
3837
using var conn = _dbProviderFactory.CreateConnection();
@@ -50,7 +49,6 @@ static IEnumerable<World> Invent(int count)
5049
}
5150
}
5251

53-
[DapperAot(false)] // dictionary usage isn't going to work today
5452
public async Task<World[]> LoadMultipleUpdatesRows(int count)
5553
{
5654
count = Clamp(count, 1, 500);

test/Dapper.AOT.Test/Interceptors/Techempower.output.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ namespace Dapper.AOT // interceptors must be in a known namespace
33
{
44
file static class DapperGeneratedInterceptors
55
{
6-
[global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\Techempower.input.cs", 41, 20)]
6+
[global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\Techempower.input.cs", 40, 20)]
7+
[global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\Techempower.input.cs", 41, 14)]
78
[global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\Techempower.input.cs", 42, 14)]
8-
[global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\Techempower.input.cs", 43, 14)]
99
internal static int Execute0(this global::System.Data.IDbConnection cnn, string sql, object? param, global::System.Data.IDbTransaction? transaction, int? commandTimeout, global::System.Data.CommandType? commandType)
1010
{
1111
// Execute, Text
@@ -17,7 +17,7 @@ internal static int Execute0(this global::System.Data.IDbConnection cnn, string
1717

1818
}
1919

20-
[global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\Techempower.input.cs", 44, 14)]
20+
[global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\Techempower.input.cs", 43, 14)]
2121
internal static int Execute1(this global::System.Data.IDbConnection cnn, string sql, object? param, global::System.Data.IDbTransaction? transaction, int? commandTimeout, global::System.Data.CommandType? commandType)
2222
{
2323
// Execute, HasParameters, Text, KnownParameters
@@ -31,7 +31,7 @@ internal static int Execute1(this global::System.Data.IDbConnection cnn, string
3131

3232
}
3333

34-
[global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\Techempower.input.cs", 95, 19)]
34+
[global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\Techempower.input.cs", 93, 19)]
3535
internal static global::System.Threading.Tasks.Task<global::World> QueryFirstOrDefaultAsync2(this global::System.Data.IDbConnection cnn, string sql, object? param, global::System.Data.IDbTransaction? transaction, int? commandTimeout, global::System.Data.CommandType? commandType)
3636
{
3737
// Query, Async, TypedResult, HasParameters, SingleRow, Text, BindResultsByName, KnownParameters

test/Dapper.AOT.Test/Interceptors/Techempower.output.netfx.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ namespace Dapper.AOT // interceptors must be in a known namespace
33
{
44
file static class DapperGeneratedInterceptors
55
{
6-
[global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\Techempower.input.cs", 41, 20)]
6+
[global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\Techempower.input.cs", 40, 20)]
7+
[global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\Techempower.input.cs", 41, 14)]
78
[global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\Techempower.input.cs", 42, 14)]
8-
[global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\Techempower.input.cs", 43, 14)]
99
internal static int Execute0(this global::System.Data.IDbConnection cnn, string sql, object? param, global::System.Data.IDbTransaction? transaction, int? commandTimeout, global::System.Data.CommandType? commandType)
1010
{
1111
// Execute, Text
@@ -17,7 +17,7 @@ internal static int Execute0(this global::System.Data.IDbConnection cnn, string
1717

1818
}
1919

20-
[global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\Techempower.input.cs", 44, 14)]
20+
[global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\Techempower.input.cs", 43, 14)]
2121
internal static int Execute1(this global::System.Data.IDbConnection cnn, string sql, object? param, global::System.Data.IDbTransaction? transaction, int? commandTimeout, global::System.Data.CommandType? commandType)
2222
{
2323
// Execute, HasParameters, Text, KnownParameters
@@ -31,7 +31,7 @@ internal static int Execute1(this global::System.Data.IDbConnection cnn, string
3131

3232
}
3333

34-
[global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\Techempower.input.cs", 95, 19)]
34+
[global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\Techempower.input.cs", 93, 19)]
3535
internal static global::System.Threading.Tasks.Task<global::World> QueryFirstOrDefaultAsync2(this global::System.Data.IDbConnection cnn, string sql, object? param, global::System.Data.IDbTransaction? transaction, int? commandTimeout, global::System.Data.CommandType? commandType)
3636
{
3737
// Query, Async, TypedResult, HasParameters, SingleRow, Text, BindResultsByName, KnownParameters

0 commit comments

Comments
 (0)