Search code examples
powershellneo4jclient

Constructing a method call


in trying to follow this guide: https://github.com/Readify/Neo4jClient/wiki/cypher-examples#get-all-users-by-label I need to create a lambda expression in order to provide it to the Return method. In C# it looks like this:

.Return(n => n.As<Project>())

and in Powershell I've gone about it this way (as per @PetSerAl's suggestion: Return overload fails):

$exp = [System.Linq.Expressions.Expression]
$param = $exp::Parameter([Neo4jClient.Cyper.ICypherResultItem], "n")
$body = $exp::TypeAs($p, (new-object Project).GetType())
$lambda = $exp::Lambda([Func[Project]], $body, $p)

such that the parameter passed to the lambda expression is typed to receive what the Neo4j expression will pass and the body of the method converts it to a Project (a locally defined class). now I can pass it to my method:

$something.Return($lambda)

however, I get this error

Exception calling "Return" with "1" argument(s): "The expression must be constructed as either an object initializer (for example: n => new MyResultType { Foo = n.Bar }), an anonymous type initializer (for example: n => new { Foo = n.Bar }), a method call (for example: n => n.Count()), or a member accessor (for example: n => n.As().Bar). You cannot supply blocks of code (for example: n => { var a = n + 1; return a; }) or use constructors with arguments (for example: n => new Foo(n)). If you're in F#, tuples are also supported. Parameter name: expression" At line:1 char:1 + $neo.Cypher.Match("n").Return($return) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [], MethodInvocationException + FullyQualifiedErrorId : ArgumentException

which makes it clear I didn't formulate the body of the lambda expression correctly. can anyone suggest how it should be instead?


Solution

  • In C# you have:

    .Return(n => n.As<Project>())
    

    If we take that out and look at the type, n => n.As<Project>() is:

    Expression<Func<ICypherResultItem, Project>>
    

    To create that using Expression Trees in C#, we end up doing something like:

    ParameterExpression parameter = Expression.Parameter(typeof (ICypherResultItem), "n");
    MethodCallExpression right = Expression.Call(parameter, typeof (ICypherResultItem).GetMethod("As").MakeGenericMethod(typeof(Project)));
    Expression<Func<ICypherResultItem, Project>> expression = Expression.Lambda<Func<ICypherResultItem, Project>>(right, parameter);
    

    So, converting that to PowerShell I think it's something like:

    $exp = [System.Linq.Expressions.Expression]
    $param = $exp::Parameter([Neo4jClient.Cypher.ICypherResultItem], "n")
    $body = $exp::Call($param, [Neo4jClient.Cypher.ICypherResultItem].GetMethod("As").MakeGenericMethod[Project])
    $lambda = $exp::Lambda([Func[ICypherResultItem, Project]], $body, $param)
    

    I am in no way a powershell person, and I suspect you'll be able to translate the C# better, but hopefully this will get you onto the right track...

    * Update I * a small fix that makes it all work. declare a var to hold the array of types that MakeGenericMethod expects, and pass that in:

    $PrjType = @((new-object Project).GetType())
    $body = $exp::Call($param, [Neo4jClient.Cypher.ICypherResultItem].GetMethod("As").MakeGenericMethod($PrjType))