Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I cache partially-executed LINQ queries?

Tags:

c#

linq

I have the following code:

IEnumerable<KeyValuePair<T, double>> items =
    sequence.Select(item => new KeyValuePair<T, double>(item, weight(item)));
if (items.Any(pair => pair.Value<0))
    throw new ArgumentException("Item weights cannot be less than zero.");

double sum = items.Sum(pair => pair.Value);
foreach (KeyValuePair<T, double> pair in items) {...}

Where weight is a Func<T, double>.

The problem is I want weight to be executed as few times as possible. This means it should be executed at most once for each item. I could achieve this by saving it to an array. However, if any weight returns a negative value, I don't want to continue execution.

Is there any way to accomplish this easily within the LINQ framework?

like image 236
dlras2 Avatar asked Apr 20 '12 22:04

dlras2


People also ask

Are LINQ queries cached?

Starting with . NET Framework 4.5, LINQ queries are cached automatically.

How are LINQ queries executed?

LINQ queries are always executed when the query variable is iterated over, not when the query variable is created. This is called deferred execution. You can also force a query to execute immediately, which is useful for caching query results.

What is compiled query in LINQ?

A compiled query is a cached version of the actual query, that we use to get the results. We write the query. For the first time it is parsed or verified for any kind of syntax error in LINQ, then converted into SQL version and is added into cache.

What is compiled query in SQL?

A compiled query is an object that keeps a prepared SQL statement and a delegate to a materializing function. The first one is to be executed at the server to get rows related to the query, the second one transforms a result set into a sequence of entity objects.


1 Answers

Sure, that's totally doable:

public static Func<A, double> ThrowIfNegative<A, double>(this Func<A, double> f)
{
    return a=>
    { 
      double r = f(a);  
      // if r is NaN then this will throw.
      if ( !(r >= 0.0) )
        throw new Exception(); 
      return r;
    };
}

public static Func<A, R> Memoize<A, R>(this Func<A, R> f)
{
    var d = new Dictionary<A, R>();
    return a=>
    {
        R r;
        if (!d.TryGetValue(a, out r))
        {
          r = f(a);
          d.Add(a, r);
        }
        return r;
    };
}

And now...

Func<T, double> weight = whatever;
weight = weight.ThrowIfNegative().Memoize();

and you're done.

like image 177
Eric Lippert Avatar answered Nov 07 '22 21:11

Eric Lippert