2

我有一个使用 MVVM 设计模式和异步数据访问方法的大型 WPF 应用程序。它使用带有回调处理程序和接口的旧式异步代码IAsyncResult......这是一个典型的例子:

function.BeginInvoke(callBackMethod, asyncState);

然后,在视图模型中,我有以下回调处理程序:

private void GotData(GetDataOperationResult<Genres> result)
{
    UiThreadManager.RunOnUiThread((Action)delegate
    {
        if (result.IsSuccess) DoSomething(result.ReturnValue);
        else FeedbackManager.Add(result);
    });
}

RunOnUiThread方法基本如下:

public object RunOnUiThread(Delegate method)
{
    return Dispatcher.Invoke(DispatcherPriority.Normal, method);
}

此问题仅影响一种视图模型,即允许用户编辑Release对象的视图模型。在相关视图上,填充 es 的某些集合ComboBox在首次加载时会从数据库中请求。让我们简化这个说法,即只有一个名为 的集合Genres。数据到达视图模型后,处理如下:

private void GotGenres(GetDataOperationResult<Genres> result)
{
    UiThreadManager.RunOnUiThread((Action)delegate
    {
        if (result.IsSuccess) Genres.AddEmptyItemBefore(result.ReturnValue);
        else FeedbackManager.Add(result);
    });
}

当集合存在并且Release在 UI 中选择了一个对象时,我有以下代码Release.Genre从集合中选择当前值:

if (!Release.Genre.IsEmpty && Genres.ContainsItemWithId(Release.Genre.Id)) 
    Release.Genre = Genres.GetItemWithId(Release.Genre);

在这一点上,我应该注意到这一切都很好,这是唯一Release.Genre从视图模型中引用属性的行。

我的特殊问题是,有时该Release.Genre属性设置为null,我无法弄清楚如何或从哪里开始。>> 编辑 >> 当我在属性设置器上放置一个断点时,<< 编辑 <<Call Stack没有提供关于设置null值的真正线索,因为只有[Native to Managed Transition]一行。从窗口中选择Show External Code选项后Call Stack,我可以看到基本的异步代码调用:

调用堆栈

现在,我可以确认在尝试解决此问题时发现的以下事实:

  1. Release.Genre引用该属性的一行将其设置为null.
  2. 对 Genres.AddEmptyItemBefore(result.ReturnValue) 的调用将其设置为null... 这只是Genres在添加 'empty' 后将结果集合添加到集合中Genre
  3. Release.Genre属性有时被设置为null在调用 Genres.AddEmptyItemBefore(result.ReturnValue) 时或之后,但不是因为它......在几次单步执行时,执行已经跳到(以不相关的方式)到中断我在输入参数Release.Genre所在的属性设置器上设置的点,但这并不是每次都发生。valuenull
  4. 它通常发生在从相关视图模型到Release视图模型时,但不是每次都发生。
  5. 相关视图模型没有对该Release.Genre属性的引用。

需要明确的是,我并不是要求任何人从我提供的稀疏信息中调试我的问题。我也没有就进行异步数据调用征求意见。相反,我真的在努力寻找我还没有想到的新方法。我知道某处的某些代码(几乎可以肯定是我的代码)将属性设置为null...我的问题是如何检测此代码的位置?它似乎不在Release视图模型中。在没有更多线索的情况下,如何继续调试此问题?

4

1 回答 1

0

我通常使用平面文件、XML 或数据库日志来进行调试。我创建了这些 Log 类用于记录目的,以便我可以从我开发的每个应用程序中调用它。

对于数据库日志记录,您可以简单地执行以下操作:

void WriteLog(string log){
  // Your database insert here
}

也许您需要日期时间和其他支持信息,但这取决于开发人员。对于简单的平面文件日志记录是:

void WriteLog(string log){
  using(TextWriter tx = new StreamWriter("./Log/" + DateTime.Now.ToString() + ".txt", false)){
    tx.WriteLine(log);
  }
}

您可以通过两种方式在应用程序中使用日志记录:

1:方法调用

WriteLog((Release.Genre == null).ToString());
if (!Release.Genre.IsEmpty && Genres.ContainsItemWithId(Release.Genre.Id)) 
    Release.Genre = Genres.GetItemWithId(Release.Genre);

2:将其添加到您的 Release.Genre 设置(或获取)属性中

public class Release{
  private Genre _genre=null;
  public Genre Genre{
    get{
      WriteLog((_genre == null).ToString());
      return _genre;
    }
    set{
      WriteLog((_genre == null).ToString());
      _genre = value;      
    }
  }
}

有了这个,你可以尝试获取调用顺序,Release.Genre是否在其他地方设置过之前,调用中等。

请注意,我只是给出了构建日志的一般图像。请期待错误。但是,开发记录活动以满足要求是开发人员的责任。

于 2013-03-27T17:33:50.623 回答