Project Server. CSOM + Custom Fields

Wednesday, November 1, 2017

Project Online (as a part of Office 365) and Project Server 2013/2016 (on-premise) contains core objects such as project, resource, task. To meet business needs we need to extend available object attributes. To achieve this goal in Project Online (Server) there are Enterprise Custom Fields (ECF).

In this post I'll show how to get and set Enterprise Custom Field values using the client-side object model (CSOM).


First of all we have to add a new Enterprise Custom Field to resource object in Project Online (Server). There is a step-by-step official guide from Microsoft how to achieve this: Add or edit an Enterprise Custom Field. Just follow the instruction if you have no any custom field in your environemnt.

In my demo env I've created custom field assigned to Resource object:

It's a simple text field which we'll try to read and update. To use this field we need to know its guid - just open field and copy it out from url:

Client-Side object model (CSOM)

To interoperate with Project Online we'll use Client-Side object model. To read more check this article out: Developing a Project Online application using the client-side object model.

In my demo we'll read and modify resource' custom field. So, we are to understand the object which Enterprise Resource is presented in CSOM. Following diagram can help you to figure out Enterprise Resource object and its related properties:

As you can see EnterpriseResource object has CustomFields property which is a collection of Enterprise Custom Fields.

It's time to creativity!


Before starting we need to determine project instance which we will working with. It's neccesary to know two things: project server url and user credentials. In my case instance' url is the following:

I've created a simple console application in Visual Studio 2017. To use CSOM libraries we are just to install Microsoft.SharePointOnline.CSOM NuGet package. To achieve this just execute the following command in Package Management Console:

Install-Package Microsoft.SharePointOnline.CSOM

In my case it took about 20 seconds:

That the application actually will do:

  • Connect to Project Online instance
  • Retrieve a resource by its Id
  • Set initial value for Enterprise Custom Field
  • Save changes
  • Retrieve the same resource and read new value of the field

All settings ap the app are presented in the configuration file:

  <add key="pwa:SiteUrl" value=""/>
  <add key="pwa:Login" value=""/>
  <add key="pwa:Password" value="*****"/>
  <add key="pwa:FieldId" value="eabfccb7-3abf-e711-8125-00155df5be05"/>
  <add key="pwa:ResourceId" value="bfeda518-bf8b-e711-80d6-00155de88c08"/>

Complete solution is just about fivty rows of code:

// Environment variables
var siteUrl = ConfigurationManager.AppSettings["pwa:SiteUrl"];
var login = ConfigurationManager.AppSettings["pwa:Login"];
var password = ConfigurationManager.AppSettings["pwa:Password"];
var fieldId = new Guid(ConfigurationManager.AppSettings["pwa:FieldId"]);
var resourceId = new Guid(ConfigurationManager.AppSettings["pwa:ResourceId"]);

// Store password in secure string
var securePassword = new SecureString();
foreach (var c in password) securePassword.AppendChar(c);

// Project instance credentials
var creds = new SharePointOnlineCredentials(login, securePassword);

// Initiation of the client context
using (var ctx = new ProjectContext(siteUrl))
    ctx.Credentials = creds;

    // Retrieve Enterprise Custom Field
    var field = ctx.CustomFields.GetByGuid(fieldId);

    // Load InernalName property, we will use it to get the value
    ctx.Load(field, x=>x.InternalName);

    // Execture prepared query on server side

    var fieldInternalName = field.InternalName;

    // Retrieve recource by its Id
    var resource = ctx.EnterpriseResources.GetByGuid(resourceId);

    // Load custom field value
    ctx.Load(resource, x => x[fieldInternalName]);

    // Update ECF value
    resource[fieldInternalName] = "Vitaly Zhukov";
    // Get ECF value from server

To read ot modify field we have to know its internal name. Usually it equals "Custom_" + field guid. To load it from server just reference it in the labda expression in Load method.

To send changes to the server use Update method of EnterpriseResources collection.

Source code

All the source code of demo project is located on my github: