【问题标题】:Raise event from one class and subscribe it in another从一个类中引发事件并在另一个类中订阅它
【发布时间】:2018-09-13 14:09:42
【问题描述】:

我正在开展一个项目,该项目基本上运行扫描并根据某些正则表达式确定目录中是否存在敏感信息。在这个项目中,我们有一个名为 DataScannerUI 的主项目 (WPF) 和一个名为 FileScanCore 的类库。 FileScanCore 被引用到 DataScannerUI。

简介:

这就是我的项目的运作方式。所有与扫描相关的逻辑都写在 FileScanCore 中,然后在调用它时将由此生成的结果集传递到 DataScannerUI。下面是扫描层次结构:-

  1. 我定义了一个需要扫描的来源。
  2. 扫描源目录(根目录到子目录:这只是查找需要扫描的路径的标识号。)
  3. 在数据库中清点扫描结果。
  4. 收集从 DB 清点的所有路径。
  5. 根据库存路径执行深度扫描。
  6. 将深度扫描结果保存在数据库中。

现在这里发生的事情是没有。 4 如果数据源大小小于 50k,则不需要太多时间即可完成。如果数据源大小为 600k,例如那么这大约需要 30 分钟。这里似乎 UI 被挂起,因为这里没有显示任何状态。我需要的是显示一些状态并让用户知道到目前为止已经加载了多少库存百分比。这很快就实现了。现在这就是我现在卡住的地方,所有状态消息逻辑都写在 DataScannerUI 中,问题是我无法在 FileScanCore 中引用 DataScannerUI(循环依赖问题)。

我需要将百分比从 FileScanCore 传递给 DataScannerUI。为了解决这个问题,我在谷歌上搜索并遇到了下面的线程。

我尝试使用上面的方法,但前 2 个线程对我没有用,虽然第 3 个线程有效,但它不能满足我的需要。 Shared 是我需要进行更改的 sn-p。

文件扫描核心

// This method is written in DeepScanner class that implements IScanner
private Queue<string> BuildInventory(FileSystem fs, IList<string> paths, bool includeChildren)
{
    var previousProgressStatus = 0L;
    var totalPathProcessed = 0L;

    var filesToInventory = new Stack<string>();
    var filesToDeepScan = new Queue<string>();

    //files To Inventory logic is written here. 
    {
        ...
        ...
        ...
    }

    // Here we find all directory and files 
    foreach (var c in fs.GetChildren(currentPath))
    {
        // This is where I determine the percentage completed
        var progressStatus = Math.Abs((totalPathProcessed++) * 100 / fs.TotalCount); 
        previousProgressStatus = ((progressStatus == previousProgressStatus) ? previousProgressStatus : progressStatus);

        filesToInventory.Push(c.FullPath);

        // I am looking for something that can trigger from here 
        // and send percentage change to DataScannerUI. 
    }
}

DataScannerUI

// This method is written in Scan class
foreach (var dataSource in _dataSources)
{
    try
    {
        _scanRunSemaphore.WaitAsync(_cancelSource.Token);
        ActiveDataSource = dataSource;
        _status = $"Performing deep scan - {ActiveDataSource.Name}";

        AllDeepScanners[ActiveDataSource.Name] = new DeepScanner(processors, extractor, _fileTypes, Job.Settings.SkipBinaries);
        ActiveScanner = AllDeepScanners[dataSource.Name];
        ActiveFileSystem = UsedFileSystems[ActiveDataSource.Name];

        // This is where I want to subscribe that event.
        // ActiveScanner is a property of type IScanner
        ActiveScanner.ScanAsync(ActiveFileSystem, _cancelSource.Token, _pauseSource.Token).Wait(); // This is where the inventory gets loaded.
        _status = $"Saving deep scan results - {ActiveDataSource.Name}";

        _logger.Debug($"Storing Deep scan results belonged to data source '{dataSource.Name}'");
        dataSource.SaveDeepScanResults(ActiveFileSystem, services);

        _logger.Debug($"Storing Job Last Scanned info belonged to data source '{dataSource.Name}'");
        SaveLastScanned(services, dataSource);        
    }
    // I'm not mentioning catch block 
}

我不确定我正在寻找的东西是否可能,但值得一试。

注意:如果这还不够,请发表评论,我将尝试使其更具解释性和可读性。

【问题讨论】:

  • “这里好像 UI 挂了,因为这里没有显示任何状态” 那是因为你在 .Wait()ing 一个异步调用,你不应该使用 @987654328 @如果你想要异步。添加事件并不能解决这个问题,UI 线程已经被锁定。
  • @RonBeyer:不知道为什么在这里使用.wait()。这段代码是由其他开发人员编写的。

标签: c# wpf delegates event-handling


【解决方案1】:

要将进度传递到您的 UI,您可以将事件添加到您的 IScanner 类。像这样的:

public delegate void ProgressUpdateHandler(object sender, float progress);

interface IScanner
{
  event ProgressUpdateHandler OnProgressUpdate;
  ...
}

在你的DeepScanner 类中实现这个事件:

public class DeepScanner : IScanner
{
  public event ProgressUpdateHandler OnProgressUpdate;
  ...
}

要引发此事件,您需要在 BuildInventory 函数中执行以下操作:

// Here we find all directory and files 
foreach (var c in fs.GetChildren(currentPath))
{
  var progressStatus = Math.Abs((totalPathProcessed++) * 100 / fs.TotalCount); // This is where I detemine the percentage completed
  previousProgressStatus = ((progressStatus == previousProgressStatus) ? previousProgressStatus : progressStatus);

  filesToInventory.Push(c.FullPath);

  // Send the progress to anyone who is subscribed to this event.
  OnProgressUpdate?.Invoke(this, progressStatus);
}

在您的 UI 代码中订阅和处理此功能:

foreach (var dataSource in _dataSources)
{
  try
  {
    _scanRunSemaphore.WaitAsync(_cancelSource.Token);
    ActiveDataSource = dataSource;
    _status = $"Performing deep scan - {ActiveDataSource.Name}";

    AllDeepScanners[ActiveDataSource.Name] = new DeepScanner(processors, extractor, _fileTypes, Job.Settings.SkipBinaries);
    ActiveScanner = AllDeepScanners[dataSource.Name];
    ActiveFileSystem = UsedFileSystems[ActiveDataSource.Name];

    // Subscribe to the progress updates.
    ActiveScanner.OnProgressUpdate += ActiveScanner_OnProgressUpdate;

    ActiveScanner.ScanAsync(ActiveFileSystem, _cancelSource.Token, _pauseSource.Token).Wait(); // This is where the inventory gets loaded.

    // Now unsubscribe to the progress updates.
    ActiveScanner.OnProgressUpdate -= ActiveScanner_OnProgressUpdate;

    _status = $"Saving deep scan results - {ActiveDataSource.Name}";

    _logger.Debug($"Storing Deep scan results belonged to data source '{dataSource.Name}'");
    dataSource.SaveDeepScanResults(ActiveFileSystem, services);

    _logger.Debug($"Storing Job Last Scanned info belonged to data source '{dataSource.Name}'");
    SaveLastScanned(services, dataSource);
  }
  // I'm not mentioning catch block 
}

...
// Handle the progress updates.
private void ActiveScanner_OnProgressUpdate(object sender, float percent)
{
  // Here you can update your UI with the progress.
  // Note, in order to avoid cross-thread exceptions we need to invoke the
  // UI updates since this function will be called on a non-UI thread.
  if (progressLabel.InvokeRequired)
  {
    progressLabel.Invoke(new Action(() => ActiveScanner_OnProgressUpdate(sender, percent)));
  }
  else
  {
    progressLabel.Text = "Progress: " + progress;
  }
}

请注意,您可能需要更改_scanRunSemaphore.WaitAsync。我不明白你在做什么。看来您可能正在以非典型方式使用它。

【讨论】:

  • ActiveScanner.OnProgressUpdate 将不起作用。 ActiveScanner 实际上是 IScanner 类型的属性。有一个名为 DeepScanner 的类实现了 IScanner,BuildInventory 是用 DeepScanner 编写的。让我也将其添加到我的帖子中。
  • 用户界面未冻结。由于没有显示更新,它似乎被冻结了。我正在尝试添加一条消息 n% 已从库存中加载。
  • 试试这个,我把事件声明移到了 IScanner
  • 谢谢。这符合我的要求,虽然这个块内的内容没有直接连接到 UI。这是一个实用程序类,我已经从我身边进行了更改,以将其与 UI 线程连接起来。无论如何,再次感谢您的帮助:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-24
  • 2021-04-03
  • 1970-01-01
  • 1970-01-01
  • 2022-07-22
相关资源
最近更新 更多