Строго типизированные центры
Определяйте типизированные интерфейсы центров, чтобы получать безопасность на этапе компиляции и IntelliSense для вызовов методов клиента.
«Строго типизированные центры» — бесплатный урок C# Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения 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);
}Создание строго типизированного хаба
Унаследуйте хаб от Hub<T>, где 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 запускает вызовы клиента без ожидания результата — клиент ничего не возвращает серверу.
// 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!");Несколько хабов в одном приложении
В приложении может быть несколько конечных точек хабов для разных функций реального времени, и у каждой может быть собственный строго типизированный интерфейс клиента.
// 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 автоматически сериализует их в 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> - Клиенты на основе интерфейсов можно имитировать — строго типизированные хабы проще тестировать
Часто задаваемые вопросы
Урок «Строго типизированные центры» бесплатный?
Да — полный текст урока «Строго типизированные центры» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс C# Academy, подпишись на CoddyKit PRO. Курс C# Academy содержит 4 уроков всего.
Чему я научусь в уроке «Строго типизированные центры»?
Определяйте типизированные интерфейсы центров, чтобы получать безопасность на этапе компиляции и IntelliSense для вызовов методов клиента. Ты практикуешь C# Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать C# Academy?
Предыдущий опыт не требуется. C# Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.
Сколько времени занимает урок «Строго типизированные центры»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке C# Academy?
Да. Каждый урок C# Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Центры и подключения SignalR
- Группы, пользователи и управление подключениями
- Строго типизированные центры
- Масштабирование с помощью Redis Backplane