Lambda Expression in SharePoint CSOM PowerShell

Sathish Nadarajan
Solution Architect
December 11, 2017
Rate this article
[Total: 0    Average: 0/5]

In the CSOM, we might have using the Lambda Expression => frequently to load only the required properties. But the Lambda Expression is an Operator in C# not in PowerShell. When, we started coding with the PowerShell CSOM intensively, I required this Lambda to be replaced with its equivalent. When searching for that, Got a wonderful article by Gary here. Wanted to make it to be handy here as well.

To be more precise, let us see a scenario in C#.

 using (var ctx = authMgr.GetSharePointOnlineAuthenticatedContextTenant(siteUrl, userName, password))
                 Web web = ctx.Web;
                 List list = web.Lists.GetByTitle("D1");
                 ctx.Load(list, li => li.HasUniqueRoleAssignments);

Gary has come up with a Method which is equivalent to our Lambda Expression – Load-CSOMProperties

The Lambda Expression method is as below. (Re-wrote from Gary’s article)

     Facilitates the loading of specific properties of a Microsoft.SharePoint.Client.ClientObject object or Microsoft.SharePoint.Client.ClientObjectCollection object.
     Replicates what you would do with a lambda expression in C#. 
     For example, "ctx.Load(list, l => list.Title, l => list.Id)" becomes
     "Load-CSOMProperties -object $list -propertyNames @('Title', 'Id')".
     Load-CSOMProperties -parentObject $web -collectionObject $web.Fields -propertyNames @("InternalName", "Id") -parentPropertyName "Fields" -executeQuery
     $web.Fields | select InternalName, Id
    Load-CSOMProperties -object $web -propertyNames @("Title", "Url", "AllProperties") -executeQuery
    $web | select Title, Url, AllProperties
 function global:Load-CSOMProperties {
     param (
         # The Microsoft.SharePoint.Client.ClientObject to populate.
         [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0, ParameterSetName = "ClientObject")]
         # The Microsoft.SharePoint.Client.ClientObject that contains the collection object.
         [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0, ParameterSetName = "ClientObjectCollection")]
         # The Microsoft.SharePoint.Client.ClientObjectCollection to populate.
         [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 1, ParameterSetName = "ClientObjectCollection")]
         # The object properties to populate
         [Parameter(Mandatory = $true, Position = 1, ParameterSetName = "ClientObject")]
         [Parameter(Mandatory = $true, Position = 2, ParameterSetName = "ClientObjectCollection")]
         # The parent object's property name corresponding to the collection object to retrieve (this is required to build the correct lamda expression).
         [Parameter(Mandatory = $true, Position = 3, ParameterSetName = "ClientObjectCollection")]
         # If specified, execute the ClientContext.ExecuteQuery() method.
         [Parameter(Mandatory = $false, Position = 4)]
     begin { }
     process {
         if ($PsCmdlet.ParameterSetName -eq "ClientObject") {
             $type = $object.GetType()
         } else {
             $type = $collectionObject.GetType() 
             if ($collectionObject -is [Microsoft.SharePoint.Client.ClientObjectCollection]) {
                 $type = $collectionObject.GetType().BaseType.GenericTypeArguments[0]
         $exprType = [System.Linq.Expressions.Expression]
         $parameterExprType = [System.Linq.Expressions.ParameterExpression].MakeArrayType()
         $lambdaMethod = $exprType.GetMethods() | ? { $_.Name -eq "Lambda" -and $_.IsGenericMethod -and $_.GetParameters().Length -eq 2 -and $_.GetParameters()[1].ParameterType -eq $parameterExprType }
         $lambdaMethodGeneric = Invoke-Expression "`$lambdaMethod.MakeGenericMethod([System.Func``2[$($type.FullName),System.Object]])"
         $expressions = @()
         foreach ($propertyName in $propertyNames) {
             $param1 = [System.Linq.Expressions.Expression]::Parameter($type, "p")
             try {
                 $name1 = [System.Linq.Expressions.Expression]::Property($param1, $propertyName)
             } catch {
                 Write-Error "Instance property '$propertyName' is not defined for type $type"
             $body1 = [System.Linq.Expressions.Expression]::Convert($name1, [System.Object])
             $expression1 = $lambdaMethodGeneric.Invoke($null, [System.Object[]] @($body1, [System.Linq.Expressions.ParameterExpression[]] @($param1)))
             if ($collectionObject -ne $null) {
                 $expression1 = [System.Linq.Expressions.Expression]::Quote($expression1)
             $expressions += @($expression1)
         if ($PsCmdlet.ParameterSetName -eq "ClientObject") {
             $object.Context.Load($object, $expressions)
             if ($executeQuery) { $object.Context.ExecuteQuery() }
         } else {
             $newArrayInitParam1 = Invoke-Expression "[System.Linq.Expressions.Expression``1[System.Func````2[$($type.FullName),System.Object]]]"
             $newArrayInit = [System.Linq.Expressions.Expression]::NewArrayInit($newArrayInitParam1, $expressions)
             $collectionParam = [System.Linq.Expressions.Expression]::Parameter($parentObject.GetType(), "cp")
             $collectionProperty = [System.Linq.Expressions.Expression]::Property($collectionParam, $parentPropertyName)
             $expressionArray = @($collectionProperty, $newArrayInit)
             $includeMethod = [Microsoft.SharePoint.Client.ClientObjectQueryableExtension].GetMethod("Include")
             $includeMethodGeneric = Invoke-Expression "`$includeMethod.MakeGenericMethod([$($type.FullName)])"
             $lambdaMethodGeneric2 = Invoke-Expression "`$lambdaMethod.MakeGenericMethod([System.Func``2[$($parentObject.GetType().FullName),System.Object]])"
             $callMethod = [System.Linq.Expressions.Expression]::Call($null, $includeMethodGeneric, $expressionArray)
             $expression2 = $lambdaMethodGeneric2.Invoke($null, @($callMethod, [System.Linq.Expressions.ParameterExpression[]] @($collectionParam)))
             $parentObject.Context.Load($parentObject, $expression2)
             if ($executeQuery) { $parentObject.Context.ExecuteQuery() }
     end { }

After this, we can use the method Load-CSOMProperties which is similar to our Lambda Expression.

The actual Usage is as follows.

 # Add Necessary Client DLLs
 Add-Type -Path "C:\SATHISH\ClientDLLs\Microsoft.SharePoint.Client.dll" 
 Add-Type -Path "C:\SATHISH\ ClientDLLs\Microsoft.SharePoint.Client.Runtime.dll" 
 #Get the UserName and Password
 $UserName = "sathish@*******"
 $password = Read-Host 'Enter Password' -AsSecureString
 $Url = "https://*******"
 $credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($UserName , $Password)
 $context = New-Object Microsoft.SharePoint.Client.ClientContext($Url)
 $context.Credentials = $credentials        
 #Get the Web
 $web = $context.Web
 #Get the Lists in the Web
 #Get a specific List
 $list = $web.Lists.GetByTitle("RamList")
 #Usage of Lambda Operator.  I am Loading the HasUniqueRoleASsignments similar to #the Lambda Operator
 Load-CSOMProperties -object $list -propertyNames @("HasUniqueRoleAssignments")
 Write-Host $list.HasUniqueRoleAssignments 

Happy Coding,

Sathish Nadarajan.

Category : CSOM, PowerShell, SharePoint

Author Info

Sathish Nadarajan
Solution Architect
Rate this article
[Total: 0    Average: 0/5]
Sathish is a Microsoft MVP for SharePoint (Office Servers and Services) having 13+ years of experience in Microsoft Technologies. He holds a Masters Degree in Computer Aided Design and Business more

Leave a comment