Skip to content

Commit 9f06e48

Browse files
committed
Update generator to use HubMethodNameAttribute.
- Introduced `SignalRMethodName` property in `MethodMetadata.cs` to retrieve method names from `HubMethodNameAttribute`. - Modified `HubConnectionExtensionsBinderTemplate.cs` and `MethodMetadataExtensions.cs` to utilize the new `SignalRMethodName` for correct method registration and invocation. - Added `TypedSignalR.Client.SourceGeneratorTests` project to the solution to unit test the source generator.
1 parent 9483028 commit 9f06e48

File tree

8 files changed

+3529
-25
lines changed

8 files changed

+3529
-25
lines changed

Directory.Packages.props

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,25 @@
11
<Project>
2-
<!-- https://learn.microsoft.com/nuget/consume-packages/central-package-management -->
3-
<PropertyGroup>
4-
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
5-
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
6-
</PropertyGroup>
7-
<ItemGroup>
8-
<PackageVersion Include="AspNetCore.SignalR.OpenTelemetry" Version="1.6.0" />
9-
<PackageVersion Include="TypedSignalR.Client" Version="3.6.0" />
10-
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" />
11-
<PackageVersion Include="Microsoft.AspNetCore.SignalR.Client" Version="8.0.13" />
12-
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.14" />
13-
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="8.0.14" />
14-
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
15-
<PackageVersion Include="xunit" Version="2.9.3" />
16-
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" />
17-
<PackageVersion Include="coverlet.collector" Version="6.0.4" />
18-
</ItemGroup>
19-
</Project>
2+
<!-- https://learn.microsoft.com/nuget/consume-packages/central-package-management -->
3+
<PropertyGroup>
4+
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
5+
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
6+
</PropertyGroup>
7+
<ItemGroup>
8+
<PackageVersion Include="AspNetCore.SignalR.OpenTelemetry" Version="1.6.0" />
9+
<PackageVersion Include="Microsoft.AspNetCore.SignalR.Core" Version="1.2.0" />
10+
<PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="4.8.0" />
11+
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing" Version="1.1.2" />
12+
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.SourceGenerators.Testing" Version="1.1.2" />
13+
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.8.0" />
14+
<PackageVersion Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="4.8.0" />
15+
<PackageVersion Include="TypedSignalR.Client" Version="3.6.0" />
16+
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" />
17+
<PackageVersion Include="Microsoft.AspNetCore.SignalR.Client" Version="8.0.13" />
18+
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.14" />
19+
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="8.0.14" />
20+
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
21+
<PackageVersion Include="xunit" Version="2.9.3" />
22+
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" />
23+
<PackageVersion Include="coverlet.collector" Version="6.0.4" />
24+
</ItemGroup>
25+
</Project>

TypedSignalR.Client.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
3636
Directory.Packages.props = Directory.Packages.props
3737
EndProjectSection
3838
EndProject
39+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TypedSignalR.Client.SourceGeneratorTests", "tests\TypedSignalR.Client.SourceGeneratorTests\TypedSignalR.Client.SourceGeneratorTests.csproj", "{547B45CB-8CAD-6B6B-5E80-88AE3B93A43F}"
40+
EndProject
3941
Global
4042
GlobalSection(SolutionConfigurationPlatforms) = preSolution
4143
Debug|Any CPU = Debug|Any CPU
@@ -86,6 +88,10 @@ Global
8688
{4AD46B59-DB78-465F-BF10-F0F74B34722D}.Debug|Any CPU.Build.0 = Debug|Any CPU
8789
{4AD46B59-DB78-465F-BF10-F0F74B34722D}.Release|Any CPU.ActiveCfg = Release|Any CPU
8890
{4AD46B59-DB78-465F-BF10-F0F74B34722D}.Release|Any CPU.Build.0 = Release|Any CPU
91+
{547B45CB-8CAD-6B6B-5E80-88AE3B93A43F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
92+
{547B45CB-8CAD-6B6B-5E80-88AE3B93A43F}.Debug|Any CPU.Build.0 = Debug|Any CPU
93+
{547B45CB-8CAD-6B6B-5E80-88AE3B93A43F}.Release|Any CPU.ActiveCfg = Release|Any CPU
94+
{547B45CB-8CAD-6B6B-5E80-88AE3B93A43F}.Release|Any CPU.Build.0 = Release|Any CPU
8995
EndGlobalSection
9096
GlobalSection(SolutionProperties) = preSolution
9197
HideSolutionNode = FALSE
@@ -102,6 +108,7 @@ Global
102108
{0628FEE3-2C6E-4C02-A398-4E55781A5A85} = {2F614DC6-F5D9-4872-9B4E-70F39B02D6EC}
103109
{4D174400-02DF-4816-8CFC-A94DB3DBBBFA} = {2F614DC6-F5D9-4872-9B4E-70F39B02D6EC}
104110
{4AD46B59-DB78-465F-BF10-F0F74B34722D} = {196DB73F-4D3E-4A90-AE48-40925CB33560}
111+
{547B45CB-8CAD-6B6B-5E80-88AE3B93A43F} = {2F614DC6-F5D9-4872-9B4E-70F39B02D6EC}
105112
EndGlobalSection
106113
GlobalSection(ExtensibilityGlobals) = postSolution
107114
SolutionGuid = {3C64E712-2479-4BF8-9169-B21DC3E787EC}

src/TypedSignalR.Client/CodeAnalysis/MethodMetadata.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Collections.Generic;
2+
using System.Diagnostics;
23
using System.Linq;
34
using Microsoft.CodeAnalysis;
45

@@ -12,6 +13,7 @@ public sealed class MethodMetadata
1213
public IReadOnlyList<ParameterMetadata> Parameters { get; }
1314

1415
public string MethodName { get; }
16+
public string SignalRMethodName { get; }
1517
public string ReturnType { get; }
1618
public bool IsGenericReturnType { get; }
1719
public string? GenericReturnTypeArgument { get; }
@@ -25,6 +27,15 @@ public MethodMetadata(IMethodSymbol methodSymbol)
2527
.Select(x => new ParameterMetadata(x))
2628
.ToArray();
2729

30+
SignalRMethodName =
31+
(
32+
methodSymbol.GetAttributes()
33+
.FirstOrDefault(x => x.AttributeClass?.ToDisplayString() == "Microsoft.AspNetCore.SignalR.HubMethodNameAttribute")
34+
?.ConstructorArguments.SingleOrDefault()
35+
)
36+
?.Value?.ToString()
37+
?? MethodName;
38+
2839
ReturnType = methodSymbol.ReturnType.ToDisplayString(SymbolDisplayFormatRule.FullyQualifiedNullableReferenceTypeFormat);
2940

3041
INamedTypeSymbol? returnTypeSymbol = methodSymbol.ReturnType as INamedTypeSymbol;

src/TypedSignalR.Client/Templates/HubConnectionExtensionsBinderTemplate.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ private string CreateRegistrationString(TypeMetadata receiverType)
106106
private string CreateRegistrationStringCore(MethodMetadata method)
107107
{
108108
return $$"""
109-
compositeDisposable.Add(global::Microsoft.AspNetCore.SignalR.Client.HubConnectionExtensions.On(connection, nameof(receiver.{{method.MethodName}}), {{method.CreateParameterTypeArrayString(_specialSymbols)}}, HandlerConverter.Convert{{method.CreateTypeArgumentsStringForHandlerConverter(_specialSymbols)}}(receiver.{{method.MethodName}})));
109+
compositeDisposable.Add(global::Microsoft.AspNetCore.SignalR.Client.HubConnectionExtensions.On(connection, "{{method.SignalRMethodName}}", {{method.CreateParameterTypeArrayString(_specialSymbols)}}, HandlerConverter.Convert{{method.CreateTypeArgumentsStringForHandlerConverter(_specialSymbols)}}(receiver.{{method.MethodName}})));
110110
""";
111111
}
112112
}

src/TypedSignalR.Client/Templates/MethodMetadataExtensions.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ private static string CreateUnaryMethodString(MethodMetadata method)
278278
return $$"""
279279
public {{method.ReturnType}} {{method.MethodName}}({{method.CreateParametersString()}})
280280
{
281-
return global::Microsoft.AspNetCore.SignalR.Client.HubConnectionExtensions.InvokeCoreAsync{{method.CreateGenericReturnTypeArgumentString()}}(_connection, nameof({{method.MethodName}}), {{method.CreateArgumentsString()}}, _cancellationToken);
281+
return global::Microsoft.AspNetCore.SignalR.Client.HubConnectionExtensions.InvokeCoreAsync{{method.CreateGenericReturnTypeArgumentString()}}(_connection, "{{method.SignalRMethodName}}", {{method.CreateArgumentsString()}}, _cancellationToken);
282282
}
283283
""";
284284
}
@@ -288,7 +288,7 @@ private static string CreateServerStreamingMethodAsAsyncEnumerableString(MethodM
288288
return $$"""
289289
public {{method.ReturnType}} {{method.MethodName}}({{method.CreateParametersString()}})
290290
{
291-
return _connection.StreamAsyncCore{{method.CreateGenericReturnTypeArgumentString()}}(nameof({{method.MethodName}}), {{method.CreateArgumentsStringExceptCancellationToken(specialSymbols)}}, {{method.CreateCancellationTokenString("_cancellationToken", specialSymbols)}});
291+
return _connection.StreamAsyncCore{{method.CreateGenericReturnTypeArgumentString()}}("{{method.SignalRMethodName}}", {{method.CreateArgumentsStringExceptCancellationToken(specialSymbols)}}, {{method.CreateCancellationTokenString("_cancellationToken", specialSymbols)}});
292292
}
293293
""";
294294
}
@@ -298,7 +298,7 @@ private static string CreateServerStreamingMethodAsTaskAsyncEnumerableString(Met
298298
return $$"""
299299
public {{method.ReturnType}} {{method.MethodName}}({{method.CreateParametersString()}})
300300
{
301-
var ret = _connection.StreamAsyncCore{{method.CreateGenericReturnTypeArgumentStringForStreaming()}}(nameof({{method.MethodName}}), {{method.CreateArgumentsStringExceptCancellationToken(specialSymbols)}}, {{method.CreateCancellationTokenString("_cancellationToken", specialSymbols)}});
301+
var ret = _connection.StreamAsyncCore{{method.CreateGenericReturnTypeArgumentStringForStreaming()}}("{{method.SignalRMethodName}}", {{method.CreateArgumentsStringExceptCancellationToken(specialSymbols)}}, {{method.CreateCancellationTokenString("_cancellationToken", specialSymbols)}});
302302
return global::System.Threading.Tasks.Task.FromResult(ret);
303303
}
304304
""";
@@ -309,7 +309,7 @@ private static string CreateServerStreamingMethodAsChannelString(MethodMetadata
309309
return $$"""
310310
public {{method.ReturnType}} {{method.MethodName}}({{method.CreateParametersString()}})
311311
{
312-
return global::Microsoft.AspNetCore.SignalR.Client.HubConnectionExtensions.StreamAsChannelCoreAsync{{method.CreateGenericReturnTypeArgumentStringForStreaming()}}(_connection, nameof({{method.MethodName}}), {{method.CreateArgumentsStringExceptCancellationToken(specialSymbols)}}, {{method.CreateCancellationTokenString("_cancellationToken", specialSymbols)}});
312+
return global::Microsoft.AspNetCore.SignalR.Client.HubConnectionExtensions.StreamAsChannelCoreAsync{{method.CreateGenericReturnTypeArgumentStringForStreaming()}}(_connection, "{{method.SignalRMethodName}}", {{method.CreateArgumentsStringExceptCancellationToken(specialSymbols)}}, {{method.CreateCancellationTokenString("_cancellationToken", specialSymbols)}});
313313
}
314314
""";
315315
}
@@ -319,7 +319,7 @@ private static string CreateClientStreamingMethodAsAsyncEnumerableString(MethodM
319319
return $$"""
320320
public {{method.ReturnType}} {{method.MethodName}}({{method.CreateParametersString()}})
321321
{
322-
return _connection.SendCoreAsync(nameof({{method.MethodName}}), {{method.CreateArgumentsString()}}, _cancellationToken);
322+
return _connection.SendCoreAsync("{{method.SignalRMethodName}}", {{method.CreateArgumentsString()}}, _cancellationToken);
323323
}
324324
""";
325325
}
@@ -330,7 +330,7 @@ private static string CreateClientStreamingMethodAsChannelString(MethodMetadata
330330
331331
public {{method.ReturnType}} {{method.MethodName}}({{method.CreateParametersString()}})
332332
{
333-
return _connection.SendCoreAsync(nameof({{method.MethodName}}), {{method.CreateArgumentsString()}}, _cancellationToken);
333+
return _connection.SendCoreAsync("{{method.SignalRMethodName}}", {{method.CreateArgumentsString()}}, _cancellationToken);
334334
}
335335
""";
336336
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
using System;
2+
using System.Collections.Immutable;
3+
using System.Linq;
4+
using Microsoft.CodeAnalysis;
5+
using Microsoft.CodeAnalysis.CSharp;
6+
using Xunit;
7+
8+
namespace TypedSignalR.Client.SourceGeneratorTests;
9+
10+
using System.Collections.Generic;
11+
using global::TypedSignalR.Client;
12+
13+
public static class CompilationHelper
14+
{
15+
public static (ImmutableArray<Diagnostic> diagnostics, Dictionary<string, string> outputs) GetGeneratedOutput(string source)
16+
{
17+
const LanguageVersion LanguageVersion = LanguageVersion.CSharp12;
18+
19+
var generator = new SourceGenerator();
20+
21+
var sourceSyntaxTree = CSharpSyntaxTree.ParseText(source, path: "source.cs");
22+
var parsingOptions = new CSharpParseOptions(LanguageVersion);
23+
var sourceSyntaxTreeWithOptions = sourceSyntaxTree.WithRootAndOptions(sourceSyntaxTree.GetRoot(), parsingOptions);
24+
25+
var references = AppDomain.CurrentDomain.GetAssemblies()
26+
.Where(assembly => !assembly.IsDynamic && !string.IsNullOrWhiteSpace(assembly.Location))
27+
.Select(assembly => MetadataReference.CreateFromFile(assembly.Location))
28+
.Concat([
29+
MetadataReference.CreateFromFile(generator.GetType().Assembly.Location),
30+
MetadataReference.CreateFromFile(typeof(System.Threading.Tasks.Task).Assembly.Location),
31+
MetadataReference.CreateFromFile(typeof(Microsoft.AspNetCore.SignalR.HubMethodNameAttribute).Assembly.Location),
32+
MetadataReference.CreateFromFile(typeof(Microsoft.AspNetCore.SignalR.Client.HubConnection).Assembly.Location),
33+
MetadataReference.CreateFromFile(typeof(System.ComponentModel.DataAnnotations.DisplayAttribute).Assembly.Location),
34+
MetadataReference.CreateFromFile(typeof(System.CodeDom.Compiler.GeneratedCodeAttribute).Assembly.Location)
35+
]);
36+
37+
var compilation = CSharpCompilation.Create(
38+
"generator",
39+
[sourceSyntaxTreeWithOptions],
40+
references,
41+
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
42+
43+
44+
GeneratorDriver driver =
45+
CSharpGeneratorDriver.Create(
46+
[generator.AsSourceGenerator()],
47+
driverOptions: new GeneratorDriverOptions(
48+
disabledOutputs: IncrementalGeneratorOutputKind.None,
49+
trackIncrementalGeneratorSteps: true),
50+
optionsProvider: null,
51+
parseOptions: new CSharpParseOptions(LanguageVersion));
52+
53+
driver = driver.RunGenerators(compilation);
54+
55+
var runResult = driver.GetRunResult();
56+
57+
return (runResult.Diagnostics, runResult.Results.SelectMany(r => r.GeneratedSources).ToDictionary(s => s.HintName, s => s.SourceText.ToString()));
58+
}
59+
}

0 commit comments

Comments
 (0)