如何在PowerShell中执行lambda表达式?

时间:2015-12-17 13:52:48

标签: powershell lambda

以下在C#中使用正常(使用Microsoft.SharePoint.Client):

ClientContext sourceCtx = new ClientContext(sourceSiteUrl);
sourceCtx.Credentials = new NetworkCredential("username", "password");
Site sourceSite = sourceCtx.Site;
sourceCtx.Load(sourceSite, s => s.Usage);
sourceCtx.ExecuteQuery();

但是,我真的希望这在PowerShell中运行。我无法弄清楚的部分是lambda表达式s => s.Usage。看this question我看到答案可能是内联脚本块,所以我尝试了以下内容:

$lambda = {
    param($s)
    $s.Usage
}
$sourceCtx.Load($sourceSite, $lambda)
$sourceCtx.ExecuteQuery();

但是在Load方法中,以下内容失败了:

  

无法找到" load"和参数计数:" 2"。

基本上,我需要进行某种类型转换:

Expression<Func<T, Object>>[]

有什么想法吗?

2 个答案:

答案 0 :(得分:3)

在我的无知中,我认为这不会是坏事......  它不会很有趣,但这似乎是唯一的例子

http://www.itunity.com/article/loading-specific-values-lambda-expressions-sharepoint-csom-api-windows-powershell-1249

C:\Scripts\Load-CSOMProperties.ps1 
$web = $ctx.Web 
Load-CSOMProperties -object $web -propertyNames @("AllProperties", "Url", "Title") 
$ctx.ExecuteQuery() 

$web = $ctx.Web 
Load-CSOMProperties -parentObject $web -collectionObject $web.Fields -propertyNames @("Id", "InternalName") -parentPropertyName "Fields" 
$ctx.ExecuteQuery()

<#
.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 { }
}

答案 1 :(得分:1)

如果你能用c#做,你可以在powershell中做到这一点!

add-type @"
namespace MyNamespace
{
    public class MyProgram
    {
        public static string MyMethod (string args)
        {
            return args;
        }
    }
}
"@

[MyNamespace.MyProgram]::MyMethod('Hello World!')