Alex Rivera | Logout

Converting a lambda expression into a unique key for caching

Asked 2012-03-17T03:01:20.267
12

I've had a look at other questions similar to this one but I couldn't find any workable answers.

I've been using the following code to generate unique keys for storing the results of my linq queries to the cache.

    string key = ((LambdaExpression)expression).Body.ToString();

    foreach (ParameterExpression param in expression.Parameters)
    {
        string name = param.Name;
        string typeName = param.Type.Name;

        key = key.Replace(name + ".", typeName + ".");
    }

    return key;

It seems to work fine for simple queries containing integers or booleans but when my query contains nested constant expressions e.g.

// Get all the crops on a farm where the slug matches the given slug.
(x => x.Crops.Any(y => slug == y.Slug) && x.Deleted == false)

The key returned is thus:

(True AndAlso (Farm.Crops.Any(y => (value(OzFarmGuide.Controllers.FarmController+<>c__DisplayClassd).slug == y.Slug)) AndAlso (Farm.Deleted == False)))

As you can see any crop name I pass will give the same key result. Is there a way I can extract the value of the given parameter so that I can differentiate between my queries?

Also converting the y to say the correct type name would be nice.....

Edit
Report

1 Answer

1

What about this?

public class KeyGeneratorVisitor : ExpressionVisitor
{
    protected override Expression VisitParameter(ParameterExpression node)
    {
        return Expression.Parameter(node.Type, node.Type.Name);
    }

    protected override Expression VisitMember(MemberExpression node)
    {
        if (CanBeEvaluated(node))
        {
            return Expression.Constant(Evaluate(node));
        }
        else
        {
            return base.VisitMember(node);
        }
    }

    private static bool CanBeEvaluated(MemberExpression exp)
    {
        while (exp.Expression.NodeType == ExpressionType.MemberAccess)
        {
            exp = (MemberExpression) exp.Expression;
        }

        return (exp.Expression.NodeType == ExpressionType.Constant);
    }

    private static object Evaluate(Expression exp)
    {
        if (exp.NodeType == ExpressionType.Constant)
        {
            return ((ConstantExpression) exp).Value;
        }
        else
        {
            MemberExpression mexp = (MemberExpression) exp;
            object value = Evaluate(mexp.Expression);

            FieldInfo field = mexp.Member as FieldInfo;
            if (field != null)
            {
                return field.GetValue(value);
            }
            else
            {
                PropertyInfo property = (PropertyInfo) mexp.Member;
                return property.GetValue(value, null);
            }
        }
    }
}

This will replace the complex constant expressions to their original values as well as the parameter names to their type names. So just have to create a new KeyGeneratorVisitor instance and call its Visit or VisitAndConvert method with your expression.

Please note that the Expression.ToString method will be also invoked on your complex types, so either override their ToString methods or write a custom logic for them in the Evaluate<

answered 2012-03-17T16:13:43.307

Your Answer