问题
In my Startup.Auth.cs:  
private static void ConfigSignalR(IAppBuilder appBuilder)
{
    appBuilder.MapSignalR();
    var idProvider = new PrincipalUserIdProvider();
    GlobalHost.DependencyResolver.Register(typeof(IUserIdProvider), () => idProvider);
}
My UserHub.cs:
public class UserHub : Hub
{
}
On the server-side, in one of my API Controller action (a Put related to a Grid Update):
[...]
var userHub = GlobalHost.ConnectionManager.GetHubContext<UserHub>();
// Line below does not work
// userHub.Clients.User(userId).send("Hi");
// But this line below works when sending the message to everybody
userHub.Clients.All.send("Hi");
return Request.CreateResponse(HttpStatusCode.OK);
On the JS View client-side:
@Request.IsAuthenticated
{
    <script>
        $(function() {
            var userHub = $.connection.userHub;
            console.log(userHub.client);
            userHub.client.send = function(message) {
                alert('received: ' + message);
            };
            $.connection.hub.start().done(function() {
            });
        });
    </script>
}
Why when passing the userId my client receives nothing?
(also tried passing the userName, with the same outcome).
[EDIT]
Technically the right way to achieve that is to leverage the implementation of the IUserIdProvider:  
- https://docs.microsoft.com/en-us/aspnet/signalr/overview/guide-to-the-api/mapping-users-to-connections#IUserIdProvider
- SignalR - Sending a message to a specific user using (IUserIdProvider) *NEW 2.0.0*
However, I've noticed that in my case the User property of the IRequest object passed to the GetUserId method is always set to null...
回答1:
The solution was actually already given for another issue, right here: https://stackoverflow.com/a/22028296/4636721
The problem was all about the initialization order in the Startup.Auth.cs:
SignalR must be initialized after the cookies and the OwinContext initialization, such as that IUserIdProvider passed to GlobalHost.DependencyResolver.Register receives a IRequest containing a non-null User for its GetUserId method:
public partial class Startup
{
    public void ConfigureAuth(IAppBuilder appBuilder)
    {
        // Order matters here...
        // Otherwise SignalR won't get Identity User information passed to Id Provider...
        ConfigOwinContext(appBuilder);
        ConfigCookies(appBuilder);
        ConfigSignalR(appBuilder);
    }
    private static void ConfigOwinContext(IAppBuilder appBuilder)
    {
        appBuilder.CreatePerOwinContext(ApplicationDbContext.Create);
        appBuilder.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
        appBuilder.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
        appBuilder.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
        appBuilder.CreatePerOwinContext(LdapAdEmailAuthenticator.Create);
    }
    private static void ConfigCookies(IAppBuilder appBuilder)
    {
        appBuilder.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
            LoginPath = new PathString("/Account/Login"),
            Provider = new CookieAuthenticationProvider
            {
                OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>
                (
                    TimeSpan.FromHours(4),
                    (manager, user) => user.GenerateUserIdentityAsync(manager)
                )
            }
        });
        appBuilder.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
        appBuilder.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
        appBuilder.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
    }
    private static void ConfigSignalR(IAppBuilder appBuilder)
    {
        appBuilder.MapSignalR();
        var idProvider = new HubIdentityUserIdProvider();
        GlobalHost.DependencyResolver.Register(typeof(IUserIdProvider), () => idProvider);
    }
}
Using the IUserIdProvider below, I explicit declared that I want to use the UserId and not the UserName as given by the default implementation of the IUserIdProvider, aka PrincipalUserIdProvider:
public class HubIdentityUserIdProvider : IUserIdProvider
{
    public string GetUserId(IRequest request)
    {
        return request == null
            ? throw new ArgumentNullException(nameof(request))
            : request.User?.Identity?.GetUserId();
    }
}
来源:https://stackoverflow.com/questions/46612501/how-to-send-message-via-signalr-to-a-specific-useridentity-id