Search code examples
c#.netpowershellpowershell-cmdlet

How to access PSObject DefaultKeyPropertySet details in .NET with C#


I am trying to implement a cmdlet in C# that uses the PSStandardMembers.DefaultKeyPropertySet.ReferencedPropertyNames collection to build a single string key based on the property values of a PSObject.

While I can do this in PowerShell, in a manner such as this:

param($ObjectArray)

$TypeName = $ObjectArray[0].PSObject.TypeNames[0]
$KeyProperty =  $ObjectArray[0].PSStandardMembers.DefaultKeyPropertySet.ReferencedPropertyNames

for ($i=0; $i -lt $ObjectArray.Count; ++$i) {
    $O = $ObjectArray[$i]
    $KeyValue = $O.PSObject.Properties[$KeyProperty].Value -replace ':','::' -replace ';',';;' -replace ',',',,' -join ','
    $KeyValueString = $KeyValue -join ','
    "K;${script:scope};Object::${TypeName};${KeyValueString}" 
}

I'd like to implement something more directly in C# as I'm finding that, for my purposes, there is too much overhead in calling the above via a native PowerShell function.

I'm stuck understanding how I can obtain the referenced property names in C#. The following appears to be appropriate to obtain the correct member collection, but it's not clear how to then obtain the default key property set or the referenced property names from the correct item of the PSMemberInfoCollection.

Assume below that objectList is an array of PSObject and that I would like referencedPropertyNames to be assigned as a reference to the names of the default key properties for the object. It is unclear how I can do the assignment via PSStandardMembers.

foreach (PSObject current in objectList) 
{
    // 
    referencedPropertyNames = current.Members["PSStandardMembers"]; 

Help.


Solution

  • Thank goodness for the PowerShell open source project on GitHub. The answer can unsurprisingly be found in the source for Sort-Object. I'll post it here for posterity.

        internal static string[] GetDefaultKeyPropertySet(PSObject mshObj)
        {
            PSMemberSet standardNames = mshObj.PSStandardMembers;
            if (standardNames == null)
            {
                return null;
            }
            PSPropertySet defaultKeys = standardNames.Members["DefaultKeyPropertySet"] as PSPropertySet;
    
            if (defaultKeys == null)
            {
                return null;
            }
            string[] props = new string[defaultKeys.ReferencedPropertyNames.Count];
            defaultKeys.ReferencedPropertyNames.CopyTo(props, 0);
            return props;
        }
    

    See the helper function for the Sort-Object and Group-Object Cmdlets

    It seems that PSStandardMembers and GetDefaultKeyPropertySet are internal members of the class and are therefore inaccessible from user code. This implies that they should not be used by external components, however it's clear that they may be used directly in PowerShell.

    A solution appears to be:

    public static class Utility  
    {
        private static string[] GetDefaultKeyPropertySet(PSObject mshObj)
        {
            PSMemberSet standardNames = mshObj.Members["PSStandardMembers"] as PSMemberSet;
            if (standardNames == null)
            {
                return null;
            }
            PSPropertySet  defaultKeys = standardNames.Members["DefaultKeyPropertySet"] as PSPropertySet;
    
            if (defaultKeys == null)
            {
                return null;
            }
            string[] props = new string[defaultKeys.ReferencedPropertyNames.Count];
            defaultKeys.ReferencedPropertyNames.CopyTo(props, 0);
            return props;
        }
    }