Dispatcher.BeginInvoke不被在一种情况下执行的 [英] Dispatcher.BeginInvoke not being executed in one case

查看:1407
本文介绍了Dispatcher.BeginInvoke不被在一种情况下执行的的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图修复现有应用程序,其中,在单一的情况下,不被执行调度的事件的错误。

I'm trying to fix a bug in an existing application, where in a single case, a dispatched event is not being executed.

在我们的应用程序,有几个服务实现一个共同的抽象类(AbstractService)。当一个服务节省的实体,并具有提高在UI线程通知我有问题发生。

In our application, there are several services implementing a common abstract class (AbstractService). The issue I've got occurs when a service saves an entity and has to raise a notification in the UI thread.

AbstractService的相关部分:

Relevant part of AbstractService:

public abstract class AbstractService<TEntity, TData, TInfo> : IDataProvider, IAbstractService
    where TEntity : AbstractEntity, new()
    where TData : AbstractData<TEntity>
    where TInfo : IEntityInfo {

    [...]

    protected virtual void NotifyEntityChanged(NotifyEventItem<TInfo>[] pNotifyEventItems) { }

    private void NotifyPersPlanChanged() {
        if (PersPlanChanged != null)
            PersPlanChanged(this, new NotifyEventArgs<IEntityInfo>(null, PersistanceState.Reset));

        Debug.WriteLine(string.Format("{0} {1} Call {2} to NotifyPersPlanChanged",
            DateTime.Now.ToLongTimeString(), GetType().Name, m_counterNotifyPersPlanChanged++));
    }

    private static int m_counterInternalFireNotification;
    private void InternalFireNotification(List<NotifyEventItem<TInfo>> pNotifyEventItem) {
        if (pNotifyEventItem == null || pNotifyEventItem.Count == 0)
            return;

        Debug.WriteLine(string.Format("{0} {1} Call {2} to InternalFireNotification",
            DateTime.Now.ToLongTimeString(), GetType().Name, m_counterInternalFireNotification++));

        // do not call the event handlers directly, because a transaction may be in progress and
        // we want the event handlers to see the end results of the modifications.
        Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new HandleEntityChanged(NotifyEntityChanged), pNotifyEventItem.ToArray());
        Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new HandlePersPlanChanged(NotifyPersPlanChanged));
    }
}

每当一个实体的属性已经通过该服务进行了修改,在 NotifyEntityChanged 被调用。

下面两种实现方式的 AbstractService 走出〜20。第一个,是一个我在用,第二个作品的问题。我将描述进一步下跌的问题是什么。

Here are two implementations of AbstractService out of the ~20. The first one, is the one I'm having issues with, the second works. I'll describe further down what the issue is.

public class ErgaenzungsfeldService : AbstractServiceBasic<ErgaenzungsfeldEntity, ErgaenzungsfeldData>, IErgaenzungsfeldService {

    [...]

    [EventPublication(Constants.TopicErgaenzungsfeldChanged, PublicationScope.Global)]
    public event EventHandler ErgaenzungsfeldChanged;

    private static int m_counterNotifyEntityChanged;
    protected override void NotifyEntityChanged(NotifyEventItem<IEntityInfoBasic>[] pNotifyEventItems) {
        Debug.WriteLine(string.Format("{0} {1} Call {2} to NotifyEntityChanged",
            DateTime.Now.ToLongTimeString(), GetType().Name, m_counterNotifyEntityChanged++));

        base.NotifyEntityChanged(pNotifyEventItems);
        if (ErgaenzungsfeldChanged != null) {
            ErgaenzungsfeldChanged(this, new NotifyEventArgs<IEntityInfoBasic>(pNotifyEventItems));
        }
    }
}

public class DienstleistenderService : AbstractAdaLanguageDependentServiceVersion<DienstleistenderEntity, DienstleistenderData>, IDienstleistenderService {

    [...]

    [EventPublication(Constants.TopicDienstleistenderChanged, PublicationScope.Global)]
    public event EventHandler DienstleistenderChanged;

    private static int m_counterNotifyEntityChanged;
    protected override void NotifyEntityChanged(NotifyEventItem<IEntityInfoVersion>[] pNotifyEventItem) {
        Debug.WriteLine(string.Format("{0} {1} Call {2} to NotifyEntityChanged",
            DateTime.Now.ToLongTimeString(), GetType().Name, m_counterNotifyEntityChanged++));
        if (DienstleistenderChanged != null) {
            DienstleistenderChanged(this, new NotifyEventArgs<IEntityInfoVersion>(pNotifyEventItem));
        }
    } 
 }

所有的服务都实例是这样的:

All services are instantiated like this:

WorkItem.RootWorkItem.Services.AddNew<ErgaenzungsfeldService, IErgaenzungsfeldService>();
WorkItem.RootWorkItem.Services.AddNew<DienstleistenderService, IDienstleistenderService>();

和访问使用这两种方式:

And accessed using either of these two ways:

WorkItem.Services.Get<IDienstleistenderService>()

[ServiceDependency]
public IErgaenzungsfeldService ErgaenzungsfeldService { get; set; }

问题: ErgaenzungsfeldService.NotifyEntityChanged并不总是被调用。这只能使用应用程序正常时,导入数据,而不是在发生。通过正常的我的意思是一个ErgaenzungsfeldEntity可以在应用程序进行修改并保存。相同的机制被使用和工作方式。

Issue: ErgaenzungsfeldService.NotifyEntityChanged is not always being called. This only happens when importing data and NOT when using the application 'normally'. By normal I mean an ErgaenzungsfeldEntity can be modified in the application and saved. The same mechanism is being used and works.

有些意见:

  • 在导入过程是单线程的。它不会在UI线程中运行。
  • DienstleistenderService.NotifyEntityChanged被调用正确的,当ErgaenzungsfeldService.NotifyEntityChanged不是
  • 更换 Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle,新HandleEntityChanged(NotifyEntityChanged),pNotifyEventItem.ToArray()) NotifyEntityChanged(pNotifyEventItem。的ToArray()) System.Windows.Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle,新HandlePersPlanChanged(NotifyPersPlanChanged)); 通话正确ErgaenzungsfeldService.NotifyEntityChanged。作为UI会从另一个线程通知,该应用程序显然引发异常以后。
  • 如果一个ErgaenzungsfeldEntity通过UI被保存,ErgaenzungsfeldService.NotifyEntityChanged被称为正常。
  • The import process is single-threaded. It does not run in the UI thread.
  • DienstleistenderService.NotifyEntityChanged is being called correctly, when ErgaenzungsfeldService.NotifyEntityChanged is not
  • Replacing Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new HandleEntityChanged(NotifyEntityChanged), pNotifyEventItem.ToArray()) by NotifyEntityChanged(pNotifyEventItem.ToArray()) or by System.Windows.Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new HandlePersPlanChanged(NotifyPersPlanChanged)); calls ErgaenzungsfeldService.NotifyEntityChanged correctly. As the UI gets notified from another thread, the application obviously throws exceptions later on.
  • If an ErgaenzungsfeldEntity is being saved through the UI, ErgaenzungsfeldService.NotifyEntityChanged is being called correctly.

下面是已经添加了调试目的,这帮助我分析问题的不同方法的消息的输出。

Here's the output of the messages which have been added for debugging purposes to the different methods which helped me analyze the issue.

09:39:44 ErgaenzungsfeldService Call 0 to InternalFireNotification
09:39:44 ErgaenzungsfeldService Call 1 to InternalFireNotification
09:39:44 ErgaenzungsfeldService Call 2 to InternalFireNotification
09:39:44 ErgaenzungsfeldService Call 3 to InternalFireNotification
09:40:08 DienstleistenderService Call 0 to InternalFireNotification
09:40:08 DienstleistenderService Call 1 to InternalFireNotification
09:40:09 DienstleistenderService Call 2 to InternalFireNotification
[...]
09:40:14 DienstleistenderService Call 652 to InternalFireNotification
09:40:14 DienstleistenderService Call 653 to InternalFireNotification
09:40:14 DienstleistenderService Call 654 to InternalFireNotification
09:40:14 DienstleistenderService Call 655 to InternalFireNotification
09:40:14 AdaService Call 0 to InternalFireNotification
09:40:14 DienstleistenderService Call 656 to InternalFireNotification
09:40:14 DienstleistungService Call 0 to InternalFireNotification
09:40:14 AufbauorganisationService Call 0 to InternalFireNotification
09:40:14 AufbauorganisationService Call 1 to InternalFireNotification
09:40:14 AdaService Call 0 to NotifyPersPlanChanged
09:40:14 DienstleistenderService Call 0 to NotifyEntityChanged
09:40:16 DienstleistenderService Call 0 to NotifyPersPlanChanged
09:40:16 DienstleistungService Call 0 to NotifyPersPlanChanged
09:40:16 AufbauorganisationService Call 0 to NotifyPersPlanChanged
09:40:16 AufbauorganisationService Call 1 to NotifyPersPlanChanged

<打击> 问:什么会导致无法描述执行的通知

Question: What could cause the notification not to be executed as described?

我已经通过存储获得了更深入的了解了的DispatcherOperation

I've gained some more insight by storing the DispatcherOperation:

private List<DispatcherOperation> m_dispatcherOperationresults = new List<DispatcherOperation>();
private void InternalFireNotification(List<NotifyEventItem<TInfo>> pNotifyEventItem) {
   if (pNotifyEventItem == null || pNotifyEventItem.Count == 0)
      return;

   m_log.DebugFormat("InternalFireNotification called for {0}. TInfo type is {1}", GetType(), typeof(TInfo));

   // do not call the event handlers directly, because a transaction may be in progress and
   // we want the event handlers to see the end results of the modifications.
   DispatcherOperation result = Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Send, new HandleEntityChanged(NotifyEntityChanged), pNotifyEventItem.ToArray());
   m_dispatcherOperationresults.Add(result);
   Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new HandlePersPlanChanged(NotifyPersPlanChanged));
}

这清楚地表明,委托执行滞留在执行队列中。不管的DispatcherPriority设置为ApplicationIdle(因为它始终是)或发送(这是最高优先级),他们保持卡住ErgaenzungsfeldService。

This made clear that the delegate executions are stuck in the execution queue. No matter if DispatcherPriority is set to ApplicationIdle (as it always was) or Send (which is the highest priority), they stay stuck for ErgaenzungsfeldService.

在此PRINTSCREEN,项目0-3已经在导入过程中引发了一个非UI线程,并保持未决,而第4项已被触发的GUI线程(几分钟后),并执行

In this printscreen, items 0-3 have been triggered from a non-UI thread during import and stay pending, while item 4 has been triggered from the GUI thread (minutes later) and executed

问题:有什么能为这些代表仍然悬而未决某些类别的原因,而他们对其他类的工作。

Question: What could be the reason for those delegates to remain pending for certain classes, while they work for other classes?

推荐答案

只是为了让我的意见正式。这可能是由从另一个线程调用Dispatcher.CurrentDispatcher引起的。这可能会导致该应用程序创建一个新的调度器,它没有任何访问UI

Just to make my comments "official". This might be caused by calling the Dispatcher.CurrentDispatcher from another thread. That can cause the application to create a new dispatcher that doesn't have any access to the UI.

假设你从UI创建ErgaenzungsfeldService类,你可以创建一个只读调度场,有Dispatcher.CurrentDispatcher的价值。这会给你的UI调度。

Assuming that you're creating the ErgaenzungsfeldService class from the UI, you can create a readonly Dispatcher field that has the value of Dispatcher.CurrentDispatcher. This will give you the UI dispatcher.

这篇关于Dispatcher.BeginInvoke不被在一种情况下执行的的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆