Wcf i signalR : Business Feedback

437 views

Published on

Petit document on s'explica com integrar SignalR i WCF, per oferir un feedbak a l'usuari en mig d'una operació de negoci.

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
437
On SlideShare
0
From Embeds
0
Number of Embeds
34
Actions
Shares
0
Downloads
4
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Wcf i signalR : Business Feedback

  1. 1. SOACat: http://soacat.blogspot.com ; https://twitter.com/SOACAT ;WCF i SignalRLa idea és aconseguir que un servei WCF on podem tenir allotjades les operacions de negoci d’una aplicació, puguidonar un feedback del que està fent en temps real a l’usuari de l’aplicació.Per poder fer això de manera molt fàcil, utilitzaré el VS2012 Professional, el Framework 4.5, i dues llibreries RC,Microsoft ASP.NET SignalR SystemWeb i Microsoft ASP.NET SignalR Client.
  2. 2. SOACat: http://soacat.blogspot.com ; https://twitter.com/SOACAT ;Partirem de que tenim una aplicació client “activa”, en el meu cas i com a exemple, feta amb Winforms, que esconnecta a un servei que té operacions de negoci fet amb WCF.Ara doncs es tracta d’aprofitar la potencia de SignalR per poder fer que el servidor sigui capaç d’informar al client delque està passat en el procés de negoci invocat.Primer de tot, crearem el servidor de missatges. 1. Crear un projecte Web buit. 2. Afegir refèrencia a Microsoft ASP.NET SignalR SystemWeb amb Nuget. 3. Afegir Global.asax. 4. Crear un Hub, amb les operacions que invocarem, i en la seva implementació i posarem les operacions dinàmiques que subscriuran els clients.Mirar video1: http://youtu.be/g0JiRbpK0l8Un cop tenim el servidor de missatges “SRServer”, preparem el servei de negoci i l’aplicació client per podertreballar com a clients del servei de missatgeria. En aquest cas, l’ aplicació client només escoltarà, o ben dit, rebràmissatges i el servei n’enviarà. Això no vol dir que no puguin fer les dues feines els dos.A ambdós projectes li afegim la llibreria i Microsoft ASP.NET SignalR Client, amb Nuget package manager.Un cop fet això implementarem les subscripcions del client winforms.En el codi del form1 i declarem unes variables:private string urlmessenger = "http://localhost:15560/";private string hubproxy = "BusinessMessenger";HubConnection hubConnection = null;Sobreescrivim el delegat de l’event “OnShown”: protected override void OnShown(EventArgs e) { base.OnShown(e); hubConnection = new HubConnection(urlmessenger); //Creem el proxy var messageHub = hubConnection.CreateHubProxy(hubproxy); var schu = TaskScheduler.FromCurrentSynchronizationContext(); // ens subscrivim als metodes dinàmics del servidor messageHub.On<string>("sendmessage", message => { // do our work on the UI thread Task.Factory.StartNew( () => { this.textBoxConsole.ForeColor = Color.White; this.textBoxConsole.AppendText(message + Environment.NewLine); }, CancellationToken.None, TaskCreationOptions.None, schu ); }); messageHub.On<string>("sendBCmessage", message => { // do our work on the UI thread
  3. 3. SOACat: http://soacat.blogspot.com ; https://twitter.com/SOACAT ; Task.Factory.StartNew( () => { this.textBoxConsole.ForeColor = Color.Yellow; this.textBoxConsole.AppendText(message + Environment.NewLine); }, CancellationToken.None, TaskCreationOptions.None, schu ); }); hubConnection.Start().ContinueWith(task => { if (task.IsFaulted) { task.ContinueWith((result) => { textBoxConsole.ForeColor = Color.Red; textBoxConsole.AppendText( String.Format("Hi ha un error obrint la conexió: {0}{1}", result.Exception.GetBaseException(), Environment.NewLine)); } , schu); } else { task.ContinueWith((result) => { textBoxConsole.ForeColor = Color.Green; textBoxConsole.AppendText( String.Format("La conexió sha obert correctament -ConnectionId:[{0}]-{1}", hubConnection.ConnectionId, Environment.NewLine)); }, schu); } }).Wait(); EnableControls(); } private void EnableControls() { this.button1.Enabled = true; this.button2.Enabled = true; this.textBoxConsole.Enabled = true; }Un cop fet això ja tenim el client subscrit a dos funcions del servidor per rebre missatges.
  4. 4. SOACat: http://soacat.blogspot.com ; https://twitter.com/SOACAT ;Si ens hi fixem tenim un identificador de connexió. Aquest és el que haurà de fer servir el servei en la instància queserveix en cada moment al client. I per tant d’alguna manera hem de passar-lo al servei. Com que els serveis podemutilitzar-los de moltes maneres, amb sessions, sense, podem passar sempre per paràmetre l ‘id de connexió, ..., bé,pensem. Si el passem com a paràmetre, hauríem de modificar totes les operacions de la interfície que voléssim queenviéssim missatges, i si el client ja el tenim implementat, modificar totes les crides a aquestes operacions.Imaginem-nos que en tenim moltes, quin rotllo. I imaginem que moltes aplicacions accedeixen a operacions delservei de negoci, BUA!!!!!. I el codi que picarem no el podrem reutilitzar......pensem.Bé, si no volem fer res de tot això, i volem que sempre passi el id cap al servei, independentment de com estàinstanciat, si permet sessions o no, i de com tracta la concurrència, la millor manera es mitjançant un paràmetre decapçalera. Si nosaltres som capaços de modificar la connexió client de tal manera que a cada crida d’operació liafegim una capçalera amb el id de connexió, i a les operacions del servei que vulguin enviar un missatge al client,puguin obtenir el id de connexió d’aquest, ja ho tenim solucionat.Per fer-ho ens creem una classe que implementarà dues interfícies del namespace System.ServiceModel.Dispacher iSystem.ServideModel.Description. I en el mètode BeforeSendRequest modificarem la capçalera del missatge.I després, quan creem la connexió li afegirem aquesta classe com a nou comportament del client. public class WcfClientInspector : IClientMessageInspector, IEndpointBehavior { static string _namespace = "http://soacat.blogspot.com"; private string _messengerConnectionId = string.Empty; public WcfClientInspector(string messengerConnectionId) { _messengerConnectionId = messengerConnectionId; } public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState) { } public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel) { MessageHeader<string> header = new MessageHeader<string>(_messengerConnectionId); request.Headers.Add(header.GetUntypedHeader("MessengerConnectionId", _namespace)); return null; } public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) { } public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { clientRuntime.MessageInspectors.Add(this); } public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { }
  5. 5. SOACat: http://soacat.blogspot.com ; https://twitter.com/SOACAT ; public void Validate(ServiceEndpoint endpoint) { } }Ara modifiquem els delegats dels events del botons per tal de que invoquin les operacions del servei, afegint elcomportament nou. private async void button1_Click(object sender, EventArgs e) { button1.Enabled = false; RefOrderService.IOrders cliordres = CreateAndOpenClient(); await cliordres.ProcessOrdersAsync().ContinueWith((r)=>{ CloseClient((ICommunicationObject)cliordres); button1.Enabled = true; }, TaskScheduler.FromCurrentSynchronizationContext()); } private async void button2_Click(object sender, EventArgs e) { button2.Enabled = false; RefOrderService.IOrders cliordres = CreateAndOpenClient(); await cliordres.MailingAsync().ContinueWith((s) => { textBoxConsole.ForeColor = Color.Blue; textBoxConsole.AppendText(string.Format("{0}-{1}", s.Result, Environment.NewLine)); this.textBoxConsole.SelectionStart = this.textBoxConsole.Text.Length; this.textBoxConsole.ScrollToCaret(); CloseClient((ICommunicationObject)cliordres); button2.Enabled = true; }, TaskScheduler.FromCurrentSynchronizationContext()); } private RefOrderService.IOrders CreateAndOpenClient() { RefOrderService.OrdersClient client = new RefOrderService.OrdersClient(); client.Endpoint.EndpointBehaviors.Add(newWcfClientInspector(hubConnection.ConnectionId)); client.Open(); return client; } private void CloseClient(ICommunicationObject comobj){ comobj.Close(); }Ara ja només ens falta el servei de negoci. Primer haurà de poder recuperar la el id de connexió del client cap elservei de missatges, i després saber com enviar-li un missatge. Farem una funció senzilla al servei, que llegira lacapçalera requerida.
  6. 6. SOACat: http://soacat.blogspot.com ; https://twitter.com/SOACAT ; private string GetClientMessengerConnectionId() { int pos=OperationContext.Current. IncomingMessageHeaders.FindHeader("MessengerConnectionId", "http://soacat.blogspot.com"); if (pos >= 0) { return OperationContext.Current.IncomingMessageHeaders.GetHeader<string>(pos); } else { return string.Empty; } }Ara ja només ens falta, poder enviar missatges al client des del negoci. Els requisits mínims són la url del servei demissatgeria i el nom del Hub que contés les operacions de missatgeria. Un cop fet això, en el constructor del servei,inicialitzen la connexió i el Hub. public class Orders: IOrders { private string _urlserver = "http://localhost:15560/"; private string _hubname = "BusinessMessenger"; private IHubConnection _hubconnection = null; private IHubProxy _hubproxy = null; public Orders() { _hubconnection = new HubConnection(_urlserver); _hubproxy = ((HubConnection)_hubconnection).CreateHubProxy(_hubname); // Engeguem la connexió i el HUB StartConnection(); } private void StartConnection() { ((HubConnection)_hubconnection).Start().ContinueWith(task => { if (task.IsFaulted) { throw new FaultException<Exception>(task.Exception.GetBaseException()); } }).Wait(); }...
  7. 7. SOACat: http://soacat.blogspot.com ; https://twitter.com/SOACAT ;Ara creem dues funcions per enviar missatges cap el client utilitzant el hub. private void SendMessageToClient(string clientConnectionId, string message) { if(_hubconnection.State== Microsoft.AspNet.SignalR.Client.ConnectionState.Connected) if(!string.IsNullOrEmpty(clientConnectionId)) _hubproxy.Invoke("SendTo", clientConnectionId, message).Wait(); } private void SendMessageToAllClient(string message) { if (_hubconnection.State == Microsoft.AspNet.SignalR.Client.ConnectionState.Connected) _hubproxy.Invoke("BroadCast", message); }I ja està, la resta al Codi Font : https://dl.dropbox.com/u/108938215/WCF_SignalR/SolutionWCF_SignalR.rarVeure Video 2 : http://youtu.be/_X5klFffnsU

×