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-CSOMPropertiesThe 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.