csharp-signalrlisted
Install: claude install-skill CloudyWing/ai-dotfiles
# SignalR Hub 開發規範
## Hub Lifetime(Crucial)
- Hub 實例為 **Transient**:每次用戶端呼叫 Hub 方法時,都會建立新的 Hub 實例。
- **禁止**在 Hub 中儲存狀態(instance field)。跨呼叫的狀態必須使用外部儲存(如 Redis、資料庫或 `IMemoryCache`)。
- Hub 可透過建構函式注入 Scoped 與 Transient 服務。注入 Singleton 服務時需注意執行緒安全。
```csharp
// ❌ 錯誤:在 Hub 中儲存狀態
public class ChatHub : Hub {
private readonly List<string> messages = []; // 每次呼叫都是新實例,此欄位無用
public async Task SendMessage(string message) {
messages.Add(message); // 永遠只有一筆
}
}
// ✅ 正確:使用外部服務管理狀態
public class ChatHub : Hub {
private readonly IChatService chatService;
public ChatHub(IChatService chatService) {
this.chatService = chatService;
}
public async Task SendMessage(string message) {
await chatService.SaveMessageAsync(message).ConfigureAwait(false);
await Clients.All.SendAsync("ReceiveMessage", message).ConfigureAwait(false);
}
}
```
## Hub 設計原則
### 方法命名
- Hub 方法(Server-side)使用 PascalCase,與 C# 方法慣例一致。
- 用戶端接收的事件名稱(`SendAsync` 的第一個參數)使用 PascalCase,與前端 `on` 方法對應。
- 命名應明確表達動作語意(如 `SendMessage`、`JoinRoom`、`LeaveRoom`)。
### 回傳值
```csharp
// ✅ Hub 方法可以有回傳值(用戶端可 await 取得結果)
public async Task<IReadOnlyList<MessageDto>> GetRecentMessages(string roomId) {
return await chatService.GetRecentMessagesAsync(roomId).ConfigureAwait(false);
}
```
### 強型別 Hub(推薦)
使用介面定義用戶端方法,獲得編譯時期型別檢查。
```csharp
public interface IChatClient {
Task ReceiveMessage(string user, string message);
Task UserJoined(string user);
Task UserLeft(string