2015-07-01 14 views
18

Próbuję poprawić moją aplikację, która będzie wymagać wywoływania koncentratora z C# zamiast javascript. Obecny workflow do dodawania zadania w mojej aplikacji jest:SignalR: Jak naprawdę wywołać metodę koncentratora z serwera/C#

  • dokonać wywołania API, aby dodać dane do bazy danych
  • powrót nowy rekord do kontrolera angularjs
  • metoda powołać Hub z kontrolerem
  • piasta rozgłasza wywołanie do klientów odpowiednio

Chciałbym pominąć wywołanie metody hubu z mojego kontrolera AngularJS i zadzwonić bezpośrednio z mojej metody kontrolera API.

To właśnie moja piasta obecnie wygląda następująco:

public class TaskHub : Hub 
{ 
    public void InsertTask(TaskViewModel task) 
    { 
     Clients.Caller.onInsertTask(task, false); 
     Clients.Others.onInsertTask(task, true); 
    } 
} 

Istnieje wiele tak wątków tam na ten temat, ale wszystko czytałem musiałby mi dodając następujący kod do mojego kontrolera API:

var hubContext = GlobalHost.ConnectionManager.GetHubContext<TaskHub>(); 
hubContext.Clients.All.onInsertTask(task); 

Istnieje wiele problemów z tym. Przede wszystkim chcę, aby połączenia rozgłaszania klienta istniały w jednej klasie, a nie wywoływane bezpośrednio z mojego kontrolera API. Po drugie, hubContext jest instancją IHubContext zamiast IHubCallerConnectionContext. Oznacza to, że mam dostęp tylko do wszystkich klientów i nie mogę rozgłaszać różnych odpowiedzi na Caller i Others, tak jak obecnie robię.

Czy istnieje sposób na prawdziwie wywołanie metody koncentratora z C# i, najlepiej, dostęp do różnych opcji wywołujących? Idealnie, byłbym w stanie zrobić coś tak proste, jak następujących od mojego kontrolera API (lub jeszcze lepiej, to rozwiązanie z DI):

var taskHub = new TaskHub(); 
taskHub.InsertTask(task); 

góry dzięki.

ROZWIĄZANIE

dla potomności, myślałem, że to moje pełne rozwiązanie jak na sugestię osa za.

pierwsze, zmodyfikowany mój javascript (angularjs) Usługa obejmuje identyfikator połączenia SignalR w nagłówku zwyczaj wniosku o wywołanie API, w tym przypadku należy wpisać:

var addTask = function (task) { 
    var config = { 
     headers: { 'ConnectionId': connection.id } 
    }; 
    return $http.post('/api/tasks', task, config); 
}; 

Potem podniósł połączenia ID od wniosku w moim kontroler API po wykonaniu operacji CRUD zastosowanie, a następnie zadzwonił do mojego piasty:

public HttpResponseMessage Post(HttpRequestMessage request, [FromBody]TaskViewModel task) 
{ 
    var viewModel = taskAdapter.AddTask(task); 
    var connectionId = request.Headers.GetValues("ConnectionId").FirstOrDefault(); 
    TaskHub.InsertTask(viewModel, connectionId); 
    return request.CreateResponse(HttpStatusCode.OK, viewModel); 
} 

Moja piasta wygląda to gdzie ja teraz tylko za pomocą metod statycznych wywoływane z mojego kontrolera API:

public class TaskHub : Hub 
{ 
    private static IHubContext context = GlobalHost.ConnectionManager.GetHubContext<TaskHub>(); 

    public static void InsertTask(TaskViewModel task, string connectionId) 
    { 
     if (!String.IsNullOrEmpty(connectionId)) 
     { 
      context.Clients.Client(connectionId).onInsertTask(task, false); 
      context.Clients.AllExcept(connectionId).onInsertTask(task, true); 
     } 
     else 
     { 
      context.Clients.All.onInsertTask(task, true); 
     } 
    } 
} 

Jak widzisz, mam metodę warunkową w mojej metodzie koncentratora, która jest obsługiwana, jeśli wywołanie w centrum nie zostało zainicjowane z części aplikacji po stronie klienta. Byłoby to, gdyby zewnętrzna aplikacja/usługa nazwała moje API. W takiej sytuacji nie byłoby połączenia SignalR i oczywiście wartości nagłówkowej "ConnectionId".Jednak w moim przypadku nadal chciałbym wywołać metodę onInsertTask dla wszystkich podłączonych klientów, która informuje ich o zmianie danych. To nigdy nie powinno się zdarzyć, ale po prostu włączyłem to do kompletności.

+0

W kontekście wywołania API SignalR nie wie, który klient jest "dzwoniącym". Normalnie wywołania SignalR przechodzą przez ich własne połączenie, które ma ten kontekst. – heavyd

+0

Rozważyłem to, rozważając problem z możliwością wyboru odbiorców. Nie byłem pewien, czy istnieje sposób na "dołączenie" wywołującego wywołania API, ale (myśląc głośno) myślę, że musisz wtedy uruchomić połączenie/koncentrator po stronie klienta. W przypadkach, gdy rozmówca/publiczność nie ma znaczenia, myślę, że nadal powinno być możliwe wywołanie metody koncentratora z C# (kontroler API w moim przypadku) ... – im1dermike

Odpowiedz

7

Aby uzyskać prawdziwie, wywołaj metodę hub, jak ją nazywasz, musisz być połączony z nią i wywołać to połączenie. Wywołując coś innego (twoje API), nie możesz wykonywać tego rodzaju połączeń, dlatego musisz korzystać z serwera , który z natury nie może wiedzieć o tym, co jest Caller, ponieważ nie ma osoby dzwoniącej z SignalR.

Powiedział, że jeśli klient wywołanie API (bez względu na to czy jest to JavaScript lub C#) jest już podłączony do koncentratora podczas wykonywania połączenia, zawsze można ozdobić wezwanie do API z connectionId z koncentrator użytkownika połączenie (przez ciąg zapytania, nagłówki, ...). Jeśli API otrzyma tę informację, może wtedy symulowaćCaller API z

Clients.Client(connectionId) 

i może zrobić to samo dla Others z

Clients.AllExcept(connectionId) 

na przykład IHubContext. Sprawdź official docs.

Następnie można skorzystać z sugestii DDan o enkapsulacji użycia IHubContext w wygodny sposób scentralizowany, lub nawet trochę go zrestrukturyzować, aby było łatwo zgodne z DI.

+0

Dzięki! Udało mi się z powodzeniem wykorzystać Twoją sugestię do wdrożenia rozwiązania i uwzględnić je w moim pytaniu dla potomności. :) – im1dermike

+0

@ im1dermike tylko ze względu na jasność, dlaczego nie akceptujesz tej odpowiedzi po tym, jak powiedziałeś, że zadziałało i stworzyłeś na niej działające rozwiązanie? Co ważniejsze, zaakceptowana odpowiedź nie wyjaśnia, w jaki sposób określić, kto jest dzwoniącym z metody innej niż SignalR, o co prosi OP. To jest "a" rozwiązanie, całkowicie poprawne, ale nie pasujące do tego, co OP określa jako kontekst, więc nie widzę, jak to ma zastosowanie.Głównym celem nie jest "zachowanie moich punktów", to posiadanie odpowiedzi pasującej do OP, a nie tego, co w końcu robiłeś. – Wasp

+0

Nie mam pojęcia, jak to się stało. Nie celowo to zmieniłem. Być może zrobił to ktoś inny? W obu przypadkach po prostu poprawiłem sprawę. Jeszcze raz dziękuję za pomoc. – im1dermike

3

Używam metody wyjaśnionej w this odpowiedzi.

public class NewsFeedHub : Hub 
{ 
    private static IHubContext hubContext = GlobalHost.ConnectionManager.GetHubContext<NewsFeedHub>(); 

    // Call this from JS: hub.client.send(channel, content) 
    public void Send(string groupName, string content) 
    { 
     Clients.Group(groupName).addMessage(content); 
    } 

    // Call this from C#: NewsFeedHub.Static_Send(groupName, content) 
    public static void Static_Send(string groupName, string content) 
    { 
     hubContext.Clients.Group(groupName).addMessage(content); 
    } 

} 

Piasta definiuje i używa jej hubContext, więc można zrobić:

var newsFeedHub = new NewsFeedHub(); 
var newsFeedHub.Static_Send("ch1", "HELLO"); 

czyli

var taskHub = new TaskHub(); 
var taskHub.InsertTask(task); 

Jeśli wolisz, że na podstawie metody swojej nazewnictwa.

+0

Co to jest "kanał"? – im1dermike

+0

I zastąpione do 'groupName' i' Client.Gourp (groupName) '. Myślę, że w twoim przypadku połączenie 'Clients.Client (connectionId)' i 'Clients.AllExcept (connectionId)' będzie działało dobrze. – DDan

Powiązane problemy