Вызов метода сервера, находящегося вне класса Hub, из клиента в 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), но возможно ли сделать это наоборот?
К сожалению, я загнал себя в угол, и код должен выполняться из написанного мною класса (а не из самого Hub - если, конечно, вы не можете запустить SignalR Hub как фабрику задач)
1 ответ
Там может быть ответ на ваш вопрос, который включает в себя HubConnection
с и IHubProxy
s (который позволяет связывать вызовы методов-концентраторов) или API более низкого уровня, но я думаю, что вы, возможно, ошибаетесь.
Концептуально, вы хотите GeoFeedHub
обрабатывать запросы клиентов, а TwitterStream
класс для обработки взаимодействия с API Twitter. Таким образом, ваш GeoFeedHub
класс зависит от TwitterStream
,
Это хорошо, что ваш TwitterStream
класс имеет async
методы, и это полностью поддерживается в SignalR. Вы можете иметь асинхронные методы Hub, которые вызывают TwitterStream
, что устраняет необходимость вашего TaskFactory
использование в Global.asax
,
Вместо создания вашего TwitterStream
при запуске приложения и попытке найти способ привязать к нему вызовы Hub (обратная зависимость и нарушение принципа единой ответственности), было бы лучше, если бы ваш Hub выступал в качестве точки соприкосновения между вашими клиентами в реальном времени и внедрял экземпляр TwitterStream
в GeoFeedHub
таким образом, концентратор может получить доступ к API Twitter.
Вот пример кода, который должен проиллюстрировать эту идею:
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);
}
}