Project Server. CSOM + Custom Fields
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).
Preparing
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 custom field in your environment.
In my demo env I've created custom field assigned to Resource object:
It's a simple text field that 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 the 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 the resource' custom field. So, we are to understand the object in 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 for creativity!
Project
Before starting we need to determine the project instance which we will be working with. It's necessary 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 the 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 the new value of the field
All settings ap the app are presented in the configuration file:
<appSettings>
<add key="pwa:SiteUrl" value="https://sharepoint.vitalyzhukov.com/sites/pwa"/>
<add key="pwa:Login" value="v.zhukov@vitalyzhukov.com"/>
<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"/>
</appSettings>
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
ctx.ExecuteQuery();
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]);
ctx.ExecuteQuery();
// Update ECF value
resource[fieldInternalName] = "Vitaly Zhukov";
ctx.EnterpriseResources.Update();
ctx.ExecuteQuery();
// Get ECF value from server
ctx.Load(resource,
x=>x[fieldInternalName]);
ctx.ExecuteQuery();
}
To read or modify the field we have to know its internal name. Usually, it equals "Custom_" + field GUID. To load it from the server just reference it in the lambda expression in the Load method.
To send changes to the server use the Update method of EnterpriseResources collection.
Source code
All the source code of the demo project is located on my GitHub: https://github.com/vzhukov.