在 Unified Service Desk 中为自定义托管控件使用 SafeDispatcher。 

适用于: Dynamics 365 (online),Dynamics 365 (on-premises),Dynamics CRM 2013,Dynamics CRM 2015,Dynamics CRM 2016

Unified Service Desk 是基于 Windows Presentation Foundation (WPF) 的应用程序,在此应用程序中,Unified Service Desk 中的所有操作均在主 WPF 调度程序执行绪上执行。WPF 调度程序类提供用于管理执行绪的工作项队列的服务。

可以通过创建自定义控件以及在 Unified Service Desk 内托管来扩展 Unified Service Desk。 但是,如果自定义托管控件包含错误代码或使用新执行绪执行操作,而未在代码执行时相应处理异常,这可能会导致 Unified Service Desk 中出现稳定性问题,甚至可能导致客户端应用程序被冻结或无响应。 第三方自定义控件中未处理的异常让产品/支持团队发现、排除和解决问题变得有挑战性,因为他们可能无法访问 Unified Service Desk 中发生错误/异常的原因的信息,以及导致错误的错误代码。

引入 SafeDispatcher,它通过为未处理的异常提供现成的日志记录(包含有关异常的来源和原因的详细信息),并让您配置或覆盖 SafeDispatcher 异常处理来执行某些其他步骤,来为 Unified Service Desk 中的自定义控件提供强大有用的异常处理机制。 同时还可避免 Unified Service Desk 客户端由于自定义托管控件代码中未处理的异常不作响应。

备注

此功能在 Unified Service Desk 2.2.1 中引入。

本主题内容

什么是 SafeDispatcher?

如何使用 SafeDispatcher?

在现有自定义托管控件中从 WPF 调度程序迁移到 SafeDispatcher

使用 SafeDispatcher 时要考虑的事项

什么是 SafeDispatcher?

SafeDispatcher 在与 WPF Dispatcher 相同的行上构建,为 Unified Service Desk 内的自定义托管控件提供灵活的有用异常处理。 它呈现为受保护的属性 SafeDispatcher,位于 DynamicsBaseHostedControl 类,让 SafeDispatcher 对源自 DynamicsBaseHostedControl 类的所有 Unified Service Desk 自定义托管控件自动可用。

备注

请勿使用您的代码中的 SafeDispatcher 类来使用 SafeDispatcher。 相反,您必须使用源自 DynamicsBaseHostedControl 类的自定义托管控件实例中的 SafeDispatcher 属性来使用 SafeDispatcher。

就像 WPF 计划一样,SafeDispatcher 提供诸如 BeginInvokeInvokeInvokeAsync 等方法来使用其他布尔参数 runOnMainUiThread(控制是否在 UI 执行绪上运行 SafeDispatcher)来在 SafeDispatcher 上同步或异步运行操作。

SafeDispatcher 提供以下好处:

  • 保护 UI 调度程序执行绪:开发人员通过在调用方法中将 runOnMainUiThread 参数设置为 "true" 来运行 UI 执行绪上的 SafeDispatcher 来运行 SafeDispatcher 中的所有 UI 相关操作。 在主 UI 调度程序未处理的所有异常将在托管控件级别安全处理,而不是推送到 DispatcherUnhandledException 事件 处理程序。

  • 受保护的非 UI 调度程序执行绪:开发人员可以在 SafeDispatcher 上运行所有独立于 UI 的代码。 通过在调用方法中将 runOnMainUiThread 参数设置为 "false" 来运行非 UI 执行绪上的 SafeDispatcher。 在主非 UI 调度程序未处理的所有异常将在托管控件级别安全处理,而不是推送到 DispatcherUnhandledException 事件 处理程序。

  • 有关异常来源和原因的详细信息::当 UI 或非 UI 执行绪在 DynamicsBaseHostedControl 级别引发未处理异常时,将引发 SafeDispatcher 异常处理程序,这可以让 Unified Service Desk 在托管控件级别捕获重要信息,如托管控件名称、托管控件类型、方法名称和完整的堆栈跟踪,来确定准确的异常位置和原因。

  • 配置或替代 SafeDispatcher 异常处理程序:开发人员可以利用现成的 SafeDispatcher 异常处理程序行为提示用户有关未处理异常的信息,或根据其业务需求替代行为,如配置其他日志记录、关闭基于会话的控件或退出 Unified Service Desk 客户端。

如何使用 SafeDispatcher?

SafeDispatcher 属性对源自 DynamicsBaseHostedControl 类的所有 Unified Service Desk 自定义托管控件实例可用。 当初始化自定义托管控件时,SafeDispatcher 实例将可用于在 UI 执行绪上运行。 但是,SafeDispatcher 仅在您初次执行调用方法时可以在非 UI 执行绪上运行。

  • 使用 SafeDispatcher 同步调用 UI 特定函数

    SafeDispatcher.Invoke(() =>
                {
                    ProcessData();
                }, DispatcherPriority.Normal, CancellationToken.None, true);
    

    SafeDispatcher.Invoke(() =>
                {
                    ProcessData();
                }, DispatcherPriority.Normal, CancellationToken.None);
    

    备注

    有关 UI 特定函数,则应将 runOnMainUiThread 可选参数设置为 "true"。 如果不为此参数指定值,默认传递 "true"。 因此,上述任何方法定义均适用。

  • 使用 SafeDispatcher 异步调用 UI 特定函数。 您可以使用 BeginInvokeInvokeAsync 方法。

    SafeDispatcher.BeginInvoke(new Action(() =>
                {
                   ProcessData();
                }));
    

    SafeDispatcher.InvokeAsync(new Action(() =>
                {
                   ProcessData();
                }));
    

自定义 SafeDispatcher 异常处理程序

引入 SafeDispatcher 后,Unified Service Desk 中的所有未处理的异常将引发 SafeDispatcherUnhandledException Event,而不是全局 DispatcherUnhandledException 事件SafeDispatcherUnhandledExceptionHandler Method为 SafeDispatcher 提供现成的异常处理程序以向 Unified Service Desk 用户显示包含以下详细信息的信息:出现异常的源控件以及关于该异常的详细信息。

还可以替代 SafeDispatcher 的现成异常处理,以执行其他操作,如提示用户关闭基于会话的非动态托管控件。

以下示例代码演示了如何替代现成的 SafeDispatcher 异常处理程序以显示消息框、在发生异常时提示用户关闭自定义托管控件:

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); });
    }
}

在现有自定义托管控件中从 WPF 调度程序迁移到 SafeDispatcher

由于 WPF 调度程序和 SafeDispatcher 之间的合同几乎相同,从 WPF 调度程序迁移到 SafeDispatcher 的工作是最少的。 若要迁移源自 DynamicsBaseHostedControl 类的任何托管控件实例,应将所有 "Dispatcher" 实例替换为 "SafeDispatcher"。

例如,考虑以下代码:

Dispatcher.Invoke((System.Action)delegate()
{
    DynamicsLogger.Logger.Log("Raising SetupHotKey's", TraceEventType.Verbose);
    SetupHotkeys();
    CRMGlobalManager.AppWithFocusChanged += CRMGlobalManager_AppWithFocusChanged;
    FireEvent("DesktopReady");
    InitializeFocusSelection();
});

将面代码中的 SafeDispatcher 替换为 Dispatcher,代码的其余部分保持相同:

SafeDispatcher.Invoke((System.Action)delegate()
{
    DynamicsLogger.Logger.Log("Raising SetupHotKey's", TraceEventType.Verbose);
    SetupHotkeys();
    CRMGlobalManager.AppWithFocusChanged += CRMGlobalManager_AppWithFocusChanged;
    FireEvent("DesktopReady");
    InitializeFocusSelection();
});

使用 SafeDispatcher 时要考虑的事项

SafeDispatcher 提供了多线程模型,在向 UI 或非 UI 执行绪同步或异步分派函数时非常有用。 需要在可用于容错的执行绪上运行的操作应在 SafeDispatcher 上执行。 但应非常小心地实施多线程,以避免执行绪之间出现死锁。 此类情况的一个示例是从非 UI 执行绪同步分派到主 WPF 调度程序。 考虑一下此示例:

Thread thread = new Thread(() =>
{
    Dispatcher.Invoke(ProcessData);
});
thread.SetApartmentState(ApartmentState.STA);
thread.Priority = ThreadPriority.Highest;
thread.IsBackground = true;
thread.Start();
thread.Join();

thread.Join() 方法使主线程被阻止,等待单线程单元 (STA) 执行绪的终止,但 STA 执行绪等待主执行绪完成 ProcessData 的执行。 这使您的应用程序处于死锁状态。

同样,考虑以下示例:

// Invoke on STA thread
SafeDispatcher.Invoke(() =>
{
    // Invoke back on main dispatcher
    SafeDispatcher.Invoke(() =>
    {
        ProcessData();
    });
}, false);

如果 WPF 调度程序或 STA 非 UI 执行绪上发生异常,SafeDispatcherUnhandledExceptionHandler Method将被调用,并将在发生异常的各个执行绪上引发。 您应谨慎确保不在此处理程序中放置上述组合,即,如果在非 UI 执行绪上发生异常,请勿向主 UI 调度程序同步分派。

protected override void SafeDispatcherUnhandledExceptionHandler(object sender, SafeDispatcherUnhandledExceptionEventArgs ex)
{
    Dispatcher.Invoke(LogException);                    // Incorrect
    SafeDispatcher.Invoke(LogException);            // Incorrect
    SafeDispatcher.BeginInvoke(LogException);  // Correct
    SafeDispatcher.InvokeAsync(LogException); // Correct
}

另请参阅

演练:创建 Unified Service Desk 的自定义托管控件
扩展统一服务台
TechNet:在 Unified Service Desk 中配置客户端诊断日志记录

Unified Service Desk 2.0

© 2017 Microsoft。 保留所有权利。 版权