Second Life of a Hungarian SharePoint Geek

June 25, 2014

How to restrict the properties of a single object in the results of Client Object Model requests sent from PowerShell on the server side

Filed under: Managed Client OM, PowerShell, Reflection, SP 2013 — Tags: , , , — Peter Holpar @ 21:49

In my recent posts I illustrated how can we restrict (either using expression trees or via dynamically compiled assemblies) on the server side the properties of entity collections returned by the client object model requests when using PowerShell. Although it has less effect on the network traffic, it may be interesting to see, how can we achieve a similar result when working with single objects (like a single list instance) instead of entity collections (for example, fields of a list).

A query without property restrictions might look like this in C#:

ClientContext ctx = new ClientContext("http://sp2013");
var list = ctx.Web.Lists.GetByTitle("Images");
ctx.Load(list);
ctx.ExecuteQuery();

The request contains no property restriction (see SelectAllProperties=true below),

image

and the response contains indeed dozens of properties.

image

In C# it is quite simple to specify using lambda expressions, which properties of the list we need:

ClientContext ctx = new ClientContext("http://sp2013");
var list = ctx.Web.Lists.GetByTitle("Images");
ctx.Load(list, l => l.Title, l => l.ItemCount);
ctx.ExecuteQuery();

The request will include the limitation,

image

and only the requested properties are returned in the response:

image

But how to achieve the same result from PowerShell?

Again, we can easily refactor the code with the lambda expressions into a static helper class:

public static class QueryHelper
{
    public static void LoadListWithLimtedFields(ClientContext ctx, List list)
    {
        ctx.Load(list, l => l.Title, l => l.ItemCount);
    }
}

In this case, the original method contains only the code below that is easy to translate to PowerShell:

ClientContext ctx = new ClientContext("http://sp2013");
var list = ctx.Web.Lists.GetByTitle("Images");
QueryHelper.LoadListWithLimtedFields(ctx, list);
ctx.ExecuteQuery();

The same functionality transformed to PowerShell is shown below:

  1. $url = "http://sp2013"
  2.  
  3. $referencedAssemblies = (
  4.     "Microsoft.SharePoint.Client, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c",
  5.     "Microsoft.SharePoint.Client.Runtime, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c",
  6.     "System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
  7. $sourceCode = @"
  8. using Microsoft.SharePoint.Client;
  9. using System.Collections.Generic;
  10. using System.Linq;
  11.  
  12. public static class QueryHelper
  13. {
  14.     public static void LoadListWithLimtedFields(ClientContext ctx, List list)
  15.     {
  16.         ctx.Load(list, l => l.Title, l => l.ItemCount);
  17.     }
  18. }
  19. "@
  20.  
  21. Add-Type -ReferencedAssemblies $referencedAssemblies -TypeDefinition $sourceCode -Language CSharp;
  22. Add-Type -Path "C:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.dll"
  23. Add-Type -Path "C:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.Runtime.dll"
  24.  
  25. $ctx = New-Object Microsoft.SharePoint.Client.ClientContext($url)
  26. $list = $ctx.Web.Lists.GetByTitle("Images")
  27. [QueryHelper]::LoadListWithLimtedFields($ctx, $list)
  28. $ctx.ExecuteQuery()
  29.  
  30. Write-Host $list.Title, $list.ItemCount

Although more complex as the former solution, and recommended only for the hard-core developers, we can achieve the same result using expression trees built using Reflection. Let’s see how Visual Studio compiles the lambda expressions in our C# code into plain .NET objects and method calls. We have to build the code and open the assembly with a decompiler tool.

The original code as decompiled by JetBrains dotPeek:

ClientContext clientContext = new ClientContext("http://sp2013");
List byTitle = clientContext.Web.Lists.GetByTitle("Images");
clientContext.Load<List>(byTitle, new Expression<Func<List, object>>[2]
{
  (Expression<Func<List, object>>) (l => l.Title),
  (Expression<Func<List, object>>) (l => (object) l.ItemCount)
});
clientContext.ExecuteQuery();

The original code as decompiled by Reflector:

ParameterExpression CS$0$0001;
ClientContext ctx = new ClientContext("http://sp2013&quot;);
List list = ctx.Web.Lists.GetByTitle("Images");
ctx.Load<List>(list, new Expression<Func<List, object>>[] { Expression.Lambda<Func<List, object>>(Expression.Property(CS$0$0001 = Expression.Parameter(typeof(List), "l"), (MethodInfo) methodof(List.get_Title)), new ParameterExpression[] { CS$0$0001 }), Expression.Lambda<Func<List, object>>(Expression.Convert(Expression.Property(CS$0$0001 = Expression.Parameter(typeof(List), "l"), (MethodInfo) methodof(List.get_ItemCount)), typeof(object)), new ParameterExpression[] { CS$0$0001 }) });
ctx.ExecuteQuery();

The decompiled code helps us to create the same expression tree from PowerShell using Reflection:

  1. $url = "http://sp2013&quot;
  2.  
  3. # load the required client object model assemblies
  4. Add-Type -Path "C:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.dll"
  5. Add-Type -Path "C:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.Runtime.dll"
  6.  
  7. $ctx = New-Object Microsoft.SharePoint.Client.ClientContext($url)
  8. $list = $ctx.Web.Lists.GetByTitle("Images")
  9.  
  10. $expressionType = [System.Linq.Expressions.Expression]
  11. $parameterExpressionArrayType = [System.Linq.Expressions.ParameterExpression].MakeArrayType()
  12.  
  13. $lambdaMethod = $expressionType.GetMethods() | ? { $_.Name -eq "Lambda" -and $_.IsGenericMethod -and $_.GetParameters().Length -eq 2  -and $_.GetParameters()[1].ParameterType -eq $parameterExpressionArrayType }
  14. $lambdaMethodGeneric = $lambdaMethod.MakeGenericMethod([System.Func“2[Microsoft.SharePoint.Client.List,System.Object]])
  15.  
  16. $listType = [Microsoft.SharePoint.Client.List]
  17.  
  18. # query the Title propery of type System.String
  19. $param1 = [System.Linq.Expressions.Expression]::Parameter($listType, "l")
  20. $name1 = [System.Linq.Expressions.Expression]::Property($param1, "Title")
  21. $expression1 = $lambdaMethodGeneric.Invoke($Null, [System.Object[]] @($name1, [System.Linq.Expressions.ParameterExpression[]] @($param1)))
  22.  
  23. # query the ItemCount of type System.Int32
  24. $param2 = [System.Linq.Expressions.Expression]::Parameter($listType, "l")
  25. $name2 = [System.Linq.Expressions.Expression]::Property($param2, "ItemCount")
  26.  
  27. # convert the Int32 type to object
  28. $body2 = [System.Linq.Expressions.Expression]::Convert($name2, [System.Object])
  29.  
  30. $expression2 = $lambdaMethodGeneric.Invoke($Null, [System.Object[]] @($body2, [System.Linq.Expressions.ParameterExpression[]] @($param2)))
  31.  
  32. $loadMethod = [Microsoft.SharePoint.Client.ClientRuntimeContext].GetMethod("Load")
  33. $loadMethodGeneric = $loadMethod.MakeGenericMethod([Microsoft.SharePoint.Client.List])
  34.  
  35. # call Load with both of the expressions
  36. $loadMethodGeneric.Invoke($ctx, [System.Object[]] @($list, [System.Linq.Expressions.Expression`1[System.Func“2[Microsoft.SharePoint.Client.List,System.Object]][]] @($expression1, $expression2)))
  37.  
  38. $ctx.ExecuteQuery()
  39.  
  40. Write-Host $list.Title, $list.ItemCount

Using the methods described in this and in the former posts we can achieve the same network traffic limiting results from PowerShell as we got used to in the case of the C# code.

Leave a Comment »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Create a free website or blog at WordPress.com.

%d bloggers like this: