All Projects → nenoNaninu → TypedSignalR.Client

nenoNaninu / TypedSignalR.Client

Licence: MIT License
C# Source Generator to Create Strongly Typed SignalR Client.

Programming Languages

C#
18002 projects
HTML
75241 projects

Projects that are alternatives of or similar to TypedSignalR.Client

SignalR-Core-SqlTableDependency
Shows how the new SignalR Core works with hubs and sockets, also how it can integrate with SqlTableDependency API.
Stars: ✭ 36 (+125%)
Mutual labels:  signalr, signalr-client, signalr-core
ngrx-signalr-core
A library to handle realtime SignalR (.NET Core) events using @angular, rxjs and the @ngrx library
Stars: ✭ 18 (+12.5%)
Mutual labels:  signalr, signalr-core
Notify.Me
Simple host application to provide send/receive feature for any kind of notifications and messages between client(s) and the host. The application is based on ASP.NET Core and SignalR to demostrate some features of these things...
Stars: ✭ 28 (+75%)
Mutual labels:  signalr, signalr-core
signalr-client
SignalR client library built on top of @aspnet/signalr. This gives you more features and easier to use.
Stars: ✭ 48 (+200%)
Mutual labels:  signalr, signalr-client
signalr
SignalR server and client in go
Stars: ✭ 69 (+331.25%)
Mutual labels:  signalr, signalr-client
AngularAspNetCoreSignalR
Build a simple chat app with Angular and ASP.NET Core SignalR
Stars: ✭ 12 (-25%)
Mutual labels:  signalr
GodotOnReady
A C# Source Generator that adds convenient onready-like features to your C# scripts in Godot Mono without any reflection.
Stars: ✭ 73 (+356.25%)
Mutual labels:  csharp-sourcegenerator
Chatazon
Implementing websockets in .NET Core
Stars: ✭ 19 (+18.75%)
Mutual labels:  signalr
Foundatio.Samples
Foundatio Samples
Stars: ✭ 34 (+112.5%)
Mutual labels:  signalr
vscode-csharp-snippets
Extension of C# Snippets for Visual Studio Code
Stars: ✭ 22 (+37.5%)
Mutual labels:  signalr-core
Plastic
This project provides encapsulation of things like Domain, Application Rules, Business Rules or Business Logic in Application.
Stars: ✭ 30 (+87.5%)
Mutual labels:  csharp-sourcegenerator
python-bittrex-websocket-aio
Python websocket for Bittrex (async).
Stars: ✭ 35 (+118.75%)
Mutual labels:  signalr-client
EmbeddingResourceCSharp
Embed resource files more C# programmer friendly!
Stars: ✭ 22 (+37.5%)
Mutual labels:  csharp-sourcegenerator
Achievements
A micro-service to listen to Azure Service bus for "Achievement Unlocked Events", process them, and emit a message to listening clients via SignalR
Stars: ✭ 13 (-18.75%)
Mutual labels:  signalr
WinFormsComInterop
ComWrappers required to run NativeAOT and WinForms
Stars: ✭ 54 (+237.5%)
Mutual labels:  csharp-sourcegenerator
GraphQL.Tools
GraphQL.Tools is a GraphQL to C# compiler (code-generator) which turns your GraphQL schema into a set of C# classes, interfaces, and enums.
Stars: ✭ 49 (+206.25%)
Mutual labels:  csharp-sourcegenerator
JsonSrcGen
Json library that uses .NET 5 Source Generators
Stars: ✭ 140 (+775%)
Mutual labels:  csharp-sourcegenerator
aspnet-core-react-redux-playground-template
SPA template built with ASP.NET Core 6.0 + React + Redux + TypeScript + Hot Module Replacement (HMR)
Stars: ✭ 78 (+387.5%)
Mutual labels:  signalr
WeChatMiniAppSignalRClient
微信小程序 Asp.net Core SignalR Client 代码片段演示
Stars: ✭ 27 (+68.75%)
Mutual labels:  signalr
PlanningPoker
A demo application of AspNetCore and SignalR
Stars: ✭ 20 (+25%)
Mutual labels:  signalr-core

TypedSignalR.Client

build-and-test

C# Source Generator to create strongly typed SignalR client.

Table of Contents

Install

NuGet: TypedSignalR.Client

dotnet add package Microsoft.AspNetCore.SignalR.Client
dotnet add package TypedSignalR.Client

Why TypedSignalR.Client?

The pure C# SignalR client is untyped. To call a Hub (server-side) function, we must specify the function defined in Hub using a string. We also have to determine the return type manually. Moreover, registering a client function called from the server also requires a string, and we must set the parameter types manually.

// Pure SignalR Client

// Specify the hub method to invoke using string.
await connection.InvokeAsync("HubMethod1");

// Manually determine the return type.
// The parameter is cast to object type.
var guid = await connection.InvokeAsync<Guid>("HubMethod2", "message", 99);

// Registering a client function requires a string, 
// and the parameter types must be set manually.
var subscription = connection.On<string, DateTime>("ClientMethod", (message, dateTime) => {});

Therefore, if we change the code on the server-side, the modification on the client-side becomes very troublesome. The leading cause is that it is not strongly typed.

TypedSignalR.Client aims to generate a strongly typed SignalR client by sharing interfaces in which the server and client functions are defined. Defining interfaces are helpful not only for the client-side but also for the server-side. See Usage section for details.

// TypedSignalR.Client

// First, create a hub proxy.
IHub hubProxy = connection.CreateHubProxy<IHub>();

// Invoke a hub method through hub proxy.
// We no longer need to specify the function using a string.
await hubProxy.HubMethod1();

// Both parameters and return types are strongly typed.
var guid = await hubProxy.HubMethod2("message", 99);

// The client's function registration is also strongly typed, so it's safe and easy.
var subscription = connection.Register<IReceiver>(new Receiver());

// Defining interfaces are useful not only for the client-side but also for the server-side.
// See Usage in this README.md for details.
interface IHub
{
    Task HubMethod1();
    Task<Guid> HubMethod2(string message, int value);
}

interface IReceiver
{
    Task ClientMethod(string message, DateTime dateTime);
}

class Receiver : IReceiver
{
    ...
}

API

This Source Generator provides two extension methods and one interface.

static class HubConnectionExtensions
{
    THub CreateHubProxy<THub>(this HubConnection connection, CancellationToken cancellationToken = default){...}
    IDisposable Register<TReceiver>(this HubConnection connection, TReceiver receiver){...}
}

// An interface for observing SignalR events.
interface IHubConnectionObserver
{
    Task OnClosed(Exception? exception);
    Task OnReconnected(string? connectionId);
    Task OnReconnecting(Exception? exception);
}

Use it as follows.

HubConnection connection = ...;

IHub hub = connection.CreateHubProxy<IHub>();
IDisposable subscription = connection.Register<IReceiver>(new Receiver());

Usage

For example, we have the following interface defined.

public class UserDefinedType
{
    public Guid Id { get; set; }
    public DateTime Datetime { get; set; }
}

// The return type of methods on the client-side must be Task. 
public interface IClientContract
{
    // Of course, user defined type is OK. 
    Task ClientMethod1(string user, string message, UserDefinedType userDefine);
    Task ClientMethod2();
}

// The return type of methods on the hub-side must be Task or Task<T>. 
public interface IHubContract
{
    Task<string> HubMethod1(string user, string message);
    Task HubMethod2();
}

class Receiver1 : IClientContract
{
    // implementation
}

class Receiver2 : IClientContract, IHubConnectionObserver
{
    // implementation
}

Client

It's very easy to use.

HubConnection connection = ...;

var hub = connection.CreateHubProxy<IHubContract>();
var subscription1 = connection.Register<IClientContract>(new Receiver1());

// When an instance of a class that implements IHubConnectionObserver is registered (Receiver2 in this case), 
// the method defined in IHubConnectionObserver is automatically registered regardless of the type argument. 
var subscription2 = connection.Register<IClientContract>(new Receiver2());

// Invoke hub methods
hub.HubMethod1("user", "message");

// Unregister the receiver
subscription.Dispose();

Cancellation

In pure SignalR, CancellationToken is passed for each invoke.

On the other hand, in TypedSignalR.Client, CancellationToken is passed only once when creating hub proxy. The passed CancelationToken will be used for each invoke internally.

var cts = new CancellationTokenSource();

// The following two are equivalent.

// 1: Pure SignalR
var ret =  await connection.InvokeAsync<string>("HubMethod1", "user", "message", cts.Token);
await connection.InvokeAsync("HubMethod2", cts.Token);

// 2: TypedSignalR.Client
var hubProxy = connection.CreateHubProxy<IHubContract>(cts.Token);
var ret = await hubProxy.HubMethod1("user", "message");
await hubProxy.HubMethod2();

Server

Using the interface definitions, we can write as follows on the server-side (ASP.NET Core). TypedSignalR.Client is not nessesary.

using Microsoft.AspNetCore.SignalR;

public class SomeHub : Hub<IClientContract>, IHubContract
{
    public async Task<string> HubMethod1(string user, string message)
    {
        var instance = new UserDefinedType()
        {
            Id = Guid.NewGuid(),
            DateTime = DateTime.Now,
        };

        // broadcast
        await this.Clients.All.ClientMethod1(user, message, instance);
        return "OK!";
    }

    public async Task HubMethod2()
    {
        await this.Clients.Caller.ClientMethod2();
    }
}

Recommendation

Sharing a Project

I recommend that these interfaces be shared between the client-side and server-side project, for example, by project references.

server.csproj --> shared.csproj <-- client.csproj

Client Code Format

It is easier to handle if we write the client code in the following format.

class Client : IReceiver, IHubConnectionObserver, IDisposable
{
    private readonly IHub _hubProxy;
    private readonly IDisposable _subscription;
    private readonly CancellationTokenSource _cancellationTokenSource = new();

    public Client(HubConnection connection)
    {
        _hubProxy = connection.CreateHubProxy<IHub>(_cancellationTokenSource.Token);
        _subscription = connection.Register<IReceiver>(this);
    }

    // implementation
}

Compile-Time Error Support

This library has some restrictions, including those that come from server-side implementations.

  • Type argument of the CreateHubProxy/Register method must be an interface.
  • Only method definitions are allowed in the interface used for CreateHubProxy/Register.
    • It is forbidden to define properties and events.
  • The return type of the method in the interface used for CreateHubProxy must be Task or Task<T>.
  • The return type of the method in the interface used for Register must be Task.

It is complicated for humans to comply with these restrictions properly. So, this library looks for parts that do not follow the restriction and report detailed errors at compile-time. Therefore, no run-time error occurs.

compile-time-error

Generated Source Code

TypedSignalR.Client checks the type argument of a methods CreateHubProxy and Register and generates source code. Generated source code can be seen in Visual Studio.

generated-code-visible-from-solution-explorer

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].