Cascade Skyline - with Microsoft Logo and Project Support header - author Brian Smith

Project Server: Updating and Deleting Custom Fields using the PSI

Project Server: Updating and Deleting Custom Fields using the PSI

  • Comments 10

Following on from my posting on adding custom fields http://blogs.msdn.com/b/brismith/archive/2010/08/25/project-server-adding-custom-fields-to-projects-and-tasks-using-the-psi.aspx I had a few questions on changing fields – or deleting them.  So here are a couple of short samples that follow the same code from the previous sample and show how to update a task level custom field and also to delete a custom field.  In my sample I have hard-coded the GUID for my project and for my custom field.  And my project just has one task, so in the real world you would also need to ensure you were acting on the right task – otherwise my code would update every occurrence of that custom field for every task – or delete every occurrence…  Also when I am creating these samples I usually start from the LoginDemo sample and just add buttons to do the stuff I am playing with.  So the previous sample created a project – these two just update the CF or delete it.  Obviously I am updating a Text custom field at the task level – if you are updating another custom field type you would need to change the TEXT_VALUE as appropriate.  Also be a good idea to see if the row exists before updating or deleting it – but I’ll leave that to you.  This was created with 2007 but the logic would be the same in 2010.

        private void btnUpdateCF_Click(object sender, EventArgs e)
        {
            Guid taskTextGuid = new Guid("30665299-bc21-4c51-b954-220d407ba47e");
            Guid projectId = new Guid("4c1cef33-4810-4159-b2e1-db15ea19a28b");
            WebSvcProject.ProjectDataSet projectDs = 
                project.ReadProject(projectId, WebSvcProject.DataStoreEnum.WorkingStore);

            foreach (WebSvcProject.ProjectDataSet.TaskCustomFieldsRow cfRow in 
                projectDs.TaskCustomFields)
            {
                if (cfRow.MD_PROP_UID == taskTextGuid)
                {
                    cfRow.TEXT_VALUE = "Updated Value";
                }
            }
            Guid sessionUid = Guid.NewGuid();


            project.CheckOutProject(projectId, sessionUid, "Updating CF");
            Guid jobUid = Guid.NewGuid();
            project.QueueUpdateProject(jobUid, sessionUid, projectDs, false);
            jobUid = Guid.NewGuid();
            project.QueueCheckInProject(jobUid, projectId, false, sessionUid, "Updating CF");

        }

private void btnDeleteCF_Click(object sender, EventArgs e)
        {
            Guid taskTextGuid = new Guid("30665299-bc21-4c51-b954-220d407ba47e");
            Guid projectId = new Guid("4c1cef33-4810-4159-b2e1-db15ea19a28b");
            WebSvcProject.ProjectDataSet projectDs = 
                project.ReadProject(projectId, WebSvcProject.DataStoreEnum.WorkingStore);
            
            foreach (WebSvcProject.ProjectDataSet.TaskCustomFieldsRow cfRow in 
                projectDs.TaskCustomFields)
            {
                if (cfRow.MD_PROP_UID == taskTextGuid)
                {
                    cfRow.Delete();
                }
            }
            Guid sessionUid = Guid.NewGuid();

            project.CheckOutProject(projectId, sessionUid, "Updating CF");
            Guid jobUid = Guid.NewGuid();
            project.QueueUpdateProject(jobUid, sessionUid, projectDs, false);
            jobUid = Guid.NewGuid();
            project.QueueCheckInProject(jobUid, projectId, false, sessionUid, "Updating CF");
        }

Technorati Tags:
Leave a Comment
  • Please add 5 and 5 and type the answer here:
  • Post
  • Hi Brian,

    I have a similar code to capture the ResourcePlanOnPublishing event. I want to handle it in order to calculate the total cost of the project's ResourcePlan.

    The QueueUpdateProject blocks the execution so the ResourcePlan Publish remains forever in the queue.

    May I use the existing sessionID that fires the event?

    I tried also with different events and any time that i use the QueueUpdateProject method, something is blocked within the queue.

    I hope you can help me.

    Thanks!!!

    PD: here's my update method code:

    private bool UpdateProject(Guid projectGuid, string projectName, Guid sessionGuid, ProjectDataSet dsProject)

           {

               Guid jobId;

               ProjectDataSet dsModified = (ProjectDataSet)dsProject;

               try

               {

                   //Save the modified rows      

                   dsModified = ProjectDataSet)dsProject.GetChanges(DataRowState.Modified);

                   if (dsModified != null)

                   {

                       jobId = Guid.NewGuid();

                       //Delete the existing file

                       dsModified.ProjectCustomFields[0].Delete();

                       _projectService.QueueUpdateProject(jobId, sessionGuid, dsModified, false);

                       WaitForQueue(jobId);

                   }

                   //Save the added rows                    

                   dsModified = (ProjectDataSet)dsProject.GetChanges(DataRowState.Added);

                   if (dsModified != null)

                   {

                       jobId = Guid.NewGuid();

                       dsModified.ProjectCustomFields[0].Delete();

                       _projectService.QueueUpdateProject(jobId, sessionGuid, dsModified, false);

                       WaitForQueue(jobId);

                   }

                   EventLog.WriteEntry("ProjectCostBenefit WCF: UpdateProject", "Project " + projectName + " has been updated.");

                   return true;

               }

               catch (Exception ex)

               {

                   EventLog.WriteEntry("ProjectCostBenefit WCF: UpdateProject", "Update failed for project " + projectName + ". " + ex.Message);

                   return false;

               }

           }

  • Hi meteko,

    You should use the same session Guid for the checkout, update and checkin.  You do not need to use the row state code at all - just change you dataset and use the update method (passign in the full dataset) and the PSI will take care of the changes.  The update method is expecting the full dataset - not just the changes.

    Best regards,

    Brian.

  • Hi again!

    actually I'm using the checkout sessionGuid, but the code stops in waitForQueue(jobId); (after the queueUpdate call) and the Resource Plan Publish stuck always at 84% blocking the queue and the Project Update from PSI which is the next job in the queue.

    It seems like the Resource Plan Publish is waiting for the Project Update and vice versa creating a kind of deadlock.

    I change also the code to pass the full dataset to the queueUpdate method.

    Regards

  • Hello,

    Obviously it was a deadlock.

    OnPublished method was calling QueueUpdate and WaitForQueue.

    Commenting the WaitForQueue lines...it seems to work fine.

    Thanks.

  • this code works properly, but if you want to update project custom fields from a PSI eventhandler... which one would you choose??

    I mean, if you're overriding onCheckIn method, you can't call to QueueCheckInProject.

    Regards

  • hello

    how can you fire this code from an event handler??

    you can't run it from checkIn because you need check out and check in the project inside.

    which one would be appropiate?

    Regards

  • Hi Suso,

    You could use the saved or published event - but you may need to check if the project is already checked out before doing any checking out in your code.

    Best regards,

    Brian.

  • Hi Brian,

    I am getting error on  

    projectWS.QueueUpdateProject(jobUid, sessionUid,projectDs, false);

    ProjectServerError(s) LastError=CustomFieldRequiredValueNotProvided Instructions: Pass this into PSClientError constructor to access all error information

    Tx in advance....

  • I am Editing the Custom Fields of my SharePoint Project Server and I acme to this blog, I implemented the given code it runs perfectly fine but it is not reflecting the changes in Project Server. Following is the Code, Kindly help!!!

    ProjectSoapClient projectSvc = new ProjectSoapClient();

               CustomFieldsSoapClient customfieldSvc = new CustomFieldsSoapClient();

               CustomFieldDataSet fieldDefs = customfieldSvc.ReadCustomFields(string.Empty, false);

               Guid projectId = new Guid(projGuid);

               ProjectDataSet projectDs = projectSvc.ReadProject(projectId, ListProjects.Project.DataStoreEnum.WorkingStore);

               foreach (ProjectDataSet.ProjectCustomFieldsRow cfRow in projectDs.ProjectCustomFields.Rows)

               {

                   CustomFieldDataSet.CustomFieldsRow fieldDefinition = fieldDefs.CustomFields.Single(

                           cfd => cfd.MD_PROP_UID == cfRow.MD_PROP_UID);

                   //if (cfRow.FIELD_TYPE_ENUM == 21 || cfRow.FIELD_TYPE_ENUM == 15) //if it is a choice field

                   //{

                       if (fieldDefinition.MD_PROP_NAME == "ProductCategory")

                       {

                           cfRow.TEXT_VALUE = newValue;

                       }

                   //}

               }

               Guid sessionUid = Guid.NewGuid();

               Guid jobUid = Guid.NewGuid();

               if (!IsProjectCheckedOut(projectId))

               {

                   projectSvc.CheckOutProject(projectId, sessionUid, "Updating CF");

                   jobUid = Guid.NewGuid();

                   projectSvc.QueueUpdateProject(jobUid, sessionUid, projectDs, false);

               }

               jobUid = Guid.NewGuid();

               projectSvc.QueueCheckInProject(jobUid, projectId, true, sessionUid, "Updating CF");

               projectSvc.QueuePublish(jobUid, projectId, true, SPContext.Current.Site.Url);

  • Hi Muhammad, I can only suggest checking in debug that the changes you are expecting are made to the dataset and then check logs etc, and even the DB to see what is actually happening.  Nothing in your code jumps out as obviously wrong.

    Best regards,

    Brian

Page 1 of 1 (10 items)