XenTek is a small software house developing primarily a product called Plan Runner – a tool to aid Project Managers. There is just one employee, Jonathan Tooth who is an accomplished software developer, analyst & consultant with over 30 years of commercial experience ranging from Z80 machine code to C#, VB.Net.
The tool Planrunner is a complex piece of software that provides a level of control and visibility of ‘running’ projects previously unreachable by Project Managers. Vital to this is being able to accept the assets the Project Manager has created. To this end, the tool provides two mechanisms to input existing project plans. A sync tool that sits inside Microsoft Project and provides the easiest method to the user and import/export tool built into the website for Project Managers who are unwilling or unable to download and install programs from the internet. It is this import/export tool that requires a mechanism to read and write Microsoft project files.
Three solutions have been trialed over the course of developing PlanRunner, the first two (Microsoft Interop and MPXJ) have been discounted for reasons discussed below. The third solution trialed is that of Aspose.Tasks for .NET. Whilst cost is an issue for a ‘Start-up’ company; having a commercially developed tool does ensure that it is kept up to date and faults are dealt with and fixed in a timely manner.
PlanRunner users do not directly experience the tool directly; they are presented with an interface as shown below:
Project file upload page
This facilitates the user to get their project plan into Plan Runner. By simply selecting their project file using the ‘Choose file’ button and clicking on the ‘Upload’ button the website uploads and reads their project file, storing selected contents inside a SQL Server database.
The user can control whether the data is merged into the website or erase and overwrite it.
Project file download page
This facilitates the additional information stored by the website to be inserted into the project file the user originally uploaded. The simplest use is to click the ‘Prepare and download the file’ button which updates the originally uploaded project file and downloads it to the user.
Finding a solution
Originally the reading and writing of Microsoft project files were performed by Microsoft’s own Interop assemblies. This is fine for organizations that are hosting the website internally; however, Microsoft licensing prevents this from being used on public-facing websites.
A decision was made to use MPXJ, an open-source library that enables reading of Microsoft Project files. Unfortunately, this tool would sometimes read Microsoft Project 2013 files incorrectly; this has affected our first customer extensively and is not an acceptable solution.
Aspose.Tasks was trialed to see if it could read and write all of the project files currently available. A free trial reduced financial risk enabling a full POC to be performed. The following criteria were measured to determine the suitability of the product:
- Ease of integration
- Reliability of product
- Performance of product
- Quality of help
- Availability of support
Integration of Aspose.Tasks into the Plan Runner import/export mechanism took:
- Coding and unit testing Upload mechanism – 7 hours
- System Testing including bug fixing – 7 hours
- Coding and unit testing Download mechanism – 5 hours
- System Testing including bug fixing – 7 hours
- User Acceptance Testing (of both mechanisms) – 6.5 hours
This results in a total of 32.5 hours.
A rough comparison to the integration of MPXJ is possible. It is favorable to Aspose.Tasks as MPXJ coding was quicker at ~10 hours however testing and debugging took over 40 hours.
Implementation was made easier by having had two previous mechanisms. While integrating MPXJ the decision was made to isolate all project file reading and writing. A shared library that provided the business logic and connectivity to SQL Server was created along with a ‘Neutral Task’ class that hid tool specific code.
Sample implementation to read Task Name
Public ReadOnly Property Name() As String Get Select Case cGlobals.ActiveImportExportMechanism Case cGlobals.ImportExportMechanism.UseMPXJ Return _TaskMPXJ.Name Case cGlobals.ImportExportMechanism.UseMicrosoft_OfficeInterop Return _TaskMSP.Name Case cGlobals.ImportExportMechanism.UseAsposeTasks Return _TaskAspose.Name Case Else Return String.Empty End Select End Get End Property
However some task elements did present in significantly different ways, Predecessors and Duration fields required the most work to convert them into the visual format users expect.
Setting fields is also hidden from the Plan Runner specific code, the three interface methods:
Public Function SetFieldValue(ByVal task As Aspose.Tasks.Task, ByVal eFieldName As AsposeFieldNames, ByVal Value As String) As Boolean Public Function SetFieldValue(ByVal task As Aspose.Tasks.Task, ByVal eFieldName As AsposeFieldNames, ByVal Value As DateTime) As Boolean Public Function SetFieldValue(ByVal task As Aspose.Tasks.Task, ByVal eFieldName As AsposeFieldNames, ByVal Value As Integer) As Boolean
Allow Plan Runner to set any field in a task without needing to understand how it is done.
Note that Plan Runner does not use calendars, resource scheduling or project information so these have not been used or evaluated.
Initial provision of the import and export mechanisms was relatively straight forward; the example code supplied on the ExamplesDashboard and online help provided enough help.
A later addition to creating a new project file on download proved more problematical. The forums proved very useful here providing advice and example code the online help didn’t contain, for example using a blank project file. Many problems were experienced with Start and Finish times being set incorrectly, Appendix 1 provides some guidance on this.
For the most part, a successful outcome was achieved. Importing and exporting to MPP files is fully operational and tested successfully with over 20 different project files from various versions of Microsoft Project. Export to CSV has been rewritten to generate directly from the PlanRunner SQL Server database as going via Microsoft Project gave no real benefit. Export to Excel proved to be problematic, the files generated could not be opened with Excel 2010. It was deemed prudent at this time to disable the facility and revisit when the impact on delaying other development is reduced.
At present the facility is working, over the coming weeks, it is expected to have to tweak the mechanism to allow for edge cases and bugs being found. At some point, further refactoring of the code is required including the removal of Interop code and integration of MPXJ to cater to formats not present in Aspose.Tasks.
Overall, the implementation went reasonably smoothly. The online help could be improved with examples that showed the whole process. The product appears robust, performs well and once coded was stable. It coped well with Project files from 2007 to 2013, extensive testing was performed to ensure that the task fields were being read and written correctly. Once users have put a few of their own project files through this new mechanism we expect to be able to fully sign off this part of the system.
I certainly would recommend Aspose.Tasks to anyone requiring read and write access to Microsoft Project files.
Appendix 1 – Recommendations on creating a new task
When creating a new task you may experience problems with the Start and Finish times (and to a lesser extent dates) changing or being reset. The following rules should provide some help in this endeavour.
- Set Duration first
- Then set Work, RemainingWork, RegularWork and Work again to the duration
- Then set DurationFormat
- Then set the Start, ManualStart, Finish and ManualFinish
If you assign resources to the task use
Resource = mppFile.AddResource(resourceName)
Resource = New Aspose.Tasks.Resource mppFile.Resources.Add(resource)
As is discussed in one of the forum entries, Microsoft Project automatically assigns the times and dates to the resource assignment when you link them using Microsoft’s interface. When using Aspose.Tasks this needs to be done manually.
How to assign a resource
Dim ra As New ResourceAssignment(Me.neutralTask, resource) ra.Start = Me.neutralTask.Start ra.Finish = Me.neutralTask.Finish ra.Task.Work = ra.Task.Duration ra.RemainingWork = ra.Task.Work ra.RegularWork = ra.RemainingWork ra.Work = ra.RegularWork ra.Finish = Me.neutralTask.Finish mppFile.ResourceAssignments.Add(ra)
Similarly, we found that using AddTaskLink in preference to TaskLinks.Add seemed to create a more stable result.