Insight

Insight from Agora Consultants

Project Server CSOM – Making Calls

As a developer in the Project Server world, you will find that documentation can be hard to find. Even if you take an issue you are having to the MSDN forums chances of finding a solution are slim. The simple fact is the developer community behind Project Server is small and error messages can be vague. With the introduction of the Client-Side Object Model (CSOM) these issues have only become more amplified. This blog will give developers some useful information for using the CSOM against online environments.

The general structure of a CSOM call has three parts.

1. The query declaration

2. The load statement

3. The execute statement

1. The Query Declaration

The most important part of making a call is defining your query. This is where you would declare any filters, but more importantly where you declare which data you would like to load. In my experience working with this framework, the most frequent problem I had was that the data I wanted simply was not loaded. An exception message you likely recognize:

A great way to ensure that the data you want is returned from the server is to explicitly ask for it using the Include() LINQ function.

For example, if you want to load the project owner’s login name a query like this is not good enough:

var query = projContext.Projects;

This query will load basic header information for each project only. The statement below will return what we are looking for. Notice how the Owner object as a whole is included and then the LoginName property is referenced as well.

var query = projContext.Projects.Include( x=> x.Name, x => x.Id, x => x.Owner, x => x.Owner.LoginName);

Custom fields have their own behaviour. To get custom field values, you need to include IncludeCustomFields:

var query = projContext.Projects.Include( x => x.IncludeCustomFields );

With custom field values, there is no need to explicitly state the field, doing the above statement will load all of the project’s custom field values.

Filtering

If you have used LINQ before, filtering will be very familiar to you. This query filters on Id.

var projInfoQuery = projContext.Projects

            .Include(x => x.Name, x => x.Id, x => x.IncludeCustomFields)

            .Where(x => x.Id == projectUID);

You can have AND and OR statements just like any other LINQ query, but be warned, you must Include() what you are filtering on or you will raise the dreaded PropertyOrFieldNotInitialized exception.

2. The Load Statement

For the LoadQuery() statement, all that is required is to assign the results to a collection.

IEnumerable<PublishedProject> projects = projContext.LoadQuery(query);

The compiler will tell you which type of collection it expects based on the LINQ query you have stored in the query variable. Remember that projects does not contain any data after this line; it is still null.

3. The Execute Statement

Finally it is time to go to the server and get the data. After this line is called, whatever collection you specified in the LoadQuery() statement will contain the data that you have included in the LINQ query.

projContext.ExecuteQuery();

You can now loop through your collection and gather the information you need. If you specified this LINQ query to get the owner’s login name for a specific project:

var query = projContext.Projects

.Include(x => x.Name, x => x.Id, x => x.Owner, x => x.Owner.LoginName)

       .Where(x => x.Id == projectUID);

IEnumerable<PublishedProject> projects = projContext.LoadQuery(query);

projContext.ExecuteQuery();

You can simply grab the first project in the collection and get the login name.

Console.Write( projects.ElementAt(0).Owner.LoginName );

Referencing custom field values is a bit different. This took a very long time to figure out given that MSDN is silent on this, but to read a custom field value you do this:

foreach(PublishedProject p in projects){

p.IncludeCustomFields.FieldValues[“Custom_ 27a4f85047994c5aa51712f92d2d42d0”];

}

There are two tricks to this. The first is that you need to go into IncludeCustomFields.FieldValues, instead of just accessing the FieldValues collection that is on the PublishedProject object. The second is that the Key for a custom field value is its internal name. To get a field’s internal name, you simply remove all the dashes (-) from the field’s GUID and add Custom_ before it.

Hopefully making calls with CSOM is easier after reading this article. Using Include() to load exactly what you need and the tricks to reading custom field values will save you a few days headache at least.


 
Comments are closed