Finding or filtering resources, tasks and projects can be done in several ways. What you need is the internal name of the custom field. We can retrieve the objectsAssuming you want to make your application compatible with multiple PWA’s so you can reuse your code, it might be a good idea to use the CustomField name first to retrieve the InternalName property of the CustomField object. We will take the Microsoft.ProjectServer.Client.CustomField class. There are three properties you must remember:

Property Description Object Type Example
Name This is the name you have given to your custom field. string Resource_Alias
Id This is the unique identifier for the custom field. Guid 554b6e30-b1d4-46c2-b85a-98fd21d3f425
InternalName This is a string in the format ‘Custom_’ with the dashes removed. string Custom_554b6e30b1d446c2b85a98fd21d3f425

In case you want to look up the ‘InternalName‘ property of the custom field, you can do that with the code below. Note that depending on which method you use ‘Single‘, ‘Find‘ or ‘First‘ it will affect the lookup speed.

public string GetInternalName(string customFieldName)
{
    using (ProjectContext projContext = new ProjectContext("http://domain/pwa")) // Connect to Project Server
    {
        CustomFieldCollection customFields = projContext.CustomFields;
        projContext.Load(customFields);
        projContext.ExecuteQuery();
        return customFields.Single(c => c.Name.Equals(customFieldName)).InternalName;
    }
}

Now if we pass the parameter ‘Resource_Alias‘ to the method ‘GetInternalName‘, we will get an exception if more than one result is found, which is impossible since you cannot define a custom field with the same name twice. But if the custom field is found once, we will get a string value in the format: ‘Custom_554b6e30b1d446c2b85a98fd21d3f425‘. This is basically ‘_Custom__‘ with a guid string appended to it. Now what we need to do is retrieve all the resources from ProjectServer, and filter based on the value we just retrieved.

// Define the name of your custom field here
public const string CustomFieldName = "Resource_Alias";

public static EnterpriseResource FindResource(string resourceAlias)
{
    using (ProjectContext projContext = new ProjectContext("http://domain/pwa")) // Connect to Project Server
    {
        // Load all resources
        EnterpriseResourceCollection resources = projContext.EnterpriseResources;
        projContext.Load(resources);
        projContext.ExecuteQuery();

        // Convert to list
        List<EnterpriseResource> resList = resources.ToList();

        // Filter
        string internalName = GetInternalName(CustomFieldName); // You can also enter the InternalName property directly here.

        // Beware PropertyNotInitializedException
        EnterpriseResource resource = resList.Find(r => r.FieldValues[internalName].ToString().Equals(resourceAlias));

        // return it
        return resource;
    }
}

This method will work for every object type that uses custom fields in Project Server. Also not that instead of:

r.FieldValues[internalName]

You can also use:

r[internalName]

Now in the large block of code above, I highlighted a small part:

EnterpriseResource resource = resList.Find(r => r.FieldValues[internalName].ToString().Equals(resourceAlias));

It appears that this can crash your application with a ‘PropertyNotInitializedException‘ if the custom field is empty for a certain task, resource or project. One way to solve this is by writing your code like this:

public EnterpriseResource FindResource(string resource_alias)
{
    foreach (var r in resources)
    {
        try
        {
            var value = r.FieldValues["Custom_893e3e3001b744fc9b3eb19965e9e0d2"].ToString();
            if(value.Equals(resource_alias)
            {
                return r;
            }
        }
        catch (KeyNotFoundException)
        {
            continue;
        }
        catch (PropertyOrFieldNotInitializedException)
        {
            continue;
        }
    }
    throw new ObjectNotFoundException("No matched resource found.");
}