A few weeks back I wrote about Fluentsecurty and the small bug it had which made it not work in VB.Net. Yesterday Kristoffer Ahl (The man behind the Fluentsecurity project) sent me a PM via twitter.
I've added your changes. Don't know how to test it properly though, without adding a VB.NET test project. Seems silly for 1 line! Ideas?
And to be honest, I had no idea. But I did some research and found a way that might be good enough.
The code to test
The code to test is this.
- public static string GetActionName(this LambdaExpression actionExpression)
- return ((MethodCallExpression)actionExpression.Body).Method.Name;
And the first thing to get was a failing test with this errormessage.
System.InvalidCastException : Unable to cast object of type 'System.Linq.Expressions.UnaryExpression' to type 'System.Linq.Expressions.MethodCallExpression'.
Because that was the error I got from VB.Net.
First thing to find out is, When is an expression a UnaryExpression.
You will find that MSDN is not very helpful here. It does show us how to create a UnaryExpression but we needed the Body of LambdaExpression to be a UnaryExpression so that did not help much.
So I have to find way to make sure I get a UnaryExpression from a LambdaExpression, while using a MethodCallExpression because that's what this will be used for.
By accident I stumbled on this StackOverflow question. It did not have a solution to my problem but it did have the problem I was trying to recreate.
And this was the most interesting part.
What causes some of the lambdas in my example to yield MemberExpression values and others UnaryExpression values? In my example, the first UnaryExpression is on the third line, the DateTime property, but boolean properties also result in UnaryExpressions.
So the UnaryExperssion is caused by boxing and we all know how we can cause that, right? If not, there is plenty to read about it on MSDN.
And here is now my failing test.
- class TestClass1
- class Test1
- public Boolean test()
- return false;
- public void TestIfGetActionNameDoesNotThrowErrorWhenPassingUnaryExpression()
- Expression<Func<Test1, object>> expression = x => x.test();
We can now change the code in our method to the one I already had in the previous post.
- public static string GetActionName2(this LambdaExpression actionExpression)
- var expression = (MethodCallExpression)(actionExpression.Body is UnaryExpression ? ((UnaryExpression)actionExpression.Body).Operand : actionExpression.Body);
- return expression.Method.Name;
And we now see out test pass.
Making a LambdaExpression into a convincing to be a UnaryEpression is easy, once you know how ;-).