【问题标题】:How to Improve Performance when Loading Data into Microsoft Project with VSTO使用 VSTO 将数据加载到 Microsoft Project 时如何提高性能
【发布时间】:2017-10-05 09:27:13
【问题描述】:

背景

我们有一个现有的应用程序,它可以将数据加载到 Microsoft Project 中,以便 MS Project 对其进行操作。

原始应用程序是一个老式的 COM Project 插件,由 VB6 编写,针对 MS Project 2003 / 2007,现在我们计划将它们迁移到针对 MS Project 2013 / 2016 的 VSTO 插件。

问题

对于他们的 COM 插件解决方案,我们遇到了性能问题:

测试项目有4414个活动(av_activity表)和8330个 关系(av_reln 表)。

在加载操作期间报告了性能问题:

上述项目的 MSP 2003 模板的加载时间约为:30-35 分钟。

MSP 2007 和 2010 模板的加载时间 > 3 小时

对于 VSTO 解决方案,它会变得更好,但我们仍然希望可以通过某种方式提高加载性能。

到目前为止我们所做的尝试

我们尝试了几种技巧,但都没有取得太大的效果......

  • 添加数据时禁用自动计算

    _application.Calculation = PjCalculation.pjManual;

  • 在添加数据期间禁用屏幕更新

    _application.ScreenUpdating = false;

  • 禁用更改突出显示

    _application.EnableChangeHighlighting = false;

  • 禁用状态栏

    _application.DisplayStatusBar = false;

  • 将撤消值设置为 1

  • 将默认视图设置为“任务表视图”(而不是“甘特视图”,这会在加载期间产生更多开销)。

寻求帮助

我正在寻求任何解决方案的帮助,以提高性能,同时使用 VSTO 插件和 C# 将数据加载到 MS Project 中。

提前致谢!

【问题讨论】:

  • OP 在下面的评论中指出,当前使用的方法是TaskDependencies.Add。这种方法非常缓慢。唯一的解决方法是提前构建 Predecessor 列表。详情见我的回答。

标签: c# vsto ms-project


【解决方案1】:

是的,使用TaskDependencies.Add 方法创建关系非常慢。

这是不幸的,因为这是合乎逻辑的使用方法。 唯一的解决方法是提前建立 Predecessor 列表。获得该列表后,您可以通过两种方式创建关系:

  • 添加到现有计划
  • 通过导入包含前置任务列表的任务数据来创建新计划

由于您的数据已经是表格形式,因此使用项目导入向导从 csv 或 Excel 文件导入数据非常有效。 要使用项目导入向导,请从 MS Project 中选择要打开的 csv 或 Excel 文件(文件->打开)。该向导将引导您完成创建导入映射的步骤,您可以将其保存以供以后自动化使用。 在您的导入数据中包含 Predecessors 列,向导将在几秒钟内为您创建关系。

任务Predecessors field 包含一个以逗号分隔的前置任务列表。每个前任的格式如下:

  • 任务 ID
  • 关系类型(FS、FF、SS、SF)
  • 滞后 (+/- #d)

具有 FS 类型且没有滞后的前身仅显示任务 ID。以下是一些前置字段值的示例:

  • 14,126,127
  • 73,92SS
  • 144FS+3d,145

如何从关系表中创建前置字段值

假设一个包含前任和后继任务 ID 的关系表:

  1. 使用整数键(后继任务 ID)和字符串值(前任)创建字典。
  2. 循环遍历关系表并将后继任务ID及其前任添加到字典中;如果它已经存在,则更新该值以附加逗号和前任。
  3. 遍历字典并 A) 更新 csv 文件中的前任列 B) 在计划中的后继任务上设置前任字段。

【讨论】:

  • 感谢 Rachel,解决方法是非常有价值的信息。我们可以使用内存中的 Project 对象 API 而不是使用 UI 中的向导来执行此操作吗?就像构造前置字符串并使用Task.Predecessors 将它们分配给每个任务一样?
  • @ZheYang 是的,我更新了我的答案以展示如何构建前置字符串。我还没有为大型计划更新Task.Predecessors task-by-task,但它应该很快。如果没有,您可以将此信息放在 csv 文件中,并通过项目导入向导将其合并到现有计划中。自动化向导取决于在目标计算机上有一个预定义的映射,该映射可以在 mpp 文件中提供并通过管理器复制...有关更多详细信息,我建议提出一个新问题。
【解决方案2】:

如果您要使用您的数据创建一个新项目,您可以使用MPXJ 生成一个 Microsoft 项目 XML 文件(MSPDI 文件),然后您可以直接在 Project 中打开该文件。

【讨论】:

    【解决方案3】:

    如果我理解了这个问题,您说将 4000 个任务放入 MS Project 大约需要 30 分钟。我不确定您所说的 8000 条关系是什么意思;你的意思是前辈/继任者?也许你可以澄清一下?因此,您拥有的是一个由 3 个组件组成的系统:数据源、传输机制和 MS Project 本身;对吗?

    这种表现让我感到惊讶。我编写了一些 VBA 代码(见帖子底部)来测试添加 4000 个具有各种大纲级别 + 前辈的任务的性能,并且在我的系统(Proj 2016、Intel i7、Win 10)上添加任务最多需要 100 秒.这告诉我 Project 中没有瓶颈。我怀疑瓶颈在于您的数据源或传输机制。

    要确认这一点,也许您可​​以尝试将所有任务(您称它们为活动)数据添加到一个任务的 notes 属性中,然后查看您的表现。 A 如果它很快,那么也许尝试添加 4000 个任务但没有属性(除了名称),然后逐步添加更多属性,直到找到哪些属性添加了减速。在我下面的测试中,使用 task.predecessors 属性增加了 6 倍的性能降低,其余的影响可以忽略不计;您可能会遇到另一个降低性能的属性。 B 但是如果将你的任务数据添加到一个任务的笔记中仍然很慢,那么你的数据源或传输机制有问题。也许您一次发送一个任务(即活动),而是可以构建一个批处理?无论如何,找出问题并消除它。

    祝你好运!

    Sub add4000tasks()
    
    On Error Resume Next
    
    Dim myTask As task
    Dim myProject As Project
    resPool = Split("Allice,Bob,Claire,Dave", ",")
    
    For testRun = 0 To &HF '00001111
    
        testPreds = testRun And &H1 '00000001
        testOutlines = testRun And &H2 '00000010
        testDurations = testRun And &H4 '00000100
        testAssignments = testRun And &H8 '00001000
    
        If testPreds Then Debug.Print "Testing Predcessors"
        If testOutlines Then Debug.Print "Testing Outlines"
        If testDurations Then Debug.Print "Testing Durations"
        If testAssignments Then Debug.Print "Testing Resource Assignments"
    
    
        Application.Projects.Add
        Set myProject = ActiveProject
        Application.Calculation = pjManual
    
        starttime = Now
    
        Set myTask = myProject.Tasks.Add("Task 0")
        For a = 1 To 4000
    
            Set myTask = myProject.Tasks.Add("Task " & a)
    
            If testPreds Then
                myTask.Predecessors = Rnd * 10000000 Mod a + 1 'may fail if predecessor is also a parent
            End If
    
            If testOutlines Then
                If Rnd * 10000000 Mod 10 = 0 And myTask.OutlineLevel < 10 Then
                    myTask.OutlineIndent
                ElseIf Rnd * 10000000 Mod 10 = 1 And myTask.OutlineLevel > 1 Then
                    myTask.OutlineOutdent
                End If
            End If
    
            If testDurations Then
                myTask.Duration = Rnd * 10000000 Mod 50 & "d"
            End If
    
            If testAssignments Then
                myTask.ResourceNames = resPool(Rnd * 10000000 Mod UBound(resPool) + 1)
            End If
    
    
        Next
    
        Application.Calculation = pjAutomatic
    
        Debug.Print (Now - starttime) * 86400 & vbCrLf
    
        Application.FileCloseEx (pjDoNotSave)
    
    Next
    

    结束子

    【讨论】:

    • 谢谢,杰瑞德。是的,应用程序作为适配器将另一个系统中的数据映射到 Project 的概念中,因此数据被预取到内存中。 “关系”涉及诸如依赖项和分配之类的事情...一开始,添加速度是可以忍受的,但是一旦达到一定数量的任务和相应的依赖项数量,那么当Task.TaskDependencies.Add...似乎每个之后改变任务让 MS 项目想了很多......
    猜你喜欢
    • 1970-01-01
    • 2018-08-25
    • 1970-01-01
    • 1970-01-01
    • 2012-02-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-25
    相关资源
    最近更新 更多