Route gh-aw workflow design/create/debug/upgrade requests to the right prompts.
日本語の概要は準備中です。原文の説明を表示しています。
Implement or review SignalR hubs, streaming, reconnection, transport, and real-time delivery patterns in ASP.NET Core applications. USE FOR: building chat, notification, collaboration, or live-update features; debugging hub lifetime, connection state, or transport issues; deciding whether SignalR or another. DO NOT USE FOR: unrelated stacks; generic tasks that do not need this specific guidance. INVOKES: inspect the repository context, edit targeted files, and run relevant build, test, lint, or validation commands when changes are made.
インストール方法を見るインストールする前に、エージェントに与えられる指示の中身を確認できます。
dotnet/aspnetcore v10.0.11 is a servicing release and does not change the SignalR programming model. Keep guidance focused on hub contract design, reconnection, transport, authorization, and scale-out validation.// Define the client interface
public interface IChatClient
{
Task ReceiveMessage(string user, string message);
Task UserJoined(string user);
Task UserLeft(string user);
}
// Implement the strongly-typed hub
public class ChatHub : Hub<IChatClient>
{
public async Task SendMessage(string user, string message)
{
// Compiler checks client method calls
await Clients.All.ReceiveMessage(user, message);
}
public override async Task OnConnectedAsync()
{
await Clients.Others.UserJoined(Context.User?.Identity?.Name ?? "Anonymous");
await base.OnConnectedAsync();
}
public override async Task OnDisconnectedAsync(Exception? exception)
{
await Clients.Others.UserLeft(Context.User?.Identity?.Name ?? "Anonymous");
await base.OnDisconnectedAsync(exception);
}
}
public class NotificationHub : Hub<INotificationClient>
{
public async Task JoinGroup(string groupName)
{
await Groups.AddToGroupAsync(Context.ConnectionId, groupName);
await Clients.Group(groupName).UserJoined(Context.User?.Identity?.Name);
}
public async Task LeaveGroup(string groupName)
{
await Groups.RemoveFromGroupAsync(Context.ConnectionId, groupName);
}
public async Task SendToGroup(string groupName, string message)
{
await Clients.Group(groupName).ReceiveNotification(message);
}
}
// Use custom objects to avoid breaking changes
public class SendMessageRequest
{
public string Message { get; set; } = string.Empty;
public string? Recipient { get; set; } // Added later without breaking clients
public int? Priority { get; set; } // Added later without breaking clients
}
public class ChatHub : Hub<IChatClient>
{
public async Task SendMessage(SendMessageRequest request)
{
// Handle both old and new clients
if (request.Recipient != null)
{
await Clients.User(request.Recipient).ReceiveMessage(request.Message);
}
else
{
await Clients.All.ReceiveMessage(request.Message);
}
}
}
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chatHub")
.withAutomaticReconnect([0, 2000, 5000, 10000, 30000]) // Retry delays
.configureLogging(signalR.LogLevel.Information)
.build();
// Handle reconnection events
connection.onreconnecting(error => {
console.log("Reconnecting...", error);
updateUIForReconnecting();
});
connection.onreconnected(connectionId => {
console.log("Reconnected with ID:", connectionId);
// Rejoin groups - reconnection does not restore group membership
rejoinGroups();
updateUIForConnected();
});
connection.onclose(error => {
console.log("Connection closed", error);
updateUIForDisconnected();
});
async function start() {
try {
await connection.start();
console.log("SignalR Connected");
} catch (err) {
console.log(err);
setTimeout(start, 5000);
}
}
start();
var connection = new HubConnectionBuilder()
.WithUrl("https://localhost:5001/chatHub", options =>
{
options.AccessTokenProvider = () => Task.FromResult(GetAccessToken());
})
.WithAutomaticReconnect()
.Build();
connection.Reconnecting += error =>
{
_logger.LogWarning("Connection lost. Reconnecting: {Error}", error?.Message);
return Task.CompletedTask;
};
connection.Reconnected += connectionId =>
{
_logger.LogInformation("Reconnected with ID: {ConnectionId}", connectionId);
// Rejoin groups after reconnection
return RejoinGroupsAsync();
};
connection.Closed += async error =>
{
_logger.LogError("Connection closed: {Error}", error?.Message);
await Task.Delay(Random.Shared.Next(0, 5) * 1000);
await connection.StartAsync();
};
await connection.StartAsync();
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSignalR(options =>
{
options.EnableDetailedErrors = builder.Environment.IsDevelopment();
options.MaximumReceiveMessageSize = 64 * 1024; // 64 KB
options.StreamBufferCapacity = 10;
options.KeepAliveInterval = TimeSpan.FromSeconds(15);
options.ClientTimeoutInterval = TimeSpan.FromSeconds(30);
})
.AddMessagePackProtocol(); // Binary protocol for performance
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
// Read token from query string for WebSocket connections
var accessToken = context.Request.Query["access_token"];
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/hubs"))
{
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
});
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapHub<ChatHub>("/hubs/chat");
public class NotificationService
{
private readonly IHubContext<NotificationHub, INotificationClient> _hubContext;
public NotificationService(IHubContext<NotificationHub, INotificationClient> hubContext)
{
_hubContext = hubContext;
}
public async Task NotifyAllAsync(string message)
{
await _hubContext.Clients.All.ReceiveNotification(message);
}
public async Task NotifyUserAsync(string userId, string message)
{
await _hubContext.Clients.User(userId).ReceiveNotification(message);
}
public async Task NotifyGroupAsync(string groupName, string message)
{
await _hubContext.Clients.Group(groupName).ReceiveNotification(message);
}
}
builder.Services.AddSignalR()
.AddStackExchangeRedis(connectionString, options =>
{
options.Configuration.ChannelPrefix = RedisChannel.Literal("MyApp");
});
| Anti-Pattern | Why It's Bad | Better Approach |
|---|---|---|
| Storing state in Hub properties | Hub instances are created per method call | Use IMemoryCache, database, or external store |
| Instantiating Hub directly | Bypasses SignalR infrastructure | Use IHubContext<THub> for external messaging |
Not awaiting SendAsync calls | Messages may not be sent before hub method completes | Always await async hub calls |
| Adding method parameters without versioning | Breaking change for existing clients | Use custom object parameters |
| Ignoring reconnection group loss | Clients lose group membership on reconnect | Re-add to groups in OnConnectedAsync or client reconnect handler |
| Large payloads over SignalR | Memory pressure, bandwidth issues | Use REST/gRPC for bulk data, SignalR for notifications |
| Missing backplane in multi-server | Messages only reach clients on same server | Use Redis backplane or Azure SignalR Service |
| Exposing ORM entities directly | May serialize sensitive data | Use DTOs with explicit properties |
| Not validating incoming messages | Security risk after initial auth | Validate every hub method input |
[Authorize] attributeChatHubV2) for breaking changesまだレビューはありません。使ってみた感想をお寄せください。
概要と使いどころ
Route gh-aw workflow design/create/debug/upgrade requests to the right prompts.
日本語の概要は準備中です。原文の説明を表示しています。
Use a repo-root `.editorconfig` to configure free .NET analyzer and style rules. Use when a .NET repo needs rule severity, code-style options, section layout, or analyzer ownership made explicit. USE FOR: the repo needs a root .editorconfig; analyzer severity and style ownership are unclear; the team wants one source of truth for rule configuration. DO NOT USE FOR: choosing analyzers with no config change; formatting-only execution with no config ownership question. INVOKES: inspect the repository context, edit targeted files, and run relevant build, test, lint, or validation commands when changes are made.
日本語の概要は準備中です。原文の説明を表示しています。
Scans .NET code for ~50 performance anti-patterns across async, memory, strings, collections, LINQ, regex, serialization, and I/O with tiered severity classification. Use when analyzing .NET code for optimization opportunities, reviewing hot paths, or auditing allocation-heavy patterns.
日本語の概要は準備中です。原文の説明を表示しています。
Symbolicate the .NET runtime frames in an Android tombstone file. Extracts BuildIds and PC offsets from the native backtrace, downloads debug symbols from the Microsoft symbol server, and runs llvm-symbolizer to produce function names with source file and line numbers. USE FOR triaging a .NET MAUI or Mono Android app crash from a tombstone, resolving native backtrace frames in libmonosgen-2.0.so or libcoreclr.so to .NET runtime source code, or investigating SIGABRT, SIGSEGV, or other native signals originating from the .NET runtime on Android. DO NOT USE FOR pure Java/Kotlin crashes, managed .NET exceptions that are already captured in logcat, or iOS crash logs. INVOKES Symbolicate-Tombstone.ps1 script, llvm-symbolizer, Microsoft symbol server.
日本語の概要は準備中です。原文の説明を表示しています。
Symbolicate .NET runtime frames in Apple platform .ips crash logs (iOS, tvOS, Mac Catalyst, macOS). Extracts UUIDs and addresses from the native backtrace, locates dSYM debug symbols, and runs atos to produce function names with source file and line numbers. Automatically downloads .dwarf symbols from the Microsoft symbol server using Mach-O UUIDs. USE FOR triaging a .NET MAUI or Mono app crash from an .ips file on any Apple platform, resolving native backtrace frames in libcoreclr or libmonosgen-2.0 to .NET runtime source code, retrieving .ips crash logs from a connected iOS device or iPhone, or investigating EXC_CRASH, EXC_BAD_ACCESS, SIGABRT, or SIGSEGV originating from the .NET runtime. DO NOT USE FOR pure Swift/Objective-C crashes with no .NET components, or Android tombstone files. INVOKES Symbolicate-Crash.ps1 script, atos, dwarfdump, idevicecrashreport.
日本語の概要は準備中です。原文の説明を表示しています。
Design or review .NET solution architecture across modular monoliths, clean architecture, vertical slices, microservices, DDD, CQRS, and cloud-native boundaries without over-engineering. USE FOR: .NET architecture choices; layer and domain boundary review; service decomposition; clean architecture, vertical slice, DDD, CQRS, and modular monolith decisions. DO NOT USE FOR: unrelated stacks; generic tasks that do not need this specific guidance. INVOKES: inspect the repository context, edit targeted files, and run relevant build, test, lint, or validation commands when changes are made.
日本語の概要は準備中です。原文の説明を表示しています。