简体   繁体   English

从SignalR中的客户端调用Hub类之外的服务器方法

[英]Calling a server method that is outside of the Hub class from the client in SignalR

Consider the class below: 考虑下面的类:

using Microsoft.AspNet.SignalR;
public class TwitterStream
    {
        // Hub Context
        IHubContext context = GlobalHost.ConnectionManager.GetHubContext<GeoFeedHub>();

        public void ChangeStreamBounds(double latitude, double longitude)
        {
            Debug.WriteLine(latitude + "-" + longitude);
        }

        // Lots of other interesting code redacted
   }

Is it possible to call the ChangeStreamBounds method from the client even though it is outside of the Hub class? 即使在Hub类之外,也可以从客户端调用ChangeStreamBounds方法吗? It's possible to call client functions from the server (and from outside of the Hub class) but is it possible to do it the other way round? 可以从服务器(和Hub类的外部)调用客户端函数,但是是否可以反过来呢?

Unfortunately I've backed myself into a bit of a corner and the code must be executed from the class that I've written (not from the Hub itself - unless of course you can run a SignalR Hub as a Task factory) 不幸的是,我已经陷入困境,必须从我编写的类中执行代码(而不是从Hub本身执行-除非您当然可以将SignalR Hub作为Task工厂运行)

There may be an answer to your question that involves HubConnection s and IHubProxy s (which let you bind to hub method calls) or the lower-level API, but I think you may be going about this the wrong way. 您的问题可能涉及HubConnectionIHubProxy (可让您绑定到中心方法调用)或较低级别的API,但我认为您可能会以错误的方式进行操作。

Conceptually, you want the GeoFeedHub to handle client requests, and the TwitterStream class to handle interacting with the Twitter API. 从概念上讲,您希望GeoFeedHub处理客户端请求,并希望TwitterStream类处理与Twitter API的交互。 Thus, your GeoFeedHub class has a dependency on TwitterStream . 因此,您的GeoFeedHub类具有对TwitterStream的依赖。

It's good that your TwitterStream class has async methods, and this is fully supported in SignalR . 最好您的TwitterStream类具有async方法,并且SignalR完全支持此方法。 You can have async Hub methods that call into TwitterStream , which removes the need for your TaskFactory usage in Global.asax . 您可以具有调用TwitterStream异步Hub方法,从而无需在Global.asax使用TaskFactory

Instead of creating your TwitterStream at application start and trying to find a way to bind Hub calls to it (a backwards dependency and violation of the Single Responsibility Principle), it would be cleaner to let your Hub stand as the point of contact between your realtime clients, and inject an instance of TwitterStream into the GeoFeedHub so the Hub can access the Twitter API. 与其在应用程序启动时创建TwitterStream并尝试找到一种将Hub调用绑定到它的方法(向后依赖和违反单一职责原则),不如让Hub成为实时之间的联系点,会更干净客户端,然后将TwitterStream实例注入GeoFeedHub以便集线器可以访问Twitter API。


Here's some sample code that should illustrate this idea: 下面是一些示例代码,可以说明这个想法:

public class GeoFeedHub : Hub
{
    // Declare dependency on TwitterStream class
    private readonly TwitterStream _twitterStream;

    // Use constructor injection to get an instance of TwitterStream
    public GeoFeedHub(TwitterStream _twitterStream)
    {
        _twitterStream = _twitterStream;
    }

    // Clients can call this method, which uses the instance of TwitterStream
    public async Task SetStreamBounds(double latitude, double longitude)
    {
        await _twitterStream.SetStreamBoundsAsync(latitude, longitude);
    }
}


public class TwitterStream
{
    public TwitterStream() 
    {
    }

    public async Task SetStreamBoundsAsync(double latitude, double longitude)
    {
        // Do something with Twitter here maybe?
        await SomeComponent.SetStreamBoundsAsync(latitude, longitude);
    }

    // More awesome code here
}

I used DI in the example, so here's some of the glue code you'd need to hook that up. 我在示例中使用了DI,因此这里是您需要将其连接起来的一些粘合代码。 This code would go into your App_Start folder: 此代码将进入您的App_Start文件夹:

// Configure Unity as our DI container
public class UnityConfig
{

    private static readonly Lazy<IUnityContainer> Container = new Lazy<IUnityContainer>(() =>
    {
        var container = new UnityContainer();
        RegisterTypes(container);
        return container;
    });

    public static IUnityContainer GetConfiguredContainer()
    {
        return Container.Value;
    }


    private static void RegisterTypes(IUnityContainer container)
    {
        var twitterService = new TwitterService();
        container.RegisterInstance(twitterService);

        /*
         * Using RegisterInstance effectively makes a Singleton instance of 
         * the object accessible throughout the application.  If you don't need 
         * (or want) the class to be shared among all clients, then use 
         * container.RegisterType<TwitterService, TwitterService>();
         * which will create a new instance for each client (i.e. each time a Hub
         * is created, you'll get a brand new TwitterService object)
         */
    }
}


// If you're using ASP.NET, this can be used to set the DependencyResolver for SignalR 
// so it uses your configured container

[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(UnitySignalRActivator), "Start")]
[assembly: WebActivatorEx.ApplicationShutdownMethod(typeof(UnitySignalRActivator), "Shutdown")]

public static class UnitySignalRActivator
{
    public static void Start() 
    {
        var container = UnityConfig.GetConfiguredContainer();
        GlobalHost.DependencyResolver = new SignalRUnityDependencyResolver(container);
    }

    public static void Shutdown()
    {
        var container = UnityConfig.GetConfiguredContainer();
        container.Dispose();
    }
}

public class SignalRUnityDependencyResolver : DefaultDependencyResolver
{
    private readonly IUnityContainer _container;

    public SignalRUnityDependencyResolver(IUnityContainer container)
    {
        _container = container;
    }

    public override object GetService(Type serviceType)
    {
        return _container.IsRegistered(serviceType) 
            ? _container.Resolve(serviceType) 
            : base.GetService(serviceType);
    }

    public override IEnumerable<object> GetServices(Type serviceType)
    {
        return _container.IsRegistered(serviceType) 
            ? _container.ResolveAll(serviceType) 
            : base.GetServices(serviceType);
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 signalR调用服务器方法,该方法调用集线器外部的回调方法。 如何从该方法调用客户端功能? - signalR calls a server method and that method calls a callback method ,outside the hub. How can I call client function from that method? 使用GlobalHost.ConnectionManager.GetHubContext从外部集线器调用SignalR的客户端方法不起作用。但是从集线器内部呼叫确实如此 - SignalR calling client method from outside hub using GlobalHost.ConnectionManager.GetHubContext doesn't work. But calling from within the hub does SignalR - 用于检测客户端是否与集线器断开连接的服务器端方法? - SignalR - Server side method to detect if a client disconnects from a hub? 在SignalR中调用服务器和客户端方法 - Calling server and client method in SignalR 在.done之外调用函数时,SignalR不调用服务器中心方法 - SignalR does not invoke server hub method when calling function outside of .done 从ASP.NET的外部集线器调用WPF Signalr客户端方法 - Calling wpf signalr client methods from outside hub in asp.net 从 Controller 调用 SignalR 核心集线器方法时,angular 客户端未接收数据 - When calling SignalR Core Hub method from Controller the angular client is not receiving data 客户端.net SignalR中调用服务器方法 - calling server method in client .net SignalR 如何从外部调用 SignalR 集线器方法? - How do I call a SignalR hub method from the outside? 从 Controller 调用 SignalR Core Hub 方法时的连接 ID - Connection ID when calling SignalR Core Hub method from Controller
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM