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

C# 从SignalR中的客户端调用Hub类之外的服务器方法,c#,asp.net-mvc,signalr,taskfactory,C#,Asp.net Mvc,Signalr,Taskfactory,考虑以下类别: using Microsoft.AspNet.SignalR; public class TwitterStream { // Hub Context IHubContext context = GlobalHost.ConnectionManager.GetHubContext<GeoFeedHub>(); public void ChangeStreamBounds(double latitude, dou

考虑以下类别:

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
   }
使用Microsoft.AspNet.signal;
公共类推特流
{
//中心上下文
IHubContext context=GlobalHost.ConnectionManager.GetHubContext();
公共边界(双纬度、双经度)
{
Debug.WriteLine(纬度+“-”+经度);
}
//许多其他有趣的代码被编辑过
}
即使在Hub类之外,也可以从客户端调用
ChangeStreamBounds
方法吗?可以从服务器(以及从Hub类外部)调用客户机函数,但是否可以反过来呢


不幸的是,我让自己陷入了困境,代码必须从我编写的类中执行(而不是从中心本身执行-当然,除非您可以将信号器中心作为任务工厂运行)

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

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

很好,您的
TwitterStream
类有
async
方法,这就是。您可以使用异步集线器方法调用
TwitterStream
,这样就不需要在
Global.asax
中使用
TaskFactory

而不是在应用程序启动时创建您的
TwitterStream
,并试图找到一种方法将中心调用绑定到它(一种向后依赖,违反了单一责任原则),让您的中心作为实时客户端之间的联系点,并将
TwitterStream
的实例注入
GeoFeedHub
中,这样中心就可以访问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,下面是一些连接DI所需的粘合代码。这些代码将进入您的
App\u 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);
    }
}
//将Unity配置为我们的DI容器
公共类UnityConfig
{
私有静态只读惰性容器=新惰性(()=>
{
var container=new UnityContainer();
注册类型(容器);
返回容器;
});
公共静态IUnityContainer GetConfiguredContainer()
{
返回容器。值;
}
专用静态无效注册表类型(IUnityContainer容器)
{
var twitterService=新twitterService();
容器注册站(twitterService);
/*
*使用RegisterInstance可以有效地生成
*在整个应用程序中可访问的对象。如果不需要
*(或希望)在所有客户端之间共享该类,然后使用
*container.RegisterType();
*这将为每个客户机创建一个新实例(即,每次创建中心时
*创建后,您将获得一个全新的TwitterService对象)
*/
}
}
//如果您使用的是ASP.NET,则可以使用它为SignalR设置DependencyResolver
//所以它使用您配置的容器
[程序集:WebActivatorEx.PreApplicationStartMethod(类型为(UnitySignalActivator),“开始”)]
[程序集:WebActivatorEx.ApplicationShutdownMethod(类型为(UnitySignalActivator),“关机”)]
公共静态类UnitySignalActivator
{
公共静态void Start()
{
var container=UnityConfig.GetConfiguredContainer();
GlobalHost.DependencyResolver=新信号RunityDependencyResolver(容器);
}
公共静态无效关机()
{
var container=UnityConfig.GetConfiguredContainer();
container.Dispose();
}
}
公共类SignalRUnityDependencyResolver:DefaultDependencyResolver
{
专用只读IUnityContainer\u容器;
公共信号RunityDependencyResolver(IUnityContainer容器)
{
_容器=容器;
}
公共重写对象GetService(类型serviceType)
{
return\u container.IsRegistered(服务类型)
?_容器解析(serviceType)
:base.GetService(serviceType);
}
公共重写IEnumerable GetServices(类型serviceType)
{
return\u container.IsRegistered(服务类型)
?_container.ResolveAll(服务类型)
:base.GetServices(serviceType);
}
}

为什么不能在
GeoFeedHub
中放置一个方法,该方法由客户端调用,然后在
TwitterStream
的实例上调用
ChangeStreamBounds
?@RyanErdmann我正在使用
Task.Factory.StartNew(()=>new TwitterStream())创建
TwitterStream
类的实例
Global.asax
/
Application\u Start
中的
很有趣。
TwitterStream
的目的是什么?看起来
TwitterStream
本质上是应用程序的一个单例?
TwitterStream
使用invittweet-Twitter-API C包装器来处理Twitter流API异步-很多任务和异步的东西。我想你可以这么说。我不记得为什么我创建了它作为一个任务工厂-这已经在开发的迷雾中消失了!肯定有一个合理的理由。