Il presente articolo è stato tradotto automaticamente.
Silverlight
Creazione di applicazioni dell'organizzazione di linea di business-con Silverlight, parte 2
Nell'articolo viene descritto:
|
In questo articolo vengono utilizzate le seguenti tecnologie: Silverlight 2 |
Download codice disponibile dalla Raccolta di codice di MSDN
Selezionare il codice in linea
Contenuto
Integrazione con servizi di Business
Chiamata di servizio
Chiamate di servizio sincronizzati
Conversione di entità del messaggio
Modifica dello stato di Silverlight dopo le chiamate di servizio
Criteri tra domini
Cross criteri di dominio per Web Services IIS host esterno
Criteri tra domini per i servizi ospitati all'interno di IIS
Protezione dell'applicazione
Partizionamento delle applicazioni
Produttività e oltre
Durante la prima dispensa di questa serie È introdotto uno scenario di centro di chiamata e mostrato un'implementazione di popolazione dello schermo (schermo pop) tramite il socket connesso utilizzate il socket TCP asincrona supportati da Silverlight (vedere"Creazione di applicazioni dell'organizzazione di linea di business-con Silverlight, parte 1").
Il pop dello schermo è stato implementato mediante un dispatcher di chiamate simulato prelevati da una chiamata da una coda interna e inserito le notifiche tramite la connessione socket accettato in precedenza nella cache in un elenco generico sul server. È possibile verrà concludere tramite l'implementazione della protezione dell'applicazione, l'integrazione con servizi aziendali e implementazione dei criteri tra domini per i servizi Web e la partizione di applicazione. L'architettura logica dell'applicazione del centro di chiamata è illustrata nella Figura 1 . Il servizio di autenticazione verrà essere implementato nel utilità servizio mentre i servizi aziendali, ICallService e IUserProfile, verranno implementati all'interno di progetto di servizio di business, come indicato dal nome.
Figura 1 Architettura logica di Silverlight Call Center
Anche se il diagramma Mostra evento flusso a servizi di utilità, interest di tempo, demo scaricabile non include questa funzionalità. L'implementazione della funzionalità del servizio di acquisizione evento sarà simile all'implementazione di servizi aziendali. Tuttavia, possono essere memorizzati localmente nella cache in archiviazione isolata eventi aziendali che non sono gli errori critici e dump al server in modalità batch. Verrà avviare la discussione con l'implementazione di servizi aziendali e concludere con partizionamento dell'applicazione.
Integrazione con servizi di Business
Integrazione con servizi è uno degli aspetti importanti di applicazione line-of-business (LOB) e Silverlight fornisce componenti molto per l'accesso alle risorse basate sul Web e ai servizi. Infrastruttura di proxy HttpWebRequest, WebClient e Windows Communication Foundation (WCF) sono alcuni dei componenti di rete comuni per l'interazione basata su HTTP. In questo articolo, utilizza i servizi WCF per integrare con i processi aziendali server back-end.
La maggior parte di noi Utilizzo dei servizi Web per l'integrazione con le origini dati back-end nel corso di sviluppo di applicazioni; accesso al servizio Web di WCF con Silverlight non è molto diverso da con applicazioni tradizionali, ad esempio le applicazioni ASP.NET, Windows Presentation Foundation (WPF) o Windows Form. Le differenze sono il supporto dell'associazione e il modello di programmazione asincrona. Silverlight supporta solo basicHttpBinding e PollingDuplexHttpBinding. Si noti che HttpBinding è l'associazione più interoperabilità. Per questo motivo, verrà utilizzato da per l'integrazione completa di questo articolo.
PollingDuplexHttpBinding consente l'utilizzo dei contratti di callback per inserire le notifiche su HTTP. I call center potrebbe stato utilizzato questa associazione per le notifiche popup dello schermo. Tuttavia, l'implementazione richiede la memorizzazione nella cache della connessione HTTP sul server, quindi monopolizzando una delle due connessioni HTTP simultanee consentiti dal browser, ad esempio Internet Explorer 7.0. Ciò può causare problemi di prestazioni, in quanto dispone di tutto il contenuto Web da serializzare tramite una connessione. Internet Explorer 8.0 consente sei connessioni simultanee per dominio e grado di risolvere tali problemi di prestazioni. (Notifiche di push mediante PollingDuplexHttpBinding può essere un argomento per un articolo futuro quando Internet Explorer 8.0 è ampiamente disponibile).
All'applicazione. Quando l'agente accetta una chiamata, il processo pop schermo popola schermo con le informazioni del chiamante, in questo caso, i dettagli dell'ordine del chiamante. Le informazioni del chiamante devono contenere le informazioni necessarie per identifica in modo univoco l'ordine nel database di back-end. Per questo scenario demo, presupporrà che il numero dell'ordine è stato lettura nel sistema (IVR) risposta vocale interattivo. L'applicazione Silverlight chiamerà servizi WCF Web con il numero dell'ordine come identificatore univoco. La definizione del contratto di servizio e l'implementazione è illustrato nella Figura 2 .
Nella figura 2 Business servizio implementazione
ServiceContracts.cs
[ServiceContract]
public interface ICallService
{
[OperationContract]
AgentScript GetAgentScript(string orderNumber);
[OperationContract]
OrderInfo GetOrderDetails(string orderNumber);
}
[ServiceContract]
public interface IUserProfile
{
[OperationContract]
User GetUser(string userID);
}
CallService.svc.cs
[AspNetCompatibilityRequirements(RequirementsMode =
AspNetCompatibilityRequirementsMode.Allowed)]
public class CallService:ICallService, IUserProfile
{
public AgentScript GetAgentScript(string orderNumber)
{
...
script.QuestionList = DataUtility.GetSecurityQuestions(orderNumber);
return script;
}
public OrderInfo GetOrderDetails(string orderNumber)
{
...
oi.Customer = DataUtility.GetCustomerByID(oi.Order.CustomerID);
return oi;
}
public User GetUser(string userID)
{
return DataUtility.GetUserByID(userID);
}
}
File Web.config
<system.servicemodel>
<services>
<endpoint binding="basicHttpBinding" contract="AdvBusinessServices.ICallService"/>
<endpoint binding="basicHttpBinding" contract="AdvBusinessServices.IUserProfile"/>
</services>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
<system.servicemodel>
L'implementazione di tali endpoint del servizio non è effettivamente molto interessante, queste sono le implementazioni di WCF semplice. Per sake di semplicità, è possibile non utilizzerà qualsiasi database per le entità business ma saranno oggetti in memoria List per memorizzare oggetti cliente, ordine e utente. La classe DataUtil (non visualizzata di seguito ma disponibile nel download del codice) incapsula accesso a questi oggetti di elenco in memoria.
Nella figura 3 dell'agente di script con domande di protezione
Gli endpoint del servizio WCF per Silverlight consumo necessario l'accesso alla pipeline ASP.NET e di conseguenza richiedono l'attributo AspNetCompatibilityRequirements sull'implementazione CallService. <servicehostingenvironment/>Questo deve corrispondere dall'impostazione nel file web.config.
Come precedentemente indicato, basicHttpBinding supporta solo di Silverlight e PollingDuplexHttpBinding. Se si utilizza il modello WCF Service Visual Studio, consente di configurare l'associazione dell'endpoint per wsHttpBinding, deve essere modificato manualmente in basicHttpBinding prima Silverlight può aggiungere un riferimento al servizio per la generazione del proxy. ASP.NET hosting modifiche di compatibilità e modifiche di associazione verranno automaticamente da eseguita occupa di Se CallService.svc viene aggiunto il progetto di AdvBusinessServices utilizzando un modello WCF Service Visual Studio Silverlight-enabled.
Chiamata di servizio
Avere implementato un servizio richiamabile Silverlight, ora è possibile creare i proxy del servizio e utilizzarle per associare l'interfaccia utente a implementazioni servizio back-end. È possibile solo generare proxy per i servizi WCF in modo affidabile utilizzando i riferimenti di servizio | Aggiungi riferimento servizio sequenza in Visual Studio. I proxy nel mio esempio sono stati generati nello spazio dei nomi di CallBusinessProxy. Silverlight consente solo chiamate asincrone alle risorse di rete e chiamata del servizio non è fa eccezione. Quando in una chiamata di cliente, il client di Silverlight verrà ascoltare la notifica e verrà visualizzata una finestra di dialogo Accetta o Rifiuta.
Dopo una chiamata viene accettata dall'agente, il passaggio successivo del processo consiste nel chiamare il servizio Web per recuperare lo script dell'agente che corrisponde alla situazione chiamata. Per questa demo è possibile solo utilizzerà uno script così come visualizzata nella Figura 3 . Lo script visualizzato contiene una formula di apertura, nonché una lista di domande di protezione. L'agente garantisce che un numero minimo di domande viene completato prima di passare l'assistenza.
Lo script di agente recupero accedendo ICallService.GetAgentScript(), che fornisce il numero dell'ordine come input. Coerente con il modello di programmazione asincrona applicato tramite lo stack di servizi Web Silverlight, il GetAgentScript() è disponibile come CallServiceClient.BeginGetAgentScript(). Mentre si effettua la chiamata al servizio, è necessario fornire un gestore di callback GetAgentScriptCallback, come illustrato nella Figura 4 .
Nella figura 4 servizio di chiamata e l'interfaccia utente di Silverlight
class Page:UserControl
{
...
void _notifyCallPopup_OnAccept(object sender, EventArgs e)
{
AcceptMessage acceptMsg = new AcceptMessage();
acceptMsg.RepNumber = ClientGlobals.currentUser.RepNumber;
ClientGlobals.socketClient.SendAsync(acceptMsg);
this.borderCallProgressView.DataContext = ClientGlobals.callInfo;
ICallService callService = new CallServiceClient();
IAsyncResult result =
callService.BeginGetAgentScript(ClientGlobals.callInfo.OrderNumber,
GetAgentScriptCallback, callService);
//do a preemptive download of user control
ThreadPool.QueueUserWorkItem(ExecuteControlDownload);
//do a preemptive download of the order information
ThreadPool.QueueUserWorkItem(ExecuteGetOrderDetails,
ClientGlobals.callInfo.OrderNumber);
}
void GetAgentScriptCallback(IAsyncResult asyncReseult)
{
ICallService callService = asyncReseult.AsyncState as ICallService;
CallBusinessProxy.AgentScript svcOutputAgentScript =
callService.EndGetAgentScript(asyncReseult);
ClientEntityTranslator astobas =
SvcScriptToClientScript.entityTranslator;
ClientEntities.AgentScript currentAgentScript =
astobas.ToClientEntity(svcOutputAgentScript)
as ClientEntities.AgentScript;
Interlocked.Exchange<ClientEntities.AgentScript>(ref
ClientGlobals.currentAgentScript, currentAgentScript);
if (this.Dispatcher.CheckAccess())
{
this.borderAgentScript.DataContext = ClientGlobals.agentScript;
...
this.hlVerifyContinue.Visibility = Visibility.Visible;
}
else
{
this.Dispatcher.BeginInvoke(
delegate()
{
this.borderAgentScript.DataContext = ClientGlobals.agentScript;
...
this.hlVerifyContinue.Visibility = Visibility.Visible;
} );
}
}
private void ExecuteControlDownload(object state)
{
WebClient webClient = new WebClient();
webClient.OpenReadCompleted += new
OpenReadCompletedEventHandler(OrderDetailControlDownloadCallback);
webClient.OpenReadAsync(new Uri("/ClientBin/AdvOrderClientControls.dll",
UriKind.Relative));
}
...
}
Poiché è possibile recuperare il risultato della chiamata servizio solo dal gestore di callback, le modifiche allo stato dell'applicazione Silverlight dovranno essere eseguita nel gestore di callback. CallServiceClient.BeginGetAgentScript() viene richiamato da _notifyCallPopup_OnAccept esecuzione sul thread dell'interfaccia utente e alla richiesta asincrona in coda e restituisce immediatamente l'istruzione successiva. Poiché lo script dell'agente non è ancora disponibile, dovrai attendere finché non viene attivato il callback prima della cache è lo script e dati associarlo a dell'interfaccia utente.
Completamento del servizio di chiamata trigger GetAgentScriptCallback, che recupera lo script dell'agente, compila una variabile globale e per l'associazione dati lo script dell'agente agli elementi dell'interfaccia utente appropriati viene ricalcolato l'interfaccia utente. Durante la modifica dell'interfaccia utente, il GetAgentScriptCallback verrà assicurarsi che venga aggiornata sul thread dell'interfaccia utente tramite l'utilizzo di Dispatcher.CheckAccess().
UIElement.Dispatcher.CheckAccess() verrà confrontare l'ID di thread dell'interfaccia utente con il thread di lavoro e restituire true se entrambi i thread sono uguali; in caso contrario, restituisce false. Quando di esegue GetAgentScriptCallback su un thread di lavoro (in realtà, poiché questa verrà sempre eseguita su un thread di lavoro è possibile semplicemente chiamare Dispatcher.BeginInvoke), CheckAccess() restituirà false e l'interfaccia utente verrà aggiornato per l'invio di un delegato anonimo tramite Dispatcher.Invoke().
Chiamate di servizio sincronizzati
A causa della natura asincrona dell'ambiente di rete di Silverlight, risulta quasi Impossibile verificare un servizio asincrono chiamare sul thread dell'interfaccia utente e attendere che venga completata con l'intento di modificare lo stato dell'applicazione in base ai risultati della chiamata. Nella Figura 4 , _notifyCallPopup_OnAccept deve recuperare dettagli sugli ordini, trasformare il messaggio di output in un'entità di client e salvarlo in una variabile globale in modo thread-safe. A tale scopo, uno può essere tempted scrivere il codice gestore, come illustrato di seguito:
CallServiceClient client = new CallServiceClient();
client.GetOrderDetailsAsync(orderNumber);
this._orderDetailDownloadHandle.WaitOne();
//do something with the results
Ma questo codice verrà bloccare l'applicazione quando accede l'istruzione this._orderDetailDownloadHandle.WaitOne(). Si tratta in quanto l'istruzione WaitOne() blocca il thread dell'interfaccia utente di ricevere qualsiasi messaggio inviato da altri thread. Al contrario, è possibile pianificare il thread di lavoro per eseguire la chiamata al servizio, attendere la chiamata per il completamento e completare l'elaborazione di post dell'output di servizio nella sua interezza sul thread di lavoro. Questa tecnica è illustrata nella Figura 5 . Per impedire l'utilizzo accidentali di blocco chiamate nel thread dell'interfaccia utente, disposto automaticamente ManualResetEvent all'interno di un SLManualResetEvent personalizzata e viene eseguito il test per thread dell'interfaccia utente quando una chiamata a WaitOne().
Nella figura 5 recuperare Order Details
void _notifyCallPopup_OnAccept(object sender, EventArgs e)
{
...
ThreadPool.QueueUserWorkItem(ExecuteGetOrderDetails,
ClientGlobals.callInfo.OrderNumber);
}
private SLManualResetEvent _ orderDetailDownloadHandle = new
SLManualResetEvent();
private void ExecuteGetOrderDetails(object state)
{
CallServiceClient client = new CallServiceClient();
string orderNumber = state as string;
client.GetOrderDetailsCompleted += new
EventHandler<GetOrderDetailsCompletedEventArgs>
(GetOrderDetailsCompletedCallback);
client.GetOrderDetailsAsync(orderNumber);
this._orderDetailDownloadHandle.WaitOne();
//translate entity and save it to global variable
ClientEntityTranslator oito = SvcOrderToClientOrder.entityTranslator;
ClientEntities.Order currentOrder =
oito.ToClientEntity(ClientGlobals.serviceOutputOrder)
as ClientEntities.Order;
Interlocked.Exchange<ClientEntities.Order>(ref ClientGlobals.
currentOrder, currentOrder);
}
void GetOrderDetailsCompletedCallback(object sender,
GetOrderDetailsCompletedEventArgs e)
{
Interlocked.Exchange<OrderInfo>(ref ClientGlobals.serviceOutputOrder,
e.Result);
this._orderDetailDownloadHandle.Set();
}
Poiché SLManualResetEvent è una classe generica, non dipendono Dispatcher.CheckAccess() di un controllo specifico. ApplicationHelper.IsUiThread() può controllare Application.RootVisual.Dispatcher.CheckAccess(); tuttavia, accesso al metodo attiverà eccezione accesso cross-thread non valido. Così l'affidabile solo di questo test di un lavoro thread, quando non è Nessun accesso a un'istanza di UIElement, consiste nell'utilizzare Deployment.Current.Dispatcher.CheckAccess() come illustrato di seguito:
public static bool IsUiThread()
{
if (Deployment.Current.Dispatcher.CheckAccess())
return true;
else
return false;
}
Per l'esecuzione dello sfondo dell'attività, invece di utilizzare ThreadPool.QueueUserWorkItem, è possibile utilizzare BackGroundWorker, che verrà inoltre utilizzato ThreadPool ma consente di associare gestori che è possono eseguire sul thread dell'interfaccia utente. Questo modello consente l'esecuzione del servizio più chiama in parallelo e attende che tutte le chiamate a completare utilizzando SLManualResetEvent.WaitOne() prima che i risultati vengono aggregati per un'ulteriore elaborazione.
Conversione di entità del messaggio
Le entità del messaggio di output (noto anche come DataContracts il GetAgentScriptCallback inoltre converte dal servizio in un'entità sul lato client, che rappresenta la semantica di utilizzo del lato client. Ad esempio, la struttura di entità del messaggio sul lato server, potrebbe non interessano mentre pagamento prestare particolare attenzione la natura multiuse del servizio che verrà soddisfare una vasta gamma di utilizzo, non solo call center di associazione dati.
È inoltre consigliabile non dispone di stretta associazione con le entità messaggio perché le modifiche alle entità di messaggio non saranno in controllo del client. La procedura di conversione messaggio entità a entità del lato client non è applicabile solo a Silverlight, ma è in genere applicabile a qualsiasi consumer del servizio Web quando desiderano evitare accoppiamento stretta fase di progettazione.
HO deciso di mantenere l'implementazione di traduttori l'entità molto semplice, senza exotic generics nidificati, le espressioni lambda o inversione di contenitori di controllo. ClientEntityTranslator è una classe astratta che definisce il metodo di ToClientEntity() ogni sottoclasse deve eseguire l'override:
public abstract class ClientEntityTranslator
{
public abstract ClientEntities.ClientEntity ToClientEntity(object
serviceOutputEntity);
}
Ogni classe figlio è univoco per un tipo di servizio exchange; pertanto creerò traduttori quante necessarie. Nella demo sono apportate tre tipi di chiamate al servizio: IUserProfile.GetUser(), ICallService.GetAgentScript() e ICallService.GetOrderDetails(). Pertanto è stata creata traduttori tre, come illustrato nella Figura 6 .
Nella figura 6 messaggi entità per traduttore entità lato client
public class SvcOrderToClientOrder : ClientEntityTranslator
{
//singleton
public static ClientEntityTranslator entityTranslator = new
SvcOrderToClientOrder();
private SvcOrderToClientOrder() { }
public override ClientEntities.ClientEntity ToClientEntity(object
serviceOutputEntity)
{
CallBusinessProxy.OrderInfo oi = serviceOutputEntity as
CallBusinessProxy.OrderInfo;
ClientEntities.Order bindableOrder = new ClientEntities.Order();
bindableOrder.OrderNumber = oi.Order.OrderNumber;
//code removed for brevity ...
return bindableOrder;
}
}
public class SvcUserToClientUser : ClientEntityTranslator
{
//code removed for brevity ...
}
public class SvcScriptToClientScript : ClientEntityTranslator
{
//code removed for brevity ...
}
}
Se notato, traduttori sopra sono prive di stato e utilizzano un modello singleton. Il convertitore deve essere in grado di ereditare ClientEntityTranslator per garantire l'uniformità e deve essere un singleton per evitare la varianza del insieme a Garbage Collection.
È possibile mantenere riutilizzare la stessa istanza ogni volta che la chiamata del servizio corrispondente. Inoltre possibile creare ServiOutputEntityTranslator per servizio interazione che richiede di grandi dimensioni dei messaggi di input (che in genere è il caso di chiamata del servizio transazionale) con la seguente definizione di classe:
public abstract class ServiOutputEntityTranslator
{
public abstract object ToServiceOutputEntity(ClientEntity
clientEntity);
}
Se si nota che il valore restituito della funzione precedente, è oggetto, come è possibile non controllano la classe base di entità messaggio (in questa demo che È possibile, ma non nel mondo reale). L'indipendenza verrà essere implementata da traduttori corrispondente. Per maggiore semplicità di una demo, è possibile non salvare tutti i dati al server, in modo che in questa demo non include qualsiasi traduttori per la conversione client entità in entità di messaggio.
Modifica dello stato di Silverlight dopo le chiamate di servizio
Modifica dello stato visivo della Silverlight può essere eseguita solo tramite l'esecuzione di codice sul thread dell'interfaccia utente. Poiché l'esecuzione asincrona del servizio chiama sempre restituisce i risultati il gestore di callback, il gestore verrà essere posizione corretta per apportare modifiche a stato visivo o non visivo dell'applicazione.
Le modifiche dello stato non visivi devono essere scambiate in modo thread-safe se potrebbe essere più servizi tenta di modificare lo stato di condivisione in modo asincrono. È sempre consigliabile verificare Deployment.Current.Dispatcher.CheckAccess() prima di modificare l'interfaccia utente.
Criteri tra domini
A differenza le applicazioni multimediali e le applicazioni che Mostra striscioni pubblicitari, applicazioni line-of-business aziendale reale richiedono integrazione con un'ampia gamma di hosting del servizio ambienti. Ad esempio, l'applicazione di centro di chiamata a cui fa riferimento in tutta l'articolo è tipico dell'applicazione enterprise. Questa applicazione ospitata in un sito Web accede a un server di stato socket per pop dello schermo, in base di WCF servizi Web per l'accesso ai dati line-of-business e può scaricare pacchetti aggiuntivi XAP (package di distribuzione Zipped Silverlight) da un dominio diverso. Viene utilizzata ancora un altro dominio per la trasmissione di dati di strumentazione.
Il sandbox Silverlight non per impostazione predefinita consentire l'rete accesso a qualsiasi dominio diverso dal dominio di origine, ovvero advcallclientweb come visto in figura 1 . Il runtime di Silverlight controlla per i criteri opt in a quando l'applicazione accede a qualsiasi dominio diverso dal dominio di origine. Qui è un tipico elenco degli scenari di hosting di servizi che devono supportare le richieste di criteri tra domini dal client:
- Servizi Web ospitati in un processo del servizio (o un'applicazione console per motivi di semplicità)
- Servizi Web ospitato in IIS o altri Web server
- TCP servizi ospitati in un processo del servizio (o un'applicazione console)
Sono stati illustrati implementazione di criterio tra domini per i servizi TCP mese scorso e pertanto evidenzierà servizi Web ospitati in processi personalizzati e all'interno di IIS.
Mentre è semplice implementare i criteri tra domini per gli endpoint servizio Web ospitati in IIS, in altri casi è necessario conoscere la natura delle richieste di criteri e le risposte.
Cross criteri di dominio per Web Services IIS host esterno
Per la gestione dello stato effettivo, è possibile casi in cui uno può desidera servizi host in un sistema operativo processo IIS esterno. Per accedere a domini di tali servizi WCF, il processo dovrà criteri host nella directory principale dell'endpoint HTTP. Quando viene richiamato un servizio Web tra domini, Silverlight effettua una richiesta HTTP GET per clientaccesspolicy.xml. Se il servizio è ospitato all'interno di IIS, il file clientaccesspolicy.xml può essere copiato la directory principale del sito Web e IIS non il resto di rendere disponibile il file. In caso di hosting personalizzato nel computer locale, http://localhost:<port>/clientaccesspolicy.xml deve essere un URL valido.
Poiché la dimostrazione del centro di chiamata non utilizza i servizi Web ospitati personalizzati, utilizza un semplice TimeService in un'applicazione console per illustrare i concetti. Nella console verrà esporre un endpoint di trasferimento (REST) representational stato utilizzando le nuove funzionalità REST di Microsoft .NET Framework 3.5. Proprietà UriTemplate è necessario impostare con precisione per il valore letterale illustrato nella Figura 7 .
Nella figura 7 implementazione per i servizi WCF ospitato personalizzato
[ServiceContract]
public interface IPolicyService
{
[OperationContract]
[WebInvoke(Method = "GET", UriTemplate = "/clientaccesspolicy.xml")]
Stream GetClientAccessPolicy();
}
public class PolicyService : IPolicyService
{
public Stream GetClientAccessPolicy()
{
FileStream fs = new FileStream("PolicyFile.xml", FileMode.Open);
return fs;
}
}
Il nome di interfaccia o il nome di metodo non ha alcun effetto sul risultato; è possibile scegliere qualsiasi. WebInvoke dispone di altre proprietà quali RequestFormat e ResponseFormat, che sono per impostazione predefinita impostata su XML, non è necessario specificare in modo esplicito. È inoltre sono basarsi sul valore predefinito della proprietà BodyStyle da BodyStyle.Bare, significa che la risposta non essere eseguito il wrapping.
L'implementazione del servizio è molto semplice; semplicemente flussi clientaccesspolicy.xml in risposta alla richiesta del client di Silverlight. Il nome del file dei criteri può essere di propria scelta e si può chiamare qualsiasi. L'implementazione del servizio dei criteri è illustrata nella Figura 7 .
Ora è necessario configurare IPolicyService per stile REST gestione di richieste HTTP. App.config dell'applicazione console (ConsoleWebServices) viene illustrato nella Figura 8 . Nota sulla necessità di una configurazione speciale alcune possibili sono: l'associazione dell'endpoint ConsoleWebServices.IPolicyServer deve essere impostato per webHttpBinding. Inoltre, il comportamento di endpoint IPolicyService deve essere configurato con WebHttpBehavior come illustrato nel file di configurazione. L'indirizzo di base del PolicyService deve essere impostato SULL'URL principale (come in http://localhost:3045/) e l'indirizzo dell'endpoint deve essere lasciata vuota (ad esempio.
Figura 8 impostazioni WCF per ambiente di hosting personalizzato
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<services>
<!-- IPolicyService end point should be configured with
webHttpBinding-->
<service name="ConsoleWebServices.PolicyService">
<endpoint address=""
behaviorConfiguration="ConsoleWebServices.WebHttp"
binding="webHttpBinding"
contract="ConsoleWebServices.IPolicyService" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:3045/" />
</baseAddresses>
</host>
</service>
<service behaviorConfiguration="ConsoleWebServices.TimeServiceBehavior"
name="ConsoleWebServices.TimeService">
<endpoint address="TimeService" binding="basicHttpBinding"
contract="ConsoleWebServices.ITimeService">
</endpoint>
<host>
<baseAddresses>
<add baseAddress="http://localhost:3045/TimeService.svc" />
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<endpointBehaviors>
<!--end point behavior is used by REST endpoints like
IPolicyService described above-->
<behavior name="ConsoleWebServices.WebHttp">
<webHttp />
</behavior>
</endpointBehaviors>
...
</behaviors>
</system.serviceModel>
</configuration>
Infine, è necessario configurare i servizi ospitati console, ad esempio TimeService racchiusi gli esempi di codice, nonché la configurazione, per disporre di un URL simile alle relative controparti IIS. Ad esempio, l'URL di un endpoint TimeService ospitato in IIS in predefinito HTTP potrebbe simile al seguente: http://localhost/TimeService.svc. In questo caso, è possibile ottenere i metadati da http://localhost/TimeService.svc?WSDL.
Tuttavia, nel caso di hosting di console, i metadati è possibile ottenere aggiungendo "? WSDL " all'indirizzo di base dell'host del servizio. Nella configurazione illustrata nella Figura 8 , è possibile visualizzare che l'indirizzo di base del TimeService è impostato su http://localhost:3045/TimeService.svc, pertanto è possibile ottenere i metadati dal http://localhost:3045/TimeService.svc?WSDL.
Questo URL è simile a ciò che viene utilizzata in IIS che ospita. Se si imposta l'indirizzo di base host http://localhost:3045/TimeService.svc/, l'URL di metadati sarà http://localhost:3045/TimeService.svc/?WSDL, aspetto un po'dispari. Pertanto attenzione per questo comportamento come è possibile risparmiare tempo di scoprire l'URL di metadati.
Criteri tra domini per i servizi ospitati all'interno di IIS
Come è stato illustrato in precedenza, distribuzione dei criteri tra domini per i servizi ospitati di IIS è semplice: è sufficiente copiare il file clientaccesspolicy.xml nella directory principale del sito in cui sono ospitati i servizi Web. Come si è visto nella Figura 1 , l'applicazione Silverlight è ospitato su advcallclientweb (localhost:1041) e accede ai servizi aziendali da AdvBusinessServices (localhost:1043). Il runtime di Silverlight richiede clientaccesspolicy.xml essere distribuiti nella directory principale dell'AdvBusinessServices sul sito con il codice mostrato nella Figura 9 .
Nella Figura 9 Clientaccesspolicy.xml per i servizi Web ospitato in IIS
<?xml version="1.0" encoding="utf-8"?>
<access-policy>
<cross-domain-access>
<policy>
<allow-from http-request-headers="*">
<!--allows the access of Silverlight application with localhost:1041
as the domain of origin-->
<domain uri="http://localhost:1041"/>
<!--allows the access of call simulator Silverlight application
with localhost:1042 as the domain of origin-->
<domain uri="http://localhost:1042"/>
</allow-from>
<grant-to>
<resource path="/" include-subpaths="true"/>
</grant-to>
</policy>
</cross-domain-access>
</access-policy>
Se si ricorda il formato di criteri tra domini per il server di socket (advpolicyserver) da prima dispensa di questa serie, il formato di <allow-from> è simile. La differenza si trova nella sezione <grant-to> in cui il server di socket richiede un'impostazione <socket-resource> con gli attributi di intervallo e il protocollo porta, come illustrato di seguito:
<grant-to>
<socket-resource port="4530" protocol="tcp" />
</grant-to>
Se si crea il sito di hosting del servizio WCF utilizzando il modello di sito ASP.NET Web e si aggiungere endpoint WCF in un secondo momento, il test di server Web verrà associato alla directory virtuale il nome del progetto (ad esempio " / AdvBusinessServices "). Questo deve essere modificato da "/" nelle pagine delle proprietà del progetto in modo che il clientaccesspolicy.xml può essere soddisfatta dalla directory principale. Se si non modifica questo, non sarà il clientaccesspolicy.xml nella directory principale e le applicazioni Silverlight riceveranno gli errori del server quando si accede al servizio. Si noti che questo non è un problema per i siti Web creati utilizzando il modello di progetto WCF Web servizio.
Controllo di accesso nella figura 10 tramite PasswordBox
<UserControl x:Class="AdvCallCenterClient.Login">
<Border x:Name="LayoutRoot" ... >
<Grid x:Name="gridLayoutRoot">
<Border x:Name="borderLoginViw" ...>
<TextBlock Text="Pleae login.." Style="{StaticResource headerStyle}"/>
<TextBlock Text="Rep ID" Style="{StaticResource labelStyle}"/>
<TextBox x:Name="txRepID" Style="{StaticResource valueStyle}"/>
<TextBlock Text="Password" Style="{StaticResource labelStyle}"/>
<PasswordBox x:Name="pbPassword" PasswordChar="*"/>
<HyperlinkButton x:Name="hlLogin" Content="Click to login"
ToolTipService.ToolTip="Clik to login" Click="hlLogin_Click" />
</Border>
<TextBlock x:Name="tbLoginStatus" Foreground="Red" ... />
...
</UserControl>
public partial class Login : UserControl
{
public Login()
{
InitializeComponent();
}
public event EventHandler<EventArgs> OnSuccessfulLogin;
private void hlLogin_Click(object sender, RoutedEventArgs e)
{
//validate the login
AuthenticationProxy.AuthenticationServiceClient authService
= new AuthenticationProxy.AuthenticationServiceClient();
authService.LoginCompleted += new
EventHandler< AuthenticationProxy.LoginCompletedEventArgs>
(authService_LoginCompleted);
authService.LoginAsync(this.txRepID.Text, this.pbPassword.Password,
null, false);
}
void authService_LoginCompleted(object sender,
AuthenticationProxy.LoginCompletedEventArgs e)
{
if (e.Result == true)
{
if (OnSuccessfulLogin != null)
OnSuccessfulLogin(this, null);
}
else
{
this.tbLoginStatus.Text = "Invalid user id or password";
}
}
}
Protezione dell'applicazione
Uno dei requisiti critici di un'applicazione line-of-business è l'autenticazione, prima che l'agente del centro di chiamata possa iniziare lo spostamento, verrà autenticati assegnando un ID utente e una password. Nelle applicazioni Web ASP.NET, questo può facilmente scopo sfruttare il provider di appartenenza e i controlli di accesso ASP.NET lato server. In Silverlight, esistono due modi per attivare l'autenticazione: autenticazione all'esterno e autenticazione all'interno.
Autenticazione all'esterno è molto semplice ed è simile all'implementazione di autenticazione delle applicazioni ASP.NET. Con questo approccio, l'autenticazione avviene in una pagina Web basate su ASP.NET prima che venga visualizzata l'applicazione Silverlight. Il contesto di autenticazione può essere trasferito nell'applicazione Silverlight tramite InitParams parametro prima di un'applicazione Silverlight viene caricata o mediante una personalizzato chiamata servizio Web (per estrarre le informazioni di stato di autenticazione) dopo l'applicazione viene caricata.
Questo approccio è la posizione quando l'applicazione Silverlight è parte del sistema basate su ASP.NET e HTML più grande. Tuttavia, nei casi in cui Silverlight è il driver principale dell'applicazione, è naturale per eseguire l'autenticazione in Silverlight. Utilizza il controllo Silverlight 2 PasswordBox per acquisire la password e l'autenticazione utilizzando l'endpoint WCF AuthenticationService ASP.NET per convalidare le credenziali dell'utente. AuthenticationService, ProfileService e RoleService fanno parte del nuovo namespace—System.Web.ApplicationServices—which era Novità di .NET Framework 3.5. Nella figura 10 viene illustrata l'XAML per il controllo Login creato a questo scopo. Il controllo di accesso chiama AuthenticationService.LoginAsync() ASP.NET con l'ID utente e la password immessa.
Nella figura 11 controllo di accesso personalizzato Silverlight
La schermata di accesso del centro di chiamata, illustrato nella Figura 11 non sofisticato ma svolge la funzione di una demo. HO implementato un gestore per gestire l'evento LoginCompleted all'interno del controllo in modo che è indipendente per la visualizzazione dei messaggi di accesso non valido e dialogues di reimpostazione password per sofisticate implementazioni. Al momento un accesso, l'evento OnSuccessfulLogin verrà attivata per indicare al controllo padre (Application.RootVisual in questo caso) per visualizzare la prima schermata dell'applicazione compilata con le informazioni utente.
Il gestore LoginCompleted (ctrlLoginView_OnSuccessfulLogin) trova all'interno di principale Silverlight pagina richiama il servizio di profilo ospitato dal sito Web servizi aziendali, come illustrato nella Figura 12 . AuthenticationService per impostazione predefinita non è mappata a qualsiasi endpoint .svc; pertanto, verrà associato file .svc l'implementazione fisica, come illustrato di seguito:
<!-- AuthenticationService.svc -->
<%@ ServiceHost Language="C#" Service="System.Web.ApplicationServices.
AuthenticationService" %>
Nella figura 12 Utilizzo di Login.xaml all'interno il Page.xaml
<!-- Page.xaml of the main UserControl attached to RootVisual-->
<UserControl x:Class="AdvCallCenterClient.Page" ...>
<page:Login x:Name="ctrlLoginView" Visibility="Visible"
OnSuccessfulLogin="ctrlLoginView_OnSuccessfulLogin"/>
...
</UserControl>
<!-- Page.xaml.cs of the main UserControl attached to RootVisual-->
public partial class Page : UserControl
{
...
private void ctrlLoginView_OnSuccessfulLogin(object sender, EventArgs e)
{
Login login = sender as Login;
login.Visibility = Visibility.Collapsed;
CallBusinessProxy.UserProfileClient userProfile
= new CallBusinessProxy.UserProfileClient();
userProfile.GetUserCompleted += new
EventHandler<GetUserCompletedEventArgs>(userProfile_GetUserCompleted);
userProfile.GetUserAsync(login.txRepID.Text);
}
...
void userProfile_GetUserCompleted(object sender,
GetUserCompletedEventArgs e)
{
CallBusinessProxy.User user = e.Result;
UserToBindableUser utobu = new UserToBindableUser(user);
ClientGlobals.currentUser = utobu.Translate() as ClientEntities.User;
//all the time the service calls will be complete on a worker thread
//so the following check is redunant but done to be safe
if (!this.Dispatcher.CheckAccess())
{
this.Dispatcher.BeginInvoke(delegate()
{
this.registrationView.DataContext = ClientGlobals.currentUser;
this.ctrlLoginView.Visibility = Visibility.Collapsed;
this.registrationView.Visibility = Visibility.Visible;
});
}
}
}
Silverlight può solo chiamare servizi Web che sono configurati per essere chiamato dagli ambienti di script, ad esempio AJAX. Come tutti i servizi richiamabili AJAX, il servizio AuthenticationService deve accesso all'ambiente runtime di ASP.NET. Forniscono l'accesso impostando direttamente sotto il nodo <system.servicemodel>. Affinché il servizio di autenticazione per essere richiamabile dal processo di accesso di Silverlight (o da chiamare da AJAX), il file web.config deve possibile impostare in base alle indicazioni fornite nell'" Procedura: attivare il servizio di autenticazione WCF." Se vengono create mediante il modello WCF Service Silverlight-enabled all'interno della categoria di Silverlight i servizi verranno configurati automaticamente per Silverlight.
Nella figura 13 illustrata la configurazione modificata con elementi importanti necessarie per il servizio di autenticazione. Oltre a configurazione del servizio inoltre sostituito l'impostazione di configurazione di SQL Server per aspnetdb che memorizza le informazioni di autenticazione. Machine.config definisce un'impostazione di LocalSqlServer prevede aspnetdb.mdf per essere incorporato nella directory App_Data del sito. Questa impostazione di configurazione, viene rimossa l'impostazione predefinita e fa riferimento a aspnetdb collegata l'istanza di SQL Server. Può essere modificato facilmente per puntare a un'istanza di database in esecuzione su un computer separato.
Figura 13 impostazioni per servizio di autenticazione ASP.NET
//web.config
<Configuration>
<connectionStrings>
<!-- removal and addition of LocalSqlServer setting will override the
default asp.net security database used by the ASP.NET Configuration tool
located in the Visul Studio Project menu-->
<remove name="LocalSqlServer"/>
<add name="LocalSqlServer" connectionString="Data
Source=localhost\SqlExpress;Initial Catalog=aspnetdb; ... />
</connectionStrings>
<system.web.extensions>
<scripting>
<webServices>
<authenticationService enabled="true" requireSSL="false"/>
</webServices>
</scripting>
</system.web.extensions>
...
<authentication mode="Forms"/>
...
<system.serviceModel>
<services>
<service name="System.Web.ApplicationServices.AuthenticationService"
behaviorConfiguration="CommonServiceBehavior">
<endpoint
contract="System.Web.ApplicationServices.AuthenticationService"
binding="basicHttpBinding" bindingConfiguration="useHttp"
bindingNamespace="https://asp.net/ApplicationServices/v200"/>
</service>
</services>
<bindings>
<basicHttpBinding>
<binding name="useHttp">
<!--for production use mode="Transport" -->
<security mode="None"/>
</binding>
</basicHttpBinding>
</bindings>
...
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
</system.serviceModel>
</configuration>
Per mantenere l'incapsulamento del controllo di accesso e per mantenere libera associazione con il controllo padre fase di progettazione, per l'evento OnSuccessfulLogin verrà comunicata processo di accesso della ha avuto esito positivo. Application.RootVisual (che è una classe Page) verrà eseguito il processo aziendale necessarie per visualizzare la prima schermata all'accesso riuscito. La prima schermata visualizzata dopo un accesso è registrationView, come illustrato nel metodo userProfile_GetUserCompleted della Figura 12 . Prima che venga visualizzata in questa visualizzazione, verranno recuperare le informazioni utente chiamando CallBusinessProxy.UserProfileClient.GetUserAsync(). Prendere nota della chiamata asincrona del servizio, che è simile per l'integrazione del servizio business che verrà illustrato più avanti.
Tenere presente che la configurazione precedente non utilizza secure sockets layer (SSL), è necessario modificare per utilizzare SSL durante la creazione di sistemi di produzione.
Nella figura 14 controllo OrderDetails.xaml con dettagli sugli ordini popolati
Partizionamento delle applicazioni
Uno dei fattori che contribuiscono al tempo di avvio dell'applicazione Silverlight costituzione è la dimensione del pacchetto iniziale. Le indicazioni per la dimensione del pacchetto XAP sono non diversa spessore di pagina per le applicazioni Web. Larghezza di banda è una risorsa limitata. L'ora di risposta rigide dei applicazioni Web richiede che si prestano attenzione chiusura il tempo di avvio dell'applicazione Silverlight.
Oltre al tempo di elaborazione dedicato prima che venga visualizzata UserControl prima la dimensione del pacchetto dell'applicazione dispone di influenza direttamente su questo importante qualità dell'applicazione. Per migliorare la velocità di avvio, è necessario evitare l'file XAP monolitica possono aumentare a decine di MB le dimensioni per le applicazioni complesse.
L'applicazione Silverlight può essere suddiviso in un insieme di file XAP; DLL singole; o singoli file XML, immagini e qualsiasi altro tipo con tipi MIME riconosciuti. Nello scenario del centro di chiamata, per illustrare il partizionamento granulare dell'applicazione, verrà distribuito il controllo Silverlight OrderDetail come una DLL separata (AdvOrderClientControls.dll) con AdvCallCenterClient.xap nella directory del progetto AdvCallClientWeb ClientBin (fare riferimento nuovamente All'figura 1 ).
La DLL verrà scaricata preventivamente sul thread di lavoro quando l'agente accetta la chiamata in entrata. La chiamata si è visto nella Figura 4 —ThreadPool.QueueUserWorkItem (ExecuteControlDownload), è responsabile di questa. Una volta che il chiamante delle risposte alle domande protezione, utilizzerà reflection per creare un'istanza del controllo OrderDetail e aggiungerla alla struttura dei controlli prima di visualizzare sullo schermo. Figura 14 Mostra OrderDetail.xaml controllo caricato nella struttura dei controlli con dettagli dell'ordine popolati.
La DLL contenente il controllo OrderDetail viene distribuita allo stesso sito Web come client di centro di chiamata, è tipico delle DLL che appartengono a stessa applicazione, in modo che eventuali problemi di domini non incorrono in questo caso. Tuttavia, questo potrebbe non essere nel caso dei servizi, le applicazioni Silverlight possono accedere ai servizi di distribuzione su più domini, inclusi quelli locali e nell'area illustrato nel diagramma Architettura (fare nuovamente riferimento nuovamente a figura 1 ).
Il metodo ExecuteControlDownload (vedere la Figura 4 ) viene eseguito su un thread di lavoro di sfondo e utilizza la classe WebClient per il download della DLL. Per impostazione predefinita, WebClient, presuppone che il download avviene dal dominio di origine e quindi utilizza solo gli URI relativi.
Il gestore di OrderDetailControlDownloadCallback riceve il flusso DLL e consente di creare l'assembly utilizzando ResourceUtility.GetAssembly() illustrato nella Figura 15 . Poiché la creazione di assembly deve verificarsi sul thread UI, verrà inviare il GetAssembly() e l'assegnazione (thread-safe) dell'assembly dalla variabile globale per il thread dell'interfaccia utente:
void OrderDetailControlDownloadCallback(object sender,
OpenReadCompletedEventArgs e)
{
this.Dispatcher.BeginInvoke(delegate() {
Assembly asm = ResourceUtility.GetAssembly(e.Result);
Interlocked.Exchange<Assembly>(ref
ClientGlobals.advOrderControls_dll, asm ); });
}
Figura 15 funzioni di utilità di estrazione delle risorse
public class ResourceUtility
{
//helper function to retrieve assembly from a package stream
public static Assembly GetAssembly(string assemblyName, Stream
packageStream)
{
StreamResourceInfo srInfo =
Application.GetResourceStream(
new StreamResourceInfo(packageStream, "application/binary"),
new Uri(assemblyName, UriKind.Relative));
return GetAssembly(srInfo.Stream);
}
//helper function to retrieve assembly from a assembly stream
public static Assembly GetAssembly(Stream assemblyStream)
{
AssemblyPart assemblyPart = new AssemblyPart();
return assemblyPart.Load(assemblyStream);
}
//helper function to create an XML document from the stream
public static XElement GetXmlDocument(Stream xmlStream)
{
XmlReader reader = XmlReader.Create(xmlStream);
XElement element = XElement.Load(reader);
return element;
}
//helper function to create an XML document from the default package
public static XElement GetXmlDocumentFromXap(string fileName)
{
XmlReaderSettings settings = new XmlReaderSettings();
settings.XmlResolver = new XmlXapResolver();
XmlReader reader = XmlReader.Create(fileName);
XElement element = XElement.Load(reader);
return element;
}
//gets the UIElement from the default package
public static UIElement GetUIElementFromXaml(string xamlFileName)
{
StreamResourceInfo streamInfo = Application.GetResourceStream(new
Uri(xamlFileName, UriKind.Relative));
string xaml = new StreamReader(streamInfo.Stream).ReadToEnd();
UIElement uiElement = null;
try
{
uiElement = (UIElement)XamlReader.Load(xaml);
}
catch
{
throw new SLApplicationException(string.Format("Can't create
UIElement from {0}", xamlFileName));
}
return uiElement;
}
}
Poiché il delegato inviato viene eseguita in un thread diverso da quello il gestore di callback, è necessario essere cosciente dello stato di oggetti che si accede da delegato anonimo. Nel codice precedente, lo stato del flusso DLL scaricato è realmente importante. È non possibile scrivere codice che recupera le risorse del flusso della funzione OrderDetailControlDownloadCallback. Tale codice verrà eliminare in modo anomalo del flusso scaricato prima thread dell'interfaccia utente ha la possibilità di creare l'assembly. Sarà utilizzare reflection per creare un'istanza del controllo utente OrderDetail e aggiungere al Pannello di come illustrato di seguito:
_orderDetailContol = ClientGlobals.advOrderControls_dll.CreateInstance
("AdvOrderClientControls.OrderDetail") as UserControl;
spCallProgressPanel.Children.Add(_orderDetailContol);
ResourceUtility in Nella figura 15 Mostra inoltre varie funzioni di utilità per estrarre UIElement del codice XAML e il documento XML da flussi scaricati i pacchetti predefinito.
Produttività e oltre
Sono esaminati Silverlight dal punto di vista di applicazione aziendale tradizionale, intervenire diversi aspetti dell'architettura dell'applicazione. Implementazione delle notifiche di push con i socket di Silverlight è un enabler degli scenari line-of-business quali centri di chiamata. Con la prossima versione di Internet Explorer 8.0, che è pianificato per includere sei connessioni HTTP simultanee per host, push notifica implementazione su Internet verrà più complete e interessanti quando utilizza l'associazione WCF fronte retro. Integrazione con i dati line-of-business e processi è semplice come nel caso in tradizionali applicazioni desktop.
Sarà un incremento di produttività di grandi dimensioni se confrontata con AJAX e altre piattaforme di applicazione (RIA) Internet RTF. Le applicazioni Silverlight possono essere protette tramite gli endpoint di autenticazione e autorizzazione WCF forniti da ASP.NET nella versione più recente. Spero che questo piccolo esplorazione dello sviluppo di applicazioni line-of-business con Silverlight motivate per eseguire Silverlight oltre a file multimediali e gli scenari di annunci.
Hanu Kommalapati è una preparazione strategia Platform in Microsoft e in questo ruolo ha notifiche clienti aziendali in applicazioni scalabili line-of-business in Silverlight e servizi Azure piattaforme.