Utiliser SafeDispatcher pour les contrôles hébergés personnalisés dans Unified Service Desk
Unified Service Desk est une application basée sur Windows Presentation Foundation (WPF) où toutes les opérations dans Unified Service Desk sont exécutées sur le fil de discussion Répartiteur WPF. La classe Répartiteur WPF fournit des services de gestion de la file d’attente des éléments de travail d’un thread.
Vous pouvez étendre Unified Service Desk en créant des contrôles personnalisés et en les hébergeant dans Unified Service Desk. Toutefois, si un contrôle hébergé personnalisé contient un code erroné ou effectue des opérations à l’aide de nouveaux threads sans gérer de manière appropriée les exceptions pendant l’exécution du code, des problèmes de stabilité peuvent se produire dans Unified Service Desk, et l’application cliente peut même se figer ou se bloquer. Les exceptions non gérées dans les contrôles personnalisés tiers rendent difficiles l’identification, le dépannage et la résolution du problème par l’équipe de produit/support, car elle n’a pas peut-être pas accès aux informations sur la raison pour laquelle une exception ou une erreur s’est produite dans Unified Service Desk, et au code exact à l’origine de l’erreur.
Introduction de SafeDispatcher qui fournit un mécanisme puissant et informatif de gestion des exceptions pour les contrôles hébergés personnalisés dans Unified Service Desk en assurant la journalisation par défaut des exceptions non gérées avec des informations détaillées sur la source et la cause de l’exception, et en vous permettant de configurer ou de remplacer la gestion des exceptions SafeDispatcher pour exécuter d’autres étapes. Cela empêche également le client Unified Service Desk de se bloquer à cause des exceptions non gérées dans le code de contrôle hébergé personnalisé.
Qu’est ce que SafeDispatcher ?
SafeDispatcher est conçu selon les mêmes principes que le Répartiteur WPF, et assure la gestion résiliente et informative des exceptions pour les contrôles hébergés personnalisés dans Unified Service Desk. Il est exposé en tant que propriété protégée, SafeDispatcher, dans la classe DynamicsBaseHostedControl, ce qui rend SafeDispatcher automatiquement disponible pour tous les contrôles hébergés personnalisés de Unified Service Desk qui sont dérivés de la classe DynamicsBaseHostedControl.
Note
N’utilisez pas la classe SafeDispatcher dans votre code à utiliser avec SafeDispatcher. À la place, vous devez utiliser la propriété SafeDispatcher de votre instance de contrôle hébergé personnalisé qui est dérivée de la classe DynamicsBaseHostedControl pour utiliser SafeDispatcher.
Comme le Répartiteur WPF, SafeDispatcher fournit des méthodes telles que BeginInvoke, Invoke et InvokeAsync pour exécuter des opérations de manière synchrone ou asynchrone sur SafeDispatcher avec un paramètre booléen supplémentaire, runOnMainUiThread
, qui contrôle si SafeDispatcher doit être exécuté ou non sur le thread d’interface utilisateur.
SafeDispatcher présente les avantages suivants :
Thread de répartiteur d’interface utilisateur protégé : les développeurs peuvent exécuter toutes les opérations dépendantes de l’interface utilisateur sur SafeDispatcher en définissant le paramètre
runOnMainUiThread
sur « true » dans la méthode d’appel pour exécuter SafeDispatcher sur le thread d’interface utilisateur. Les exceptions non gérées déclenchées sur le répartiteur d’interface utilisateur principal seront gérées en toute sécurité au niveau du contrôle hébergé au lieu d’accéder au gestionnaire de l’Événement DispatcherUnhandledException global.Thread autre qu’un thread de répartiteur d’interface utilisateur protégé : les développeurs peuvent exécuter l’intégralité du code non dépendant de l’interface utilisateur sur SafeDispatcher en définissant le paramètre
runOnMainUiThread
sur « false » dans la méthode d’appel pour exécuter SafeDispatcher sur un thread autre qu’un thread d’interface utilisateur. Les exceptions non gérées déclenchées sur le répartiteur hors interface utilisateur principal seront gérées en toute sécurité au niveau du contrôle hébergé au lieu d’accéder au gestionnaire de l’Événement DispatcherUnhandledException global.Informations détaillées sur la source et la cause de l’exception : le gestionnaire d’exceptions SafeDispatcher est déclenché lorsqu’une exception non gérée est déclenchée au niveau du DynamicsBaseHostedControl par le thread d’interface utilisateur ou un thread autre qu’un thread d’interface utilisateur, ce qui permet à Unified Service Desk de capturer des informations critiques au niveau du contrôle hébergé, comme le nom du contrôle hébergé, le type de contrôle hébergé, le nom de la méthode et la trace complète de la pile pour identifier l’emplacement et la cause exacts de l’exception.
Configurer ou remplacer le gestionnaire d’exceptions SafeDispatcher : les développeurs peuvent utiliser le comportement par défaut du gestionnaire d’exceptions SafeDispatcher pour demander à l’utilisateur des informations sur l’exception non gérée ou remplacer le comportement selon les besoins de leur entreprise, par exemple pour configurer la journalisation supplémentaire, fermer les contrôles de session ou quitter le client Unified Service Desk.
Comment utiliser SafeDispatcher ?
La propriété SafeDispatcher est disponible pour toutes les instances de contrôle hébergé personnalisé de Unified Service Desk qui sont dérivées de la classe DynamicsBaseHostedControl. Une instance SafeDispatcher peut être exécutée sur le thread d’interface utilisateur lorsque le contrôle hébergé personnalisé est initialisé. Toutefois, une instance de SafeDispatcher peut être exécutée sur un thread autre qu’un thread d’interface utilisateur uniquement lorsque vous exécutez la méthode d’appel pour la première fois.
Appelez de manière synchrone une fonction spécifique à l’interface utilisateur à l’aide de SafeDispatcher.
SafeDispatcher.Invoke(() => { ProcessData(); }, DispatcherPriority.Normal, CancellationToken.None, true);
OU
SafeDispatcher.Invoke(() => { ProcessData(); }, DispatcherPriority.Normal, CancellationToken.None);
Note
Pour une fonction spécifique à l’interface utilisateur, vous devez définir le paramètre facultatif
runOnMainUiThread
sur « true ». Si vous ne spécifiez pas de valeur pour ce paramètre, la valeur « true » est passée par défaut. Par conséquent, les définitions de méthode ci-dessus conviennent parfaitement.Appelez de manière asynchrone une fonction spécifique à l’interface utilisateur à l’aide de SafeDispatcher. Vous pouvez utiliser la méthode
BeginInvoke
ouInvokeAsync
.SafeDispatcher.BeginInvoke(new Action(() => { ProcessData(); }));
OU
SafeDispatcher.InvokeAsync(new Action(() => { ProcessData(); }));
Personnaliser le gestionnaire d’exceptions SafeDispatcher
Avec l’introduction de SafeDispatcher, toutes les exceptions non gérées dans Unified Service Desk déclencheront l’événement SafeDispatcherUnhandledException au lieu de l’événement DispatcherUnhandledException global. La méthode SafeDispatcherUnhandledExceptionHandler fournit un gestionnaire d’exceptions par défaut pour permettre à SafeDispatcher d’afficher des informations à l’utilisateur Unified Service Desk avec les détails suivants : le contrôle source où l’exception s’est produite et des informations détaillées sur l’exception.
Vous pouvez également remplacer la gestion par défaut des exceptions pour SafeDispatcher pour exécuter d’autres opérations, comme inviter l’utilisateur à fermer un contrôle hébergé non dynamique basé sur une session.
L’exemple de code suivant montre comment remplacer le gestionnaire d’exceptions SafeDispatcher par défaut pour afficher un message invitant l’utilisateur à fermer le contrôle hébergé personnalisé lorsqu’une exception se produit :
protected override void SafeDispatcherUnhandledExceptionHandler(object sender, SafeDispatcherUnhandledExceptionEventArgs ex)
{
string error = String.Format(CultureInfo.InvariantCulture,
"Error in hosted control Application:{0} - Exception : {1} \r\nInnerException\r\n {2}", this.ApplicationName, ex.Exception, ex.InnerException);
DynamicsLogger.Logger.Log(error, TraceEventType.Error);
if (MessageBox.Show("Exception occurred in hosted control - " + this.ApplicationName + ".Do you wish to close it ?", "Question", MessageBoxButton.YesNo,
MessageBoxImage.Warning) == MessageBoxResult.Yes)
{
SafeDispatcher.BeginInvoke(() => { this.desktopAccess.CloseDynamicApplication(this.ApplicationName); });
}
}
Migration du répartiteur WPF vers SafeDispatcher dans les contrôles hébergés personnalisés existants
Comme le contrat entre le répartiteur WPF et SafeDispatcher est presque identique, peu d’efforts sont nécessaires pour passer du répartiteur WPF à SafeDispatcher. Pour migrer une instance de contrôle hébergé dérivée de la classe DynamicsBaseHostedControl, remplacez toutes les instances de « Répartiteur » par « SafeDispatcher ».
Par exemple, prenez le code suivant :
Dispatcher.Invoke((System.Action)delegate()
{
DynamicsLogger.Logger.Log("Raising SetupHotKey's", TraceEventType.Verbose);
SetupHotkeys();
CRMGlobalManager.AppWithFocusChanged += CRMGlobalManager_AppWithFocusChanged;
FireEvent("DesktopReady");
InitializeFocusSelection();
});
Remplacez Dispatcher
dans le code ci-dessus par SafeDispatcher
; le reste du code est inchangé :
SafeDispatcher.Invoke((System.Action)delegate()
{
DynamicsLogger.Logger.Log("Raising SetupHotKey's", TraceEventType.Verbose);
SetupHotkeys();
CRMGlobalManager.AppWithFocusChanged += CRMGlobalManager_AppWithFocusChanged;
FireEvent("DesktopReady");
InitializeFocusSelection();
});
Éléments à prendre en compte lors de l’utilisation de SafeDispatcher
SafeDispatcher offre un modèle multithread qui est très utile pour la répartition synchrone et asynchrone des fonctions sur les threads d’interface utilisateur et les threads autres que des threads d’interface utilisateur. Les opérations qui doivent être exécutées sur les threads prenant en charge la tolérance de panne doivent être effectuées sur SafeDispatcher. Néanmoins, le multithreading doit être implémenté avec précaution pour éviter un blocage entre les threads. C’est le cas, par exemple, lorsque vous effectuez une répartition synchrone d’un thread autre qu’un thread d’interface utilisateur sur le répartiteur WPF principal. Prenons cet exemple :
Thread thread = new Thread(() =>
{
Dispatcher.Invoke(ProcessData);
});
thread.SetApartmentState(ApartmentState.STA);
thread.Priority = ThreadPriority.Highest;
thread.IsBackground = true;
thread.Start();
thread.Join();
La méthode thread.Join()
provoque le blocage du thread principal qui attend la fin du thread unique cloisonné (STA, Single-Threaded Apartment) mais le thread STA attend que le thread principal termine l’exécution de ProcessData
. Votre application est confrontée à un blocage.
De même, prenez l’exemple suivant :
// Invoke on STA thread
SafeDispatcher.Invoke(() =>
{
// Invoke back on main dispatcher
SafeDispatcher.Invoke(() =>
{
ProcessData();
});
}, false);
La méthode SafeDispatcherUnhandledExceptionHandler sera appelée si une exception se produit sur le répartiteur WPF ou un thread autre qu’un thread d’interface utilisateur STA, et sera déclenchée sur le thread correspondant sur lequel l’exception s’est produite. Vous devez veiller à ne pas placer la combinaison ci-dessus dans ce gestionnaire, c’est-à-dire si l’exception s’est produite sur un thread autre qu’un thread d’interface utilisateur, n’effectuez pas de répartition synchrone sur le répartiteur d’interface utilisateur principal.
protected override void SafeDispatcherUnhandledExceptionHandler(object sender, SafeDispatcherUnhandledExceptionEventArgs ex)
{
Dispatcher.Invoke(LogException); // Incorrect
SafeDispatcher.Invoke(LogException); // Incorrect
SafeDispatcher.BeginInvoke(LogException); // Correct
SafeDispatcher.InvokeAsync(LogException); // Correct
}
Voir aussi
Créer le contrôle hébergé Unified Service Desk personnaliséÉtendre Unified Service DeskConfigurer la journalisation des diagnostics clients Unified Service Desk