3

How can I get a string like

Namespace.IMyService.Do("1")

from the expression as demoed in this snip it:

IMyService myService = ...;
int param1 = 1;

myExpressionService.Get(c => myService.Do(param1));

I actually don't want to call Do unless a condition is met using the string that was generated.

Daniel A. White
  • 187,200
  • 47
  • 362
  • 445

3 Answers3

4

You will have to traverse Expression trees. Here is some sample code:

internal static class myExpressionService
{
    public static string Get(Expression<Action> expression)
    {
        MethodCallExpression methodCallExpression = (MethodCallExpression)expression.Body;
        var method = methodCallExpression.Method;
        var argument = (ConstantExpression) methodCallExpression.Arguments.First();

        return string.Format("{0}.{1}({2})", method.DeclaringType.FullName, method.Name, argument.Value);
    }
}

It works if called in this way: string result = myExpressionService.Get(() => myService.Do(1));

The output is: Namespace.IMyService.Do(1)

You can extend/update it to handle your scenario.

Snowbear
  • 16,924
  • 3
  • 43
  • 67
  • Should probably be argument.Value.ToString() and \"{2}\" – ChrisWue Mar 21 '11 at 22:49
  • @Chris, `ToString` is not needed. Also I removed quotes to make it easier to read. Anyway it is not completely what OP wants, just a direction. – Snowbear Mar 21 '11 at 22:52
  • @Snowbear: How do I call the action? – Daniel A. White Mar 21 '11 at 23:11
  • 1
    @Daniel, `expression.Compile()()` will do the job – Snowbear Mar 21 '11 at 23:15
  • This results in an `InvalidCastException` on my machine -- the first argument is a `MemberExpression`, not a `ConstantExpression` – Cameron Mar 21 '11 at 23:22
  • @Cameron, that's why I have `it works if called in this way ...`. Probably you are calling it how OP has written in his answer. My answer is not general it won't cover all the scenarios but it shows the idea how it will be implemented. – Snowbear Mar 21 '11 at 23:25
  • @Snowbear: Ah, I didn't notice you'd changed the way it was called ;-) My answer works with the OP's original input, but it's a lot uglier than your code – Cameron Mar 21 '11 at 23:27
  • @Cameron, I simplified his example to make my code easier ;-) Anyway neither your code nor mine does all the required stuff. Your code won't work with my usage. I believe until we know how exactly PO will use this code there is nothing more we can do, but we provide some ideas on how to approach his scenario. – Snowbear Mar 21 '11 at 23:31
  • @Snowbear: Good point, both our examples are highly specific to the structure of the given expressions – Cameron Mar 21 '11 at 23:33
  • Thanks @snowbear! This really got me underway! – Daniel A. White Mar 21 '11 at 23:45
1

This would also work (though it's not particularly elegant):

public static string MethodCallExpressionRepresentation(this LambdaExpression expr)
{
    var expression = (MethodCallExpression)expr.Body;

    var arguments = string.Join(", ", expression.Arguments.OfType<MemberExpression>().Select(x => {
        var tempInstance = ((ConstantExpression)x.Expression).Value;
        var fieldInfo = (FieldInfo)x.Member;
        return "\"" + fieldInfo.GetValue(tempInstance).ToString() + "\"";
    }).ToArray());

    return expression.Object.Type.FullName + "." + expression.Method.Name + "(" + arguments + ")";
}

You can call it like this:

Expression<Action> expr = c => myService.Do(param1));
var repr = expr.MethodCallExpressionRepresentation();    // Namespace.IMyService.Do("1")
Cameron
  • 96,106
  • 25
  • 196
  • 225
0

You should be able to call .ToString() on the resulting expression. According to the MS documentation ToString() returns the textual representation of the expression. Have you tried that?

ChrisWue
  • 18,612
  • 4
  • 58
  • 83
  • It returns something like `() => value(Namespace.Program+<>c__DisplayClass0).myService.Do(1)` in this case which is slightly different – Snowbear Mar 21 '11 at 22:40