views:

1203

answers:

3

Suppose we have the following code:

ExpressionHelper.GetRouteValuesFromExpression<AccountController>(ax => ax.MyAction("a", "b"));

(from ASP.NET MVC Futures assembly). Method is reasonably fast - it executes 10k iterations in 150ms.

Now, we change code to this:

string a = "a";
string b = "b";
ExpressionHelper.GetRouteValuesFromExpression<AccountController>(ax => ax.MyAction(a, b));

This code will execute 10k iterations in 15 seconds

The problem is the following code:

Expression<Func<object>> lambdaExpression = Expression.Lambda<Func<object>>(Expression.Convert(arg, typeof (object)));

Func<object> func = lambdaExpression.Compile();

value = func()

Is there a better way to get value from expression than compiling expression every time? This can greatly affect ASP.NET MVC link generation speed.

+1  A: 

Why don't you just cache the value of the expression and its compiled value locally if this is such a bottleneck? I imagine a simply Dictionary could do the trick:

Dictionary<Expression<Action<T>>, Action<T>> m_Cache =
    new Dictionary<Expression<Action<T>>, Action<T>>();

public void GetRouteValuesFromExpression<T>(Expression<Action<T>> expr) {
    Action<T> compiled = null;
    if (!m_Cache.TryGetValue(expr, ref compiled)) {
        compiled = expr.Compile();
        m_Cached.Add(expr, compiled);
    }
    // execute …
}
Konrad Rudolph
I don't think this would work. Expression doesn't contain values if they are not constant. So, in my example above, you would end up caching one link for all possible values of a and b.
bh213
Yes, you're right. Sorry, I didn't realize the values were changing, which is kinda stupid on my part.
Konrad Rudolph
A: 

Does it have to be a Func<object>? You could probably manually craft a "capture" - i.e. have a type that declares a & b; have a Func<Whatever, object>, and compile this to a delegate. Then all you do at runtime is:

Foo foo = new Foo {A = a, B = b};
return cachedFunc(foo);

I'm not quite sure what the Convert(blah, typeof(object)) is doing - can you clarify this? I've got quite a bit of experience with expressions, but this seems... unusual...

Marc Gravell
This is code from ASP.NET MVC futures assembly and the same code is used in Moq to get value of parametres, so I guess this is the "official way" of doing it. I am asking this question because this behaveour reduces number of request in our web site by factor 2.5...
bh213
A: 

I've fiddled for a bit and came up with the following:

var body = (MethodCallExpression)expr.Body;
var arg1 = (MemberExpression)body.Arguments[0];
var contextType = arg1.Member.DeclaringType;
var field = contextType.GetField(arg1.Member.Name);
Console.WriteLine(field.GetValue(…));

Assuming that expr is your Expression<Action<T>> argument, this gives you the reflected field which is passed as the first argument to your call (a in your case). However, I wasn't able to extract the context necessary to evaluate this field (last line, location marked by “…”). I believe this context cannot be accessed without compilation of the expression. As a consequence, what you want isn't possible.

Please prove me wrong. ;-)

(Actually, I'm not really sure because even using the Reflector I'm unable to find where the execution context is being stored so I might overlook something.)

Konrad Rudolph