diff --git a/Source/EntityFramework.Extended/Caching/Query/Evaluator.cs b/Source/EntityFramework.Extended/Caching/Query/Evaluator.cs
deleted file mode 100644
index 080f6c5..0000000
--- a/Source/EntityFramework.Extended/Caching/Query/Evaluator.cs
+++ /dev/null
@@ -1,138 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq.Expressions;
-
-namespace EntityFramework.Caching
-{
- ///
- /// Enables the partial evaluation of queries.
- ///
- ///
- /// From http://msdn.microsoft.com/en-us/library/bb546158.aspx
- /// Copyright notice http://msdn.microsoft.com/en-gb/cc300389.aspx#O
- ///
- internal static class Evaluator
- {
- ///
- /// Performs evaluation and replacement of independent sub-trees
- ///
- /// The root of the expression tree.
- /// A function that decides whether a given expression node can be part of the local function.
- /// A new tree with sub-trees evaluated and replaced.
- public static Expression PartialEval(Expression expression, Func fnCanBeEvaluated)
- {
- return new SubtreeEvaluator(new Nominator(fnCanBeEvaluated).Nominate(expression)).Eval(expression);
- }
-
- ///
- /// Performs evaluation and replacement of independent sub-trees
- ///
- /// The root of the expression tree.
- /// A new tree with sub-trees evaluated and replaced.
- public static Expression PartialEval(Expression expression)
- {
- return PartialEval(expression, Evaluator.CanBeEvaluatedLocally);
- }
-
- private static bool CanBeEvaluatedLocally(Expression expression)
- {
- return expression.NodeType != ExpressionType.Parameter;
- }
-
- ///
- /// Evaluates and replaces sub-trees when first candidate is reached (top-down)
- ///
- class SubtreeEvaluator : ExpressionVisitor
- {
- HashSet candidates;
-
- internal SubtreeEvaluator(HashSet candidates)
- {
- this.candidates = candidates;
- }
-
- internal Expression Eval(Expression exp)
- {
- return this.Visit(exp);
- }
-
- public override Expression Visit(Expression exp)
- {
- if (exp == null)
- {
- return null;
- }
- if (this.candidates.Contains(exp))
- {
- return this.Evaluate(exp);
- }
- return base.Visit(exp);
- }
-
- private Expression Evaluate(Expression e)
- {
- if (e.NodeType == ExpressionType.Constant)
- {
- return e;
- }
- LambdaExpression lambda = Expression.Lambda(e);
- Delegate fn = lambda.Compile();
- return Expression.Constant(fn.DynamicInvoke(null), e.Type);
- }
-
- protected override Expression VisitMemberInit(MemberInitExpression node)
- {
- if (node.NewExpression.NodeType == ExpressionType.New)
- return node;
-
- return base.VisitMemberInit(node);
- }
- }
-
- ///
- /// Performs bottom-up analysis to determine which nodes can possibly
- /// be part of an evaluated sub-tree.
- ///
- class Nominator : ExpressionVisitor
- {
- Func fnCanBeEvaluated;
- HashSet candidates;
- bool cannotBeEvaluated;
-
- internal Nominator(Func fnCanBeEvaluated)
- {
- this.fnCanBeEvaluated = fnCanBeEvaluated;
- }
-
- internal HashSet Nominate(Expression expression)
- {
- this.candidates = new HashSet();
- this.Visit(expression);
- return this.candidates;
- }
-
- public override Expression Visit(Expression expression)
- {
- if (expression != null)
- {
- bool saveCannotBeEvaluated = this.cannotBeEvaluated;
- this.cannotBeEvaluated = false;
- base.Visit(expression);
- if (!this.cannotBeEvaluated)
- {
- if (this.fnCanBeEvaluated(expression))
- {
- this.candidates.Add(expression);
- }
- else
- {
- this.cannotBeEvaluated = true;
- }
- }
- this.cannotBeEvaluated |= saveCannotBeEvaluated;
- }
- return expression;
- }
- }
- }
-}
\ No newline at end of file
diff --git a/Source/EntityFramework.Extended/Caching/Query/LocalCollectionExpander.cs b/Source/EntityFramework.Extended/Caching/Query/LocalCollectionExpander.cs
deleted file mode 100644
index d05d7f8..0000000
--- a/Source/EntityFramework.Extended/Caching/Query/LocalCollectionExpander.cs
+++ /dev/null
@@ -1,82 +0,0 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Linq;
-using System.Linq.Expressions;
-
-namespace EntityFramework.Caching
-{
- ///
- /// Enables cache key support for local collection values.
- ///
- internal class LocalCollectionExpander : ExpressionVisitor
- {
- public static Expression Rewrite(Expression expression)
- {
- return new LocalCollectionExpander().Visit(expression);
- }
-
- protected override Expression VisitMethodCall(MethodCallExpression node)
- {
- // pair the method's parameter types with its arguments
- var map = node.Method.GetParameters()
- .Zip(node.Arguments, (p, a) => new { Param = p.ParameterType, Arg = a })
- .ToLinkedList();
-
- // deal with instance methods
- var instanceType = node.Object == null ? null : node.Object.Type;
- map.AddFirst(new { Param = instanceType, Arg = node.Object });
-
- // for any local collection parameters in the method, make a
- // replacement argument which will print its elements
- var replacements = (from x in map
- where x.Param != null && x.Param.IsGenericType
- let g = x.Param.GetGenericTypeDefinition()
- where g == typeof(IEnumerable<>) || g == typeof(List<>)
- where x.Arg.NodeType == ExpressionType.Constant
- let elementType = x.Param.GetGenericArguments().Single()
- let printer = MakePrinter((ConstantExpression)x.Arg, elementType)
- select new { x.Arg, Replacement = printer }).ToList();
-
- if (replacements.Any())
- {
- var args = map.Select(x => (from r in replacements
- where r.Arg == x.Arg
- select r.Replacement).SingleOrDefault() ?? x.Arg).ToList();
-
- node = node.Update(args.First(), args.Skip(1));
- }
-
- return base.VisitMethodCall(node);
- }
-
- ConstantExpression MakePrinter(ConstantExpression enumerable, Type elementType)
- {
- var value = (IEnumerable)enumerable.Value;
- var printerType = typeof(Printer<>).MakeGenericType(elementType);
- var printer = Activator.CreateInstance(printerType, value);
-
- return Expression.Constant(printer);
- }
-
- ///
- /// Overrides ToString to print each element of a collection.
- ///
- ///
- /// Inherits List in order to support List.Contains instance method as well
- /// as standard Enumerable.Contains/Any extension methods.
- ///
- class Printer : List
- {
- public Printer(IEnumerable collection)
- {
- this.AddRange(collection.Cast());
- }
-
- public override string ToString()
- {
- return "{" + this.ToConcatenatedString(t => t.ToString(), "|") + "}";
- }
- }
- }
-}
\ No newline at end of file
diff --git a/Source/EntityFramework.Extended/Caching/Query/QueryCache.cs b/Source/EntityFramework.Extended/Caching/Query/QueryCache.cs
index dcb57ea..9d07d81 100644
--- a/Source/EntityFramework.Extended/Caching/Query/QueryCache.cs
+++ b/Source/EntityFramework.Extended/Caching/Query/QueryCache.cs
@@ -1,6 +1,5 @@
-using System;
-using System.Linq;
-using System.Linq.Expressions;
+using System.Linq;
+using EntityFramework.Extensions;
namespace EntityFramework.Caching
{
@@ -20,18 +19,10 @@ public static class QueryCache
///
/// The query to get the key from.
/// A unique key for the specified
- public static string GetCacheKey(this IQueryable query)
+ public static string GetCacheKey(this IQueryable query) where TEntity : class
{
- var expression = query.Expression;
-
- // locally evaluate as much of the query as possible
- expression = Evaluator.PartialEval(expression, QueryCache.CanBeEvaluatedLocally);
-
- // support local collections
- expression = LocalCollectionExpander.Rewrite(expression);
-
- // use the string representation of the expression for the cache key
- string key = expression.ToString();
+ // The key is made up of the SQL that will be executed, along with any parameters and their values
+ string key = query.ToTraceString();
// the key is potentially very long, so use an md5 fingerprint
// (fine if the query result data isn't critically sensitive)
@@ -39,24 +30,5 @@ public static string GetCacheKey(this IQueryable query)
return key;
}
-
- static Func CanBeEvaluatedLocally
- {
- get
- {
- return expression =>
- {
- // don't evaluate parameters
- if (expression.NodeType == ExpressionType.Parameter)
- return false;
-
- // can't evaluate queries
- if (typeof(IQueryable).IsAssignableFrom(expression.Type))
- return false;
-
- return true;
- };
- }
- }
}
}
diff --git a/Source/EntityFramework.Extended/EntityFramework.Extended.net40.csproj b/Source/EntityFramework.Extended/EntityFramework.Extended.net40.csproj
index 948e605..6dfa3b0 100644
--- a/Source/EntityFramework.Extended/EntityFramework.Extended.net40.csproj
+++ b/Source/EntityFramework.Extended/EntityFramework.Extended.net40.csproj
@@ -86,8 +86,6 @@
-
-
@@ -152,7 +150,6 @@
-