Lambda Expression in SharePoint CSOM PowerShell


Sathish Nadarajan
SharePoint MVP
Published On :   11 Dec 2017
Visit Count
Today :  3    Total :   590
Plan, Migrate, Secure, Report
SharePoint & Office 365 Tool. Simple & Easy to Use. 15-Day Trial!

Sharegate: Kick-Ass Tool
Think Your SharePoint & Office 365 Are Secure ? Find Out Now!


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;
                 ctx.Load(web.Lists);
                 ctx.Load(web);
 
                 ctx.ExecuteQueryRetry();
 
                 List list = web.Lists.GetByTitle("D1");
                 ctx.Load(list);
                 ctx.Load(list, li => li.HasUniqueRoleAssignments);
                 ctx.ExecuteQuery();
 
                 System.Console.WriteLine(Convert.ToString(list.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)

 <#
 .Synopsis
     Facilitates the loading of specific properties of a Microsoft.SharePoint.Client.ClientObject object or Microsoft.SharePoint.Client.ClientObjectCollection object.
 .DESCRIPTION
     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')".
 .EXAMPLE
     Load-CSOMProperties -parentObject $web -collectionObject $web.Fields -propertyNames @("InternalName", "Id") -parentPropertyName "Fields" -executeQuery
     $web.Fields | select InternalName, Id
 .EXAMPLE
    Load-CSOMProperties -object $web -propertyNames @("Title", "Url", "AllProperties") -executeQuery
    $web | select Title, Url, AllProperties
 #>
 
 function global:Load-CSOMProperties {
     [CmdletBinding(DefaultParameterSetName='ClientObject')]
     param (
         # The Microsoft.SharePoint.Client.ClientObject to populate.
         [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0, ParameterSetName = "ClientObject")]
         [Microsoft.SharePoint.Client.ClientObject]
         $object,
 
         # The Microsoft.SharePoint.Client.ClientObject that contains the collection object.
         [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0, ParameterSetName = "ClientObjectCollection")]
         [Microsoft.SharePoint.Client.ClientObject]
         $parentObject,
 
         # The Microsoft.SharePoint.Client.ClientObjectCollection to populate.
         [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 1, ParameterSetName = "ClientObjectCollection")]
         [Microsoft.SharePoint.Client.ClientObjectCollection]
         $collectionObject,
 
         # The object properties to populate
         [Parameter(Mandatory = $true, Position = 1, ParameterSetName = "ClientObject")]
         [Parameter(Mandatory = $true, Position = 2, ParameterSetName = "ClientObjectCollection")]
         [string[]]
         $propertyNames,
 
         # 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")]
         [string]
         $parentPropertyName,
 
         # If specified, execute the ClientContext.ExecuteQuery() method.
         [Parameter(Mandatory = $false, Position = 4)]
         [switch]
         $executeQuery
     )
 
     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"
                 return
             }
             $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@*******.onmicrosoft.com"
 $password = Read-Host 'Enter Password' -AsSecureString
 $Url = "https://*******.sharepoint.com/sites/DeveloperSite/"
 $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
 $context.Load($web)
 #Get the Lists in the Web
 $context.Load($web.Lists)
 $context.ExecuteQuery() 
 
 #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")
 
 $context.ExecuteQuery();
 
 Write-Host $list.HasUniqueRoleAssignments 
 

Happy Coding,

Sathish Nadarajan.

SharePoint Usage Reports
Usage reports, collaboration and audit for SharePoint.
Categories

Migratiin Tools for SharePoint