调用服务器方法是集线器类的从外面SignalR

调用服务器方法是集线器类的从外面SignalR

问题描述:

客户考虑下面的类:调用服务器方法是集线器类的从外面SignalR

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 
    } 

是否可以从客户端调用ChangeStreamBounds方法,即使它是轮毂的外类?可以从服务器(以及Hub类以外的地方)调用客户端函数,但是否有可能以相反的方式进行操作?

不幸的是我已经把自己投入了一个角落,代码必须从我写的类中执行(而不是从Hub本身执行 - 除非你可以将SignalR Hub作为任务工厂运行)

+0

为什么你不能把一个方法'是被客户端调用GeoFeedHub',然后调用'ChangeStreamBounds'你的'TwitterStream'的实例,从那里? –

+0

@RyanErdmann我使用Task.Factory.StartNew(()=> new TwitterStream());'Global.asax' /'Application_Start'中创建'TwitterStream'类的实例。 – adaam

+0

有趣。 'TwitterStream'的目的是什么?它看起来像'TwitterStream'本质上是你的应用程序的单例实例吗? –

您的问题可能有一个答案,涉及HubConnection s和IHubProxy s(允许您绑定到hub方法调用)或较低级别的API,但我认为您可能会以错误的方式解决这个问题。

从概念上讲,您希望GeoFeedHub可以处理客户端请求,TwitterStream类可以处理与Twitter API的交互。因此,你的GeoFeedHub类对TwitterStream有依赖。

这很好,您的TwitterStream类有async方法,这是fully supported in SignalR。您可以使用调用TwitterStream的异步Hub方法,该方法不需要在Global.asax中使用TaskFactory

而不是在应用程序启动时创建TwitterStream,并试图找到一种方法将Hub调用绑定到它(向后依赖和违反单一职责原则),让您的集线器作为联系您的实时客户端,并将TwitterStream的实例注入GeoFeedHub,以便Hub可以访问Twitter API。


下面是一些示例代码,应该说明这个道理:

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 
} 

我用DI的例子,所以这里的一些胶水代码你需要钩住起来。此代码将进入你的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); 
    } 
} 
+0

谢谢!很好的答案 – adaam