I have inquired a lot about ASP.NETCore uses simple examples of SignalR, but most of them are simple chat functions. Today, on a whim, I made a simple DEMO using SignalR to call between services.

As for what SignalR is, I will not say, Microsoft official documentation is also quite a few.


Step 1 Create a new project

The first step in all VS development is to create a new solution, so I won’t go into how to create a new project

  • Development environment, VS2017,.NET CORE 2.1
  • Create two NEW ASP.NETThe core project

    It’s such a simple operation that everyone knows

Injection SignalR

Asp.net core2.1 already includes the SignalR library by default. Add the following code to the ConfigureServices method:

services.AddSignalR().AddMessagePackProtocol()

AddMessagePackProtocol() enables MessagePack support on the server

Create a new class that inherits the Hub class

public class ServerHub : Hub
{
    
}
Copy the code

It can implement methods that need to be executed by the client.

The URL of the Hub connection is configured

Add the following code to Configure in startup. cs

    app.UseSignalR(routes =>
    {
        routes.MapHub<ServerHub>("/myserver");
    });
Copy the code

Add the service

Create a new Services folder and add our service interface and interface implementation classes.

public interface IMyService
{
    Task<string> SayHello();
    Task Sleep();
}
Copy the code
public class MyService : IMyService
{
    public async Task<string> SayHello()
    {
        return await Task.Factory.StartNew(() => "Hello");
    }

    public async Task Sleep() { await Task.Delay(3000); }}Copy the code

Do dependency injection in the ConfigureServices method of startup. cs before AddSignalR().

services.AddScoped<IMyService, MyService>();

Add dependency injection to the ServerHub

Add the following code to the ServerHub

    private readonly IServiceProvider _serviceProvider;
    public ServerHub(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }
Copy the code

Because we need to use dependency injection to get the specified Service, we only inject IServiceProvider and use IServiceProvider to dynamically get the Service interface.

Use reflection to dynamically get the service interface and execute the specified method

In order to dynamically select a service and execute its corresponding method, we use reflection to dynamically fetch. Here we add two methods, one with a return value and one with no return value

    public async Task<dynamic> Excute(string serverName,string functionName,object[] parameters)
    {
        return await Task.Factory.StartNew(() =>
        {
            var type = Type.GetType(serverName);
            var service = _serviceProvider.GetRequiredService(type);
            var method = type.GetMethod(functionName);
            var resultTask = method.Invoke(service, new object[] { }) as Task;
            dynamic result = resultTask.GetType().GetProperty("Result").GetValue(resultTask, null);
            return result;
        });
    }

    public async Task ExcuteWithoutResult(string serverName, string functionName, object[] parameters)
    {
        var type = Type.GetType(serverName);
        var service = _serviceProvider.GetRequiredService(type);
        var method = type.GetMethod(functionName);
        var resultTask = method.Invoke(service, new object[] { }) as Task;
        await resultTask;
        var msg = "task done";
        await Clients.Caller.SendAsync("callback",msg);
    }
Copy the code

Three arguments are passed to the method

  • ServerName: Service interface name (full namespace)
  • FunctionName: Name of the method
  • Parameters: the parameters required by the method

  1. useType.GetType(serverName)Gets the Type of the service interface.
  2. use_serviceProvider.GetRequiredService(type)Get the corresponding service from dependency injection.
  3. usetype.GetMethod(functionName)Gets the method that needs to be executed.
  4. Execution methodmethod.Invoke(service, new object[] { })

Since our service is an asynchronous method, method.invoke () returns an object, so we are changing the method.invoke () return type to Task.

Resulttask.gettype ().getProperty (“Result”).getValue (resultTask, null) is used to retrieve the Result attribute of the Task by reflection and return the corresponding Result.

Since we don’t know what type of result is returned in the Task returned by the method, we still use reflection to get the result of the Task and dynamic to receive it.


If there is no return value, we use client.caller.sendAsync () to return a message to the Caller at the end of the task processing.


Add a SingalRClient class to the caller

    public class SignalRClient
    {
        private readonly HubConnection connection;
        public SignalRClient()
        {
            connection = new HubConnectionBuilder()
                   .WithUrl("http://localhost:5000/myserver")
                   .AddMessagePackProtocol()
                   .Build();

            connection.Closed += async (error) =>
            {
                await Task.Delay(new Random().Next(0, 5) * 1000);
                await connection.StartAsync();
            };
            InitOnMethod();
            connection.StartAsync().ConfigureAwait(false).GetAwaiter().GetResult(); }}Copy the code

Initialize the SignalR connection in the constructor

WithUrl (” http://localhost:5000/myserver “) is the connection is the caller’s URL

AddMessagePackProtocol() is a fast and compact binary serialization format for transport. Add a connection closure event to connection.Closed and the connection will be reconnected automatically. InitOnMethod initializes the listener event called back by the server

    private void InitOnMethod()
    {
        connection.On<string>("callback",(msg)=> {
            Console.WriteLine($"------------{msg}----------");
        });
    }
Copy the code

Connection.startasync () starts a connection.

Add two methods that request the server

One has a return value and one has no return value.

    public async Task<dynamic> RequestServer1()
    {
        var result = await connection.InvokeAsync<dynamic>("Excute"."SignalRServer1.Services.IMyService"."SayHello",new object[] { });
        
        return result;
    }

    public async Task RequestWithoutResult()
    {
        await connection.SendAsync("ExcuteWithoutResult"."SignalRServer1.Services.IMyService"."Sleep", new object[] { });
    }
Copy the code

To return a value, we use the connection.invokeasync () method

Where no return value is required, we use the connection.sendAsync () method

SignalRClient is registered as a singleton for dependency injection

Add services.addSingleton

() to the ConfigureServices method in startup. cs.

Use SignalRClient to request the service

Inject SignalRClient into the controller

    private readonly SignalRClient _signalRClient;

    public ValuesController(SignalRClient signalRClient)
    {
        _signalRClient = signalRClient;
    }
    // GET api/values
    [HttpGet]
    public async Task<ActionResult<IEnumerable<string>>> Get()
    {
        var str = await _signalRClient.RequestServer1().ConfigureAwait(false);
        await _signalRClient.RequestWithoutResult().ConfigureAwait(false);
        return new string[] { str };
    }
Copy the code

Call both a method with a return value and a method with no return value in the request. Methods with no return value perform a callback after the task is completed.


Start the service

        connection.On<string>("callback",(msg)=> {
            Console.WriteLine($"------------{msg}----------");
        });
Copy the code

End!!

The above is a simple DEMO I do. If there are deficiencies, please give us more advice. I haven’t written any articles, please forgive me if I don’t write well haha ~~~