簡體   English   中英

從SignalR中的客戶端調用Hub類之外的服務器方法

[英]Calling a server method that is outside of the Hub class from the client in 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
   }

即使在Hub類之外,也可以從客戶端調用ChangeStreamBounds方法嗎? 可以從服務器(和Hub類的外部)調用客戶端函數,但是是否可以反過來呢?

不幸的是,我已經陷入困境,必須從我編寫的類中執行代碼(而不是從Hub本身執行-除非您當然可以將SignalR Hub作為Task工廠運行)

您的問題可能涉及HubConnectionIHubProxy (可讓您綁定到中心方法調用)或較低級別的API,但我認為您可能會以錯誤的方式進行操作。

從概念上講,您希望GeoFeedHub處理客戶端請求,並希望TwitterStream類處理與Twitter API的交互。 因此,您的GeoFeedHub類具有對TwitterStream的依賴。

最好您的TwitterStream類具有async方法,並且SignalR完全支持此方法。 您可以具有調用TwitterStream異步Hub方法,從而無需在Global.asax使用TaskFactory

與其在應用程序啟動時創建TwitterStream並嘗試找到一種將Hub調用綁定到它的方法(向后依賴和違反單一職責原則),不如讓Hub成為實時之間的聯系點,會更干凈客戶端,然后將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,因此這里是您需要將其連接起來的一些粘合代碼。 此代碼將進入您的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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM