Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting a lambda expression into a unique key for caching

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.....

like image 762
James South Avatar asked Mar 17 '12 03:03

James South


People also ask

Which is faster Linq or lambda?

There is no performance difference between LINQ queries and Lambda expressions.

What does => mean in Linq?

The => operator can be used in two ways in C#: As the lambda operator in a lambda expression, it separates the input variables from the lambda body. In an expression body definition, it separates a member name from the member implementation.

Why use lambda expressions?

Easy-to-Use APIs and Libraries: An API designed using lambda expressions can be easier to use and support other API. Enables support for parallel processing: A lambda expression can also enable us to write parallel processing because every processor is a multi-core processor nowadays.


1 Answers

As Polity and Marc said in their comments, what you need is a partial evaluator of the LINQ expression. You can read how to do that using ExpressionVisitor in Matt Warren's LINQ: Building an IQueryable Provider - Part III. The article Caching the results of LINQ queries by Pete Montgomery (linked to by Polity) describes some more specifics regarding this kind of caching, e.g. how to represent collections in the query.

Also, I'm not sure I would rely on ToString() like this. I think it's meant mostly for debugging purposes and it might change in the future. The alternative would be creating your own IEqualityComparer<Expression> that can create a hash code for any expression and can compare two expressions for equality. I would probably do that using ExpressionVisitor too, but doing so would be quite tedious.

like image 118
svick Avatar answered Sep 21 '22 09:09

svick