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异步-很多任务和异步的东西。我想你可以这么说。我不记得为什么我创建了它作为一个任务工厂-这已经在开发的迷雾中消失了!肯定有一个合理的理由。