[英]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. 您的问题可能涉及
HubConnection
和IHubProxy
(可让您绑定到中心方法调用)或较低级别的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.