ASP.NET Przewodnik interfejsu API usługi SignalR Hubs — klient platformy .NET (SignalR 1.x)

Autor: Patrick Fletcher, Tom Dykstra

Ostrzeżenie

Ta dokumentacja nie jest przeznaczona dla najnowszej wersji usługi SignalR. Przyjrzyj się ASP.NET Core SignalR.

Ten dokument zawiera wprowadzenie do korzystania z interfejsu API usługi Hubs dla usługi SignalR w wersji 2 w klientach platformy .NET, takich jak Sklep Windows (WinRT), WPF, Silverlight i aplikacje konsolowe.

Interfejs API usługi SignalR Hubs umożliwia wykonywanie zdalnych wywołań procedur (RPCs) z serwera do połączonych klientów i klientów z serwerem. W kodzie serwera definiuje się metody, które mogą być wywoływane przez klientów, a metody uruchamiane na kliencie. W kodzie klienta zdefiniujesz metody, które mogą być wywoływane z serwera i wywołujesz metody uruchamiane na serwerze. Usługa SignalR zajmuje się wszystkimi instalacjami instalacyjnymi typu klient-serwer.

Usługa SignalR oferuje również interfejs API niższego poziomu o nazwie Połączenia trwałe. Aby zapoznać się z wprowadzeniem do usługi SignalR, koncentratorów i połączeń trwałych lub samouczka przedstawiającego sposób tworzenia kompletnej aplikacji SignalR, zobacz SignalR — Wprowadzenie.

Omówienie

Ten dokument zawiera następujące sekcje:

Przykładowe projekty klienta platformy .NET można znaleźć w następujących zasobach:

Aby uzyskać dokumentację dotyczącą programowania klientów serwera lub języka JavaScript, zobacz następujące zasoby:

Linki do tematów referencyjnych interfejsu API to .NET 4.5 w wersji interfejsu API. Jeśli używasz platformy .NET 4, zobacz tematy dotyczące platformy .NET 4 interfejsu API.

Konfiguracja klienta

Zainstaluj pakiet NuGet Microsoft.AspNet.SignalR.Client (a nie pakiet Microsoft.AspNet.SignalR ). Ten pakiet obsługuje klientów WinRT, Silverlight, WPF, konsolowych i Windows Phone dla platformy .NET 4 i .NET 4.5.

Jeśli wersja usługi SignalR, którą masz na kliencie, różni się od wersji używanej na serwerze, usługa SignalR jest często w stanie dostosować się do różnicy. Na przykład gdy usługa SignalR w wersji 2.0 zostanie wydana i zainstalowana na serwerze, serwer będzie obsługiwać klientów z zainstalowanym programem 1.1.x, a także klientami z zainstalowaną wersją 2.0. Jeśli różnica między wersją na serwerze a wersją klienta jest zbyt duża, usługa SignalR zgłasza InvalidOperationException wyjątek, gdy klient próbuje nawiązać połączenie. Komunikat o błędzie to "You are using a version of the client that isn't compatible with the server. Client version X.X, server version X.X".

Jak nawiązać połączenie

Przed nawiązaniem połączenia należy utworzyć HubConnection obiekt i utworzyć serwer proxy. Aby nawiązać połączenie, wywołaj metodę Start w HubConnection obiekcie.

var hubConnection = new HubConnection("http://www.contoso.com/");
IHubProxy stockTickerHubProxy = hubConnection.CreateHubProxy("StockTickerHub");
stockTickerHubProxy.On<Stock>("UpdateStockPrice", stock => Console.WriteLine("Stock update for {0} new price {1}", stock.Symbol, stock.Price));
await hubConnection.Start();

Uwaga

W przypadku klientów języka JavaScript należy zarejestrować co najmniej jedną procedurę obsługi zdarzeń przed wywołaniem Start metody w celu nawiązania połączenia. Nie jest to konieczne dla klientów platformy .NET. W przypadku klientów języka JavaScript wygenerowany kod serwera proxy automatycznie tworzy serwery proxy dla wszystkich centrów, które istnieją na serwerze, a zarejestrowanie programu obsługi polega na tym, jak wskazujesz, które centra mają być używane przez klienta. Jednak w przypadku klienta platformy .NET tworzysz serwery proxy usługi Hub ręcznie, dlatego usługa SignalR zakłada, że będziesz używać dowolnego centrum, dla którego tworzysz serwer proxy.

Przykładowy kod używa domyślnego adresu URL "/signalr", aby nawiązać połączenie z usługą SignalR. Aby uzyskać informacje o sposobie określania innego podstawowego adresu URL, zobacz przewodnik interfejsu API usługi ASP.NET SignalR Hubs — serwer — adres URL /signalr.

Metoda Start jest wykonywana asynchronicznie. Aby upewnić się, że kolejne wiersze kodu nie są wykonywane dopiero po nawiązaniu połączenia, należy użyć await w metodzie asynchronicznej ASP.NET 4,5 lub .Wait() w metodzie synchronicznej. Nie używaj go .Wait() w kliencie WinRT.

await connection.Start();
connection.Start().Wait();

Klasa HubConnection jest bezpieczna wątkowo.

Połączenia między domenami z klientów programu Silverlight

Aby uzyskać informacje na temat włączania połączeń między domenami z klientów programu Silverlight, zobacz Udostępnianie usługi w granicach domeny.

Jak skonfigurować połączenie

Przed nawiązaniem połączenia można określić dowolną z następujących opcji:

  • Limit połączeń współbieżnych.
  • Parametry ciągu zapytania.
  • Metoda transportu.
  • Nagłówki HTTP.
  • Certyfikaty klienta.

Jak ustawić maksymalną liczbę połączeń współbieżnych na klientach WPF

W przypadku klientów WPF może być konieczne zwiększenie maksymalnej liczby współbieżnych połączeń z wartością domyślną 2. Zalecana wartość to 10.

var hubConnection = new HubConnection("http://www.contoso.com/");
IHubProxy stockTickerHubProxy = hubConnection.CreateHubProxy("StockTickerHub");
stockTickerHubProxy.On<Stock>("UpdateStockPrice", stock => Console.WriteLine("Stock update for {0} new price {1}", stock.Symbol, stock.Price));
ServicePointManager.DefaultConnectionLimit = 10;
await hubConnection.Start();

Aby uzyskać więcej informacji, zobacz ServicePointManager.DefaultConnectionLimit.

Jak określić parametry ciągu zapytania

Jeśli chcesz wysłać dane do serwera po nawiązaniu połączenia z klientem, możesz dodać parametry parametrów zapytania do obiektu połączenia. W poniższym przykładzie pokazano, jak ustawić parametr ciągu zapytania w kodzie klienta.

var querystringData = new Dictionary<string, string>();
querystringData.Add("contosochatversion", "1.0");
var connection = new HubConnection("http://contoso.com/", querystringData);

W poniższym przykładzie pokazano, jak odczytać parametr ciągu zapytania w kodzie serwera.

public class StockTickerHub : Hub
{
    public override Task OnConnected()
    {
        var version = Context.QueryString["contosochatversion"];
        if (version != "1.0")
        {
            Clients.Caller.notifyWrongVersion();
        }
        return base.OnConnected();
    }
}

Jak określić metodę transportu

W ramach procesu nawiązywania połączenia klient usługi SignalR zwykle negocjuje z serwerem, aby określić najlepszy transport obsługiwany przez zarówno serwer, jak i klienta. Jeśli wiesz już, którego transportu chcesz użyć, możesz pominąć ten proces negocjacji. Aby określić metodę transportu, przekaż obiekt transportu do metody Start. W poniższym przykładzie pokazano, jak określić metodę transportu w kodzie klienta.

var hubConnection = new HubConnection("http://www.contoso.com/");
IHubProxy stockTickerHubProxy = hubConnection.CreateHubProxy("StockTickerHub");
stockTickerHubProxy.On<Stock>("UpdateStockPrice", stock => Console.WriteLine("Stock update for {0} new price {1}", stock.Symbol, stock.Price));
await hubConnection.Start(new LongPollingTransport());

Przestrzeń nazw Microsoft.AspNet.SignalR.Client.Transports zawiera następujące klasy, których można użyć do określenia transportu.

  • LongPollingTransport
  • ServerSentEventsTransport
  • WebSocketTransport (dostępne tylko wtedy, gdy zarówno serwer, jak i klient używają platformy .NET 4.5).
  • AutoTransport (automatycznie wybiera najlepszy transport obsługiwany przez klienta i serwer. Jest to domyślny transport. Przekazanie tego elementu do Start metody ma taki sam efekt, jak brak przekazywania żadnych elementów).

Transport ForeverFrame nie znajduje się na tej liście, ponieważ jest używany tylko przez przeglądarki.

Aby uzyskać informacje o sposobie sprawdzania metody transportu w kodzie serwera, zobacz ASP.NET SignalR Hubs API Guide - Server — How to get information about the client from the Context property (Przewodnik po interfejsie API usługi SignalR Hubs — serwer — jak uzyskać informacje o kliencie z właściwości Context). Aby uzyskać więcej informacji na temat transportu i rezerwowych, zobacz Wprowadzenie do usługi SignalR — transporty i rezerwowe.

Jak określić nagłówki HTTP

Aby ustawić nagłówki HTTP, użyj Headers właściwości w obiekcie połączenia. W poniższym przykładzie pokazano, jak dodać nagłówek HTTP.

hubConnection = new hubConnection("http://www.contoso.com/");
connection.Headers.Add("headername", "headervalue");
IHubProxy stockTickerHubProxy = hubConnection.CreateHubProxy("StockTickerHub");
stockTickerHubProxy.On<Stock>("UpdateStockPrice", stock => Console.WriteLine("Stock update for {0} new price {1}", stock.Symbol, stock.Price));
await connection.Start();

Jak określić certyfikaty klienta

Aby dodać certyfikaty klienta, użyj AddClientCertificate metody w obiekcie połączenia.

hubConnection = new hubConnection("http://www.contoso.com/");
hubConnection.AddClientCertificate(X509Certificate.CreateFromCertFile("MyCert.cer"));
IHubProxy stockTickerHubProxy = hubConnection.CreateHubProxy("StockTickerHub");
stockTickerHubProxy.On<Stock>("UpdateStockPrice", stock => Console.WriteLine("Stock update for {0} new price {1}", stock.Symbol, stock.Price));
await connection.Start();

Jak utworzyć serwer proxy koncentratora

Aby zdefiniować metody na kliencie, który centrum może wywołać z serwera, i wywołać metody w centrum na serwerze, utwórz serwer proxy dla centrum, wywołując CreateHubProxy obiekt połączenia. Przekazywany ciąg CreateHubProxy to nazwa klasy Hub lub nazwa określona przez HubName atrybut , jeśli została użyta na serwerze. Dopasowywanie nazw jest bez uwzględniania wielkości liter.

Klasa centrum na serwerze

public class StockTickerHub : Hub

Tworzenie serwera proxy klienta dla klasy Centrum

var hubConnection = new HubConnection("http://www.contoso.com/");
IHubProxy stockTickerHubProxy = hubConnection.CreateHubProxy("StockTickerHub");
stockTickerHubProxy.On<Stock>("UpdateStockPrice", stock => Console.WriteLine("Stock update for {0} new price {1}", stock.Symbol, stock.Price));
await hubConnection.Start();

Jeśli udekorujesz klasę Hub atrybutem HubName , użyj tej nazwy.

Klasa centrum na serwerze

[HubName("stockTicker")]
public class StockTickerHub : Hub

Tworzenie serwera proxy klienta dla klasy Centrum

var hubConnection = new HubConnection("http://www.contoso.com/");
IHubProxy stockTickerHubProxy = hubConnection.CreateHubProxy("stockTicker");
stockTickerHubProxy.On<Stock>("UpdateStockPrice", stock => 
    Console.WriteLine("Stock update for {0} new price {1}", stock.Symbol, stock.Price));
await hubConnection.Start();

Obiekt serwera proxy jest bezpieczny wątkowo. W rzeczywistości, jeśli wywołasz HubConnection.CreateHubProxy wiele razy z tym samym hubNameobiektem , otrzymasz ten sam obiekt w pamięci podręcznej IHubProxy .

Jak zdefiniować metody na kliencie, który serwer może wywołać

Aby zdefiniować metodę, którą serwer może wywołać, użyj metody serwera proxy do zarejestrowania On programu obsługi zdarzeń.

Dopasowanie nazwy metody jest bez uwzględniania wielkości liter. Na przykład Clients.All.UpdateStockPrice na serwerze zostanie wykonane polecenie updateStockPrice, updatestockpricelub UpdateStockPrice na kliencie.

Różne platformy klienckie mają różne wymagania dotyczące sposobu pisania kodu metody w celu zaktualizowania interfejsu użytkownika. Poniżej przedstawiono przykłady dla klientów WinRT (Windows Store .NET). Przykłady aplikacji WPF, Silverlight i konsoli znajdują się w oddzielnej sekcji w dalszej części tego tematu.

Metody bez parametrów

Jeśli obsługiwana metoda nie ma parametrów, użyj przeciążenia On nieogócznego metody :

Kod serwera wywołujący metodę klienta bez parametrów

public class StockTickerHub : Hub
{
    public void NotifyAllClients()
    {
         Clients.All.Notify();
    }
}

Kod klienta WinRT dla metody wywoływanej z serwera bez parametrów (zobacz przykłady WPF i Silverlight w dalszej części tego tematu)

var hubConnection = new HubConnection("http://www.contoso.com/");
IHubProxy stockTickerHubProxy = hubConnection.CreateHubProxy("StockTickerHub");
stockTickerHub.On("notify", () =>
    // Context is a reference to SynchronizationContext.Current
    Context.Post(delegate
    {
        textBox.Text += "Notified!\n";
    }, null)
);
await hubConnection.Start();

Metody z parametrami określającymi typy parametrów

Jeśli obsługiwana metoda ma parametry, określ typy parametrów jako typy On ogólne metody. Istnieją ogólne przeciążenia On metody , aby umożliwić określenie maksymalnie 8 parametrów (4 w Windows Phone 7). W poniższym przykładzie jeden parametr jest wysyłany do UpdateStockPrice metody .

Kod serwera wywołujący metodę klienta z parametrem

public void BroadcastStockPrice(Stock stock)
{
    context.Clients.Others.UpdateStockPrice(stock);
}

Klasa Stock używana dla parametru

public class Stock
{
    public string Symbol { get; set; }
    public decimal Price { get; set; }
}

Kod klienta WinRT dla metody wywoływanej z serwera z parametrem (zobacz przykłady WPF i Silverlight w dalszej części tego tematu)

stockTickerHubProxy.On<Stock>("UpdateStockPrice", stock => 
    // Context is a reference to SynchronizationContext.Current
    Context.Post(delegate
    {
        textBox.Text += string.Format("Stock update for {0} new price {1}\n", stock.Symbol, stock.Price);
    }, null)
);

Metody z parametrami, określając obiekty dynamiczne dla parametrów

Alternatywą dla określenia parametrów jako typów On ogólnych metody jest określenie parametrów jako obiektów dynamicznych:

Kod serwera wywołujący metodę klienta z parametrem

public void BroadcastStockPrice(Stock stock)
{
    context.Clients.Others.UpdateStockPrice(stock);
}

Klasa Stock używana dla parametru

public class Stock
{
    public string Symbol { get; set; }
    public decimal Price { get; set; }
}

Kod klienta WinRT dla metody wywoływanej z serwera z parametrem przy użyciu obiektu dynamicznego dla parametru (zobacz przykłady WPF i Silverlight w dalszej części tego tematu)

stockTickerHubProxy.On("UpdateStockPrice", stock => 
    // Context is a reference to SynchronizationContext.Current
    Context.Post(delegate
    {
        textBox.Text += string.Format("Stock update for {0} new price {1}\n", stock.Symbol, stock.Price);
    }, null)
);

Jak usunąć procedurę obsługi

Aby usunąć procedurę obsługi, wywołaj jej Dispose metodę.

Kod klienta metody wywoływanej z serwera

var updateStockPriceHandler = stockTickerHubProxy.On<Stock>("UpdateStockPrice", stock => 
    Context.Post(delegate
    {
        textBox.Text += string.Format("Stock update for {0} new price {1}\n", stock.Symbol, stock.Price);
    }, null)
);

Kod klienta do usunięcia programu obsługi

updateStockPriceHandler.Dispose();

Jak wywoływać metody serwera z klienta

Aby wywołać metodę na serwerze, użyj Invoke metody na serwerze proxy centrum.

Jeśli metoda serwera nie ma wartości zwracanej, użyj przeciążenia nieogólne metody Invoke .

Kod serwera dla metody, która nie ma zwracanej wartości

public class StockTickerHub : Hub
{
    public void JoinGroup(string groupName)
    {
        Groups.Add(Context.ConnectionId, groupName); 
    }
}

Kod klienta wywołujący metodę, która nie ma wartości zwracanej

stockTickerHubProxy.Invoke("JoinGroup", hubConnection.ConnectionID, "SignalRChatRoom");

Jeśli metoda serwera ma wartość zwracaną, określ typ zwracany jako typ Invoke ogólny metody.

Kod serwera dla metody, która ma wartość zwracaną i przyjmuje parametr typu złożonego

public IEnumerable<Stock> AddStock(Stock stock)
{
    _stockTicker.AddStock(stock);
    return _stockTicker.GetAllStocks();
}

Klasa Stock używana dla parametru i wartości zwracanej

public class Stock
{
    public string Symbol { get; set; }
    public decimal Price { get; set; }
}

Kod klienta wywołujący metodę, która ma wartość zwracaną i przyjmuje parametr typu złożonego w metodzie asynchronicznej ASP.NET 4.5

var stocks = await stockTickerHub.Invoke<IEnumerable<Stock>>("AddStock", new Stock() { Symbol = "MSFT" });
foreach (Stock stock in stocks)
{
    textBox.Text += string.Format("Symbol: {0} price: {1}\n", stock.Symbol, stock.Price);
}

Kod klienta wywołujący metodę, która ma wartość zwracaną i przyjmuje parametr typu złożonego w metodzie synchronicznej

var stocks = stockTickerHub.Invoke<IEnumerable<Stock>>("AddStock", new Stock() { Symbol = "MSFT" }).Result;
foreach (Stock stock in stocks)
{
    textBox.Text += string.Format("Symbol: {0} price: {1}\n", stock.Symbol, stock.Price);
}

Metoda Invoke wykonuje asynchronicznie i zwraca Task obiekt. Jeśli nie określisz await elementu lub .Wait(), zostanie wykonany następny wiersz kodu przed zakończeniem wykonywania wywoływanej metody.

Jak obsługiwać zdarzenia okresu istnienia połączenia

Usługa SignalR udostępnia następujące zdarzenia okresu istnienia połączenia, które można obsłużyć:

  • Received: zgłaszane, gdy wszystkie dane są odbierane w połączeniu. Dostarcza odebrane dane.
  • ConnectionSlow: zgłaszane, gdy klient wykryje wolne lub często porzucanie połączenia.
  • Reconnecting: podniesione, gdy podstawowy transport rozpoczyna ponowne nawiązywanie połączenia.
  • Reconnected: podniesione, gdy podstawowy transport został ponownie połączony.
  • StateChanged: Zgłaszane, gdy zmieni się stan połączenia. Udostępnia stary stan i nowy stan. Aby uzyskać informacje o wartościach stanu połączenia, zobacz ConnectionState, wyliczenie.
  • Closed: zgłaszane, gdy połączenie zostało rozłączone.

Jeśli na przykład chcesz wyświetlić komunikaty ostrzegawcze dotyczące błędów, które nie są krytyczne, ale powodują sporadyczne problemy z połączeniem, takie jak spowolnienie lub częste porzucanie połączenia, obsłuż ConnectionSlow zdarzenie.

hubConnection.ConnectionSlow += () => Console.WriteLine("Connection problems.");

Aby uzyskać więcej informacji, zobacz Opis i obsługa zdarzeń okresu istnienia połączenia w usłudze SignalR.

Jak obsługiwać błędy

Jeśli nie włączysz jawnie szczegółowych komunikatów o błędach na serwerze, obiekt wyjątku zwracany przez usługę SignalR po błędzie zawiera minimalne informacje o błędzie. Na przykład jeśli wywołanie nie newContosoChatMessage powiedzie się, komunikat o błędzie w obiekcie błędu zawiera "There was an error invoking Hub method 'contosoChatHub.newContosoChatMessage'." Wysyłanie szczegółowych komunikatów o błędach do klientów w środowisku produkcyjnym nie jest zalecane ze względów bezpieczeństwa, ale jeśli chcesz włączyć szczegółowe komunikaty o błędach na potrzeby rozwiązywania problemów, użyj następującego kodu na serwerze.

var hubConfiguration = new HubConfiguration();
hubConfiguration.EnableDetailedErrors = true;
RouteTable.Routes.MapHubs(hubConfiguration);

Aby obsłużyć błędy wywoływane przez usługę SignalR, można dodać procedurę obsługi dla Error zdarzenia w obiekcie połączenia.

hubConnection.Error += ex => Console.WriteLine("SignalR error: {0}", ex.Message);

Aby obsługiwać błędy z wywołań metod, opakuj kod w bloku try-catch.

try
{
    IEnumerable<Stock> stocks = await stockTickerHub.Invoke<IEnumerable<Stock>>("GetAllStocks");
    foreach (Stock stock in stocks)
    {
        Console.WriteLine("Symbol: {0} price: {1}", stock.Symbol, stock.Price);
    }
}
catch (Exception ex)
{
    Console.WriteLine("Error invoking GetAllStocks: {0}", ex.Message);
}

Jak włączyć rejestrowanie po stronie klienta

Aby włączyć rejestrowanie po stronie klienta, ustaw TraceLevel właściwości i TraceWriter dla obiektu połączenia.

var hubConnection = new HubConnection("http://www.contoso.com/");
hubConnection.TraceLevel = TraceLevels.All;
hubConnection.TraceWriter = Console.Out;
IHubProxy stockTickerHubProxy = hubConnection.CreateHubProxy("StockTickerHub");
stockTickerHubProxy.On<Stock>("UpdateStockPrice", stock => Console.WriteLine("Stock update for {0} new price {1}", stock.Symbol, stock.Price));
await hubConnection.Start();

Przykłady kodu aplikacji WPF, Silverlight i konsoli dla metod klienckich, które serwer może wywołać

Przykłady kodu pokazane wcześniej do definiowania metod klienta, które serwer może wywołać do klientów WinRT. W poniższych przykładach pokazano równoważny kod dla klientów aplikacji WPF, Silverlight i konsoli.

Metody bez parametrów

Kod klienta WPF dla metody wywoływanej z serwera bez parametrów

stockTickerHub.On<Stock>("notify", () =>
    Dispatcher.InvokeAsync(() =>
        {
            SignalRTextBlock.Text += string.Format("Notified!");
        })
);

Kod klienta programu Silverlight dla metody wywoływanej z serwera bez parametrów

stockTickerHub.On<Stock>("notify", () =>
    // Context is a reference to SynchronizationContext.Current
    Context.Post(delegate
    {
        textBox.Text += "Notified!";
    }, null)
);

Kod klienta aplikacji konsoli dla metody wywoływanej z serwera bez parametrów

stockTickerHubProxyProxy.On("Notify", () => Console.WriteLine("Notified!"));

Metody z parametrami określającymi typy parametrów

Kod klienta WPF dla metody wywoływanej z serwera z parametrem

stockTickerHubProxy.On<Stock>("UpdateStockPrice", stock => 
    Dispatcher.InvokeAsync(() =>
        {
            textBox.Text += string.Format("Stock update for {0} new price {1}\n", stock.Symbol, stock.Price);
        })
);

Kod klienta programu Silverlight dla metody wywoływanej z serwera z parametrem

stockTickerHubProxy.On<Stock>("UpdateStockPrice", stock => 
    // Context is a reference to SynchronizationContext.Current
    Context.Post(delegate
    {
        textBox.Text += string.Format("Stock update for {0} new price {1}\n", stock.Symbol, stock.Price);
    }, null)
);

Kod klienta aplikacji konsoli dla metody wywoływanej z serwera z parametrem

stockTickerHubProxy.On<Stock>("UpdateStockPrice", stock => 
    Console.WriteLine("Symbol {0} Price {1}", stock.Symbol, stock.Price));

Metody z parametrami, określając obiekty dynamiczne dla parametrów

Kod klienta WPF dla metody wywoływanej z serwera z parametrem przy użyciu obiektu dynamicznego dla parametru

stockTickerHubProxy.On("UpdateStockPrice", stock => 
    Dispatcher.InvokeAsync(() =>
        {
            textBox.Text += string.Format("Stock update for {0} new price {1}\n", stock.Symbol, stock.Price);
        })
);

Kod klienta programu Silverlight dla metody wywoływanej z serwera z parametrem przy użyciu obiektu dynamicznego dla parametru

stockTickerHubProxy.On("UpdateStockPrice", stock => 
    // Context is a reference to SynchronizationContext.Current
    Context.Post(delegate
    {
        textBox.Text += string.Format("Stock update for {0} new price {1}\n", stock.Symbol, stock.Price);
    }, null)
);

Kod klienta aplikacji konsoli dla metody wywoływanej z serwera z parametrem przy użyciu obiektu dynamicznego dla parametru

stockTickerHubProxy.On("UpdateStockPrice", stock => 
    Console.WriteLine("Symbol {0} Price {1}", stock.Symbol, stock.Price));