型付きHub
型付きHubインターフェースを定義し、クライアントメソッド呼び出しでコンパイル時の安全性とIntelliSenseを利用します。
「型付きHub」はCoddyKit上の無料C# Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはC# Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 C# Academyコースには全4レッスンが含まれています。
文字列によるメソッド名の問題
標準的な SignalR 呼び出しでは、マジック文字列を使って SendAsync("MethodName", ...) を呼び出します。メソッド名を誤記すると、静かに失敗します。つまり、クライアントはメッセージをまったく受信しません。強く型付けされたハブを使えば、この問題を解決できます。
クライアントインターフェースの定義
サーバーがクライアントで呼び出せるすべてのメソッドを宣言するインターフェースを作成します。メソッド名とパラメーター型が契約になります。
// INotificationClient.cs
public interface INotificationClient
{
Task ReceiveMessage(string user, string message);
Task OrderShipped(int orderId, string trackingNumber);
Task UserJoined(string username);
Task UserLeft(string username);
Task SystemAlert(string message);
}強く型付けされたハブの作成
クライアントインターフェースを T として、Hub<T> を継承します。これで Clients プロパティが型付けされ、IntelliSense に利用可能なメソッドが表示されるほか、誤記はコンパイルエラーになります。
public class ChatHub : Hub<INotificationClient>
{
public async Task SendMessage(string message)
{
var user = Context.User!.Identity!.Name ?? "Anonymous";
// Compile-time safety — no magic strings!
await Clients.All.ReceiveMessage(user, message);
await Clients.Others.UserJoined(user); // IntelliSense works
}
}型付きハブで特定のクライアントを対象にする
標準的な対象指定メソッドはすべて型付きハブで使用できます。Clients.User、Clients.Group、Clients.Caller、Clients.Client は、いずれも型付きインターフェースを返します。
public async Task NotifyOrderShipped(int orderId, string customerId)
{
await Clients.User(customerId)
.OrderShipped(orderId, "TRK-12345");
await Clients.Group("admins")
.SystemAlert($"Order {orderId} shipped to {customerId}");
await Clients.Caller.ReceiveMessage("System", "Notification sent");
}型付き IHubContext
サービスに IHubContext<THub, TClient> を注入すると、ハブの外部からメッセージをプッシュする場合にも、同じコンパイル時の安全性を得られます。
public class ShipmentService
{
private readonly IHubContext<ChatHub, INotificationClient> _hub;
public ShipmentService(
IHubContext<ChatHub, INotificationClient> hub) => _hub = hub;
public async Task ProcessShipmentAsync(int orderId, string customerId)
{
// Fully typed — no magic strings
await _hub.Clients.User(customerId)
.OrderShipped(orderId, "TRK-99999");
}
}クライアントインターフェースのルール
クライアントインターフェースのメソッドは Task を返す必要があり、Task<T> は返せません。SignalR はクライアント呼び出しを fire-and-forget で実行するため、クライアントからサーバーへの戻り値はありません。
// CORRECT
public interface IMyClient
{
Task ReceiveMessage(string msg); // OK
Task UpdateProgress(int percent); // OK
Task<string> GetUserInput(); // NOT SUPPORTED
}
// Return types other than Task are not supported in hub client interfaces
// For request-response patterns, use client-callable hub methods instead型付きハブとグループの組み合わせ
型付きグループは型なしのグループとまったく同じように動作します。API は同じで、コンパイル時にメソッド名が検証される点だけが異なります。
public class RoomHub : Hub<IRoomClient>
{
public async Task JoinRoom(string room)
{
await Groups.AddToGroupAsync(Context.ConnectionId, room);
await Clients.Group(room).UserJoined(Context.User!.Identity!.Name!);
}
public async Task BroadcastToRoom(string room, string message)
{
var user = Context.User!.Identity!.Name!;
await Clients.Group(room).ReceiveMessage(user, message);
}
}強く型付けされたハブのテスト
クライアントメソッドの呼び出しはインターフェース経由で行われるため、単体テストでクライアントをモックし、正しいメソッドが正しい引数で呼び出されたことを検証できます。
// NSubstitute test
var mockClients = Substitute.For<IHubCallerClients<INotificationClient>>();
var mockAllClients = Substitute.For<INotificationClient>();
mockClients.All.Returns(mockAllClients);
var hub = new ChatHub { Clients = mockClients };
await hub.SendMessage("Hello!");
await mockAllClients.Received(1).ReceiveMessage("System", "Hello!");アプリケーション内の複数のハブ
1つのアプリケーションに、異なるリアルタイム機能用の複数のハブエンドポイントを配置できます。それぞれに独自の型付きクライアントインターフェースを持たせることもできます。
// Three separate hubs
app.MapHub<ChatHub>("/hubs/chat");
app.MapHub<NotificationHub>("/hubs/notifications");
app.MapHub<DashboardHub>("/hubs/dashboard");
// Each with its own client interface:
// Hub<IChatClient>, Hub<INotificationClient>, Hub<IDashboardClient>構造化データの送信
クライアントインターフェースのメソッドでは、複雑な DTO を受け取ることもできます。SignalR は DTO を自動的に JSON にシリアライズするため、型がシリアライズ可能であることを確認してください。
public interface IOrderClient
{
Task OrderUpdated(OrderStatusDto status);
}
public record OrderStatusDto(
int OrderId,
string Status,
decimal Total,
DateTime UpdatedAt);
// Usage in hub:
await Clients.User(customerId).OrderUpdated(
new OrderStatusDto(order.Id, order.Status.ToString(),
order.Total, DateTime.UtcNow));確認問題
SignalR ハブのクライアントインターフェースにあるメソッドの戻り値型には、どのような制限がありますか?
まとめ:強く型付けされたハブ
主なポイント:
- サーバーがクライアントで呼び出せるすべてのメソッドを列挙するインターフェース(IMyClient)を定義します
Hub<IMyClient>を継承すると、Clients プロパティが完全に型付けされます- ハブの外部から型付きでプッシュするには、
IHubContext<THub, TClient>を注入します - クライアントインターフェースのメソッドは
Taskを返す必要があり、Task<T>は返せません - インターフェースベースのクライアントはモック化できるため、強く型付けされたハブはテストが容易です
よくある質問
「型付きHub」レッスンは無料ですか?
はい。「型付きHub」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、C# Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 C# Academyコースには全4レッスンが含まれています。
「型付きHub」で何を学びますか?
型付きHubインターフェースを定義し、クライアントメソッド呼び出しでコンパイル時の安全性とIntelliSenseを利用します。 ブラウザで直接実行するハンズオンコードでC# Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
C# Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのC# Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。
「型付きHub」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このC# Academyレッスンでコードを書いて実行できますか?
はい。すべてのC# Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。