|
From: <ste...@us...> - 2010-03-22 15:18:05
|
Revision: 4964
http://nhibernate.svn.sourceforge.net/nhibernate/?rev=4964&view=rev
Author: steverstrong
Date: 2010-03-22 15:17:58 +0000 (Mon, 22 Mar 2010)
Log Message:
-----------
Added support for Query Caching in the Linq provider
Modified Paths:
--------------
trunk/nhibernate/src/NHibernate/Linq/LinqExtensionMethods.cs
trunk/nhibernate/src/NHibernate/Linq/NhRelinqQueryParser.cs
trunk/nhibernate/src/NHibernate/Linq/Visitors/QueryModelVisitor.cs
trunk/nhibernate/src/NHibernate/NHibernate.csproj
trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj
Added Paths:
-----------
trunk/nhibernate/src/NHibernate/Linq/TypeHelperExtensionMethods.cs
trunk/nhibernate/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessCacheable.cs
trunk/nhibernate/src/NHibernate.Test/Linq/QueryCacheableTests.cs
Modified: trunk/nhibernate/src/NHibernate/Linq/LinqExtensionMethods.cs
===================================================================
--- trunk/nhibernate/src/NHibernate/Linq/LinqExtensionMethods.cs 2010-03-17 17:16:04 UTC (rev 4963)
+++ trunk/nhibernate/src/NHibernate/Linq/LinqExtensionMethods.cs 2010-03-22 15:17:58 UTC (rev 4964)
@@ -1,6 +1,5 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
+using System.Linq;
+using System.Linq.Expressions;
namespace NHibernate.Linq
{
@@ -11,47 +10,31 @@
return new NhQueryable<T>(session);
}
- public static void ForEach<T>(this IEnumerable<T> query, Action<T> method)
+ public static IQueryable<T> Cacheable<T>(this IQueryable<T> query)
{
- foreach (T item in query)
- {
- method(item);
- }
- }
+ var method = ReflectionHelper.GetMethod(() => Cacheable<object>(null)).MakeGenericMethod(typeof(T));
- public static bool IsEnumerableOfT(this System.Type type)
- {
- return type.IsGenericType && type.GetGenericTypeDefinition() == typeof (IEnumerable<>);
- }
+ var callExpression = Expression.Call(method, query.Expression);
- public static bool IsNullable(this System.Type type)
- {
- return (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>));
+ return new NhQueryable<T>(query.Provider, callExpression);
}
- public static bool IsNullableOrReference(this System.Type type)
+ public static IQueryable<T> CacheMode<T>(this IQueryable<T> query, CacheMode cacheMode)
{
- return !type.IsValueType || type.IsNullable();
- }
+ var method = ReflectionHelper.GetMethod(() => CacheMode<object>(null, NHibernate.CacheMode.Normal)).MakeGenericMethod(typeof(T));
- public static System.Type NullableOf(this System.Type type)
- {
- return type.GetGenericArguments()[0];
- }
+ var callExpression = Expression.Call(method, query.Expression, Expression.Constant(cacheMode));
- public static bool IsPrimitive(this System.Type type)
- {
- return (type.IsValueType || type.IsNullable() || type == typeof (string));
+ return new NhQueryable<T>(query.Provider, callExpression);
}
- public static bool IsNonPrimitive(this System.Type type)
+ public static IQueryable<T> CacheRegion<T>(this IQueryable<T> query, string region)
{
- return !type.IsPrimitive();
- }
+ var method = ReflectionHelper.GetMethod(() => CacheRegion<object>(null, null)).MakeGenericMethod(typeof(T));
- public static T As<T>(this object source)
- {
- return (T) source;
+ var callExpression = Expression.Call(method, query.Expression, Expression.Constant(region));
+
+ return new NhQueryable<T>(query.Provider, callExpression);
}
- }
+ }
}
\ No newline at end of file
Modified: trunk/nhibernate/src/NHibernate/Linq/NhRelinqQueryParser.cs
===================================================================
--- trunk/nhibernate/src/NHibernate/Linq/NhRelinqQueryParser.cs 2010-03-17 17:16:04 UTC (rev 4963)
+++ trunk/nhibernate/src/NHibernate/Linq/NhRelinqQueryParser.cs 2010-03-22 15:17:58 UTC (rev 4964)
@@ -1,7 +1,10 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Remotion.Data.Linq;
+using Remotion.Data.Linq.Clauses;
+using Remotion.Data.Linq.Clauses.StreamedData;
using Remotion.Data.Linq.EagerFetching.Parsing;
using Remotion.Data.Linq.Parsing.Structure;
using Remotion.Data.Linq.Parsing.Structure.IntermediateModel;
@@ -40,6 +43,14 @@
MethodCallRegistry.Register(new[] { typeof(EagerFetchingExtensionMethods).GetMethod("ThenFetch") }, typeof(ThenFetchOneExpressionNode));
MethodCallRegistry.Register(new[] { typeof(EagerFetchingExtensionMethods).GetMethod("ThenFetchMany") }, typeof(ThenFetchManyExpressionNode));
+ MethodCallRegistry.Register(
+ new[]
+ {
+ typeof(LinqExtensionMethods).GetMethod("Cacheable"),
+ typeof(LinqExtensionMethods).GetMethod("CacheMode"),
+ typeof(LinqExtensionMethods).GetMethod("CacheRegion"),
+ }, typeof(CacheableExpressionNode));
+
}
public static QueryModel Parse(Expression expression)
@@ -47,4 +58,57 @@
return new QueryParser(new ExpressionTreeParser(MethodCallRegistry)).GetParsedQuery(expression);
}
}
+
+ public class CacheableExpressionNode : ResultOperatorExpressionNodeBase
+ {
+ private readonly MethodCallExpressionParseInfo _parseInfo;
+ private readonly ConstantExpression _data;
+
+ public CacheableExpressionNode(MethodCallExpressionParseInfo parseInfo, ConstantExpression data) : base(parseInfo, null, null)
+ {
+ _parseInfo = parseInfo;
+ _data = data;
+ }
+
+ public override Expression Resolve(ParameterExpression inputParameter, Expression expressionToBeResolved, ClauseGenerationContext clauseGenerationContext)
+ {
+ throw new NotImplementedException();
+ }
+
+ protected override ResultOperatorBase CreateResultOperator(ClauseGenerationContext clauseGenerationContext)
+ {
+ return new CacheableResultOperator(_parseInfo, _data);
+ }
+ }
+
+ public class CacheableResultOperator : ResultOperatorBase
+ {
+ public MethodCallExpressionParseInfo ParseInfo { get; private set; }
+ public ConstantExpression Data { get; private set; }
+
+ public CacheableResultOperator(MethodCallExpressionParseInfo parseInfo, ConstantExpression data)
+ {
+ ParseInfo = parseInfo;
+ Data = data;
+ }
+
+ public override IStreamedData ExecuteInMemory(IStreamedData input)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override IStreamedDataInfo GetOutputDataInfo(IStreamedDataInfo inputInfo)
+ {
+ return inputInfo;
+ }
+
+ public override ResultOperatorBase Clone(CloneContext cloneContext)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override void TransformExpressions(Func<Expression, Expression> transformation)
+ {
+ }
+ }
}
\ No newline at end of file
Added: trunk/nhibernate/src/NHibernate/Linq/TypeHelperExtensionMethods.cs
===================================================================
--- trunk/nhibernate/src/NHibernate/Linq/TypeHelperExtensionMethods.cs (rev 0)
+++ trunk/nhibernate/src/NHibernate/Linq/TypeHelperExtensionMethods.cs 2010-03-22 15:17:58 UTC (rev 4964)
@@ -0,0 +1,51 @@
+using System;
+using System.Collections.Generic;
+
+namespace NHibernate.Linq
+{
+ public static class TypeHelperExtensionMethods
+ {
+ public static void ForEach<T>(this IEnumerable<T> query, Action<T> method)
+ {
+ foreach (T item in query)
+ {
+ method(item);
+ }
+ }
+
+ public static bool IsEnumerableOfT(this System.Type type)
+ {
+ return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>);
+ }
+
+ public static bool IsNullable(this System.Type type)
+ {
+ return (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>));
+ }
+
+ public static bool IsNullableOrReference(this System.Type type)
+ {
+ return !type.IsValueType || type.IsNullable();
+ }
+
+ public static System.Type NullableOf(this System.Type type)
+ {
+ return type.GetGenericArguments()[0];
+ }
+
+ public static bool IsPrimitive(this System.Type type)
+ {
+ return (type.IsValueType || type.IsNullable() || type == typeof(string));
+ }
+
+ public static bool IsNonPrimitive(this System.Type type)
+ {
+ return !type.IsPrimitive();
+ }
+
+ public static T As<T>(this object source)
+ {
+ return (T)source;
+ }
+ }
+}
Modified: trunk/nhibernate/src/NHibernate/Linq/Visitors/QueryModelVisitor.cs
===================================================================
--- trunk/nhibernate/src/NHibernate/Linq/Visitors/QueryModelVisitor.cs 2010-03-17 17:16:04 UTC (rev 4963)
+++ trunk/nhibernate/src/NHibernate/Linq/Visitors/QueryModelVisitor.cs 2010-03-22 15:17:58 UTC (rev 4964)
@@ -62,6 +62,7 @@
static QueryModelVisitor()
{
+ // TODO - reflection to build map
ResultOperatorMap = new ResultOperatorMap();
ResultOperatorMap.Add<AggregateResultOperator, ProcessAggregate>();
@@ -77,6 +78,7 @@
ResultOperatorMap.Add<AllResultOperator, ProcessAll>();
ResultOperatorMap.Add<FetchOneRequest, ProcessFetchOne>();
ResultOperatorMap.Add<FetchManyRequest, ProcessFetchMany>();
+ ResultOperatorMap.Add<CacheableResultOperator, ProcessCacheable>();
}
private QueryModelVisitor(VisitorParameters visitorParameters, bool root, QueryModel queryModel)
Added: trunk/nhibernate/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessCacheable.cs
===================================================================
--- trunk/nhibernate/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessCacheable.cs (rev 0)
+++ trunk/nhibernate/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessCacheable.cs 2010-03-22 15:17:58 UTC (rev 4964)
@@ -0,0 +1,41 @@
+namespace NHibernate.Linq.Visitors.ResultOperatorProcessors
+{
+ public class ProcessCacheable : IResultOperatorProcessor<CacheableResultOperator>
+ {
+ public void Process(CacheableResultOperator resultOperator, QueryModelVisitor queryModelVisitor, IntermediateHqlTree tree)
+ {
+ NamedParameter parameterName;
+
+ switch (resultOperator.ParseInfo.ParsedExpression.Method.Name)
+ {
+ case "Cacheable":
+ tree.AddAdditionalCriteria((q, p) => q.SetCacheable(true));
+ break;
+ case "CacheMode":
+ queryModelVisitor.VisitorParameters.ConstantToParameterMap.TryGetValue(resultOperator.Data,
+ out parameterName);
+ if (parameterName != null)
+ {
+ tree.AddAdditionalCriteria((q, p) => q.SetCacheMode((CacheMode) p[parameterName.Name].First));
+ }
+ else
+ {
+ tree.AddAdditionalCriteria((q, p) => q.SetCacheMode((CacheMode) resultOperator.Data.Value));
+ }
+ break;
+ case "CacheRegion":
+ queryModelVisitor.VisitorParameters.ConstantToParameterMap.TryGetValue(resultOperator.Data,
+ out parameterName);
+ if (parameterName != null)
+ {
+ tree.AddAdditionalCriteria((q, p) => q.SetCacheRegion((string) p[parameterName.Name].First));
+ }
+ else
+ {
+ tree.AddAdditionalCriteria((q, p) => q.SetCacheRegion((string) resultOperator.Data.Value));
+ }
+ break;
+ }
+ }
+ }
+}
\ No newline at end of file
Modified: trunk/nhibernate/src/NHibernate/NHibernate.csproj
===================================================================
--- trunk/nhibernate/src/NHibernate/NHibernate.csproj 2010-03-17 17:16:04 UTC (rev 4963)
+++ trunk/nhibernate/src/NHibernate/NHibernate.csproj 2010-03-22 15:17:58 UTC (rev 4964)
@@ -649,6 +649,7 @@
<Compile Include="Impl\SessionIdLoggingContext.cs" />
<Compile Include="Linq\Expressions\AggregateExpressionNode.cs" />
<Compile Include="Linq\EagerFetchingExtensionMethods.cs" />
+ <Compile Include="Linq\TypeHelperExtensionMethods.cs" />
<Compile Include="Linq\Visitors\NameUnNamedParameters.cs" />
<Compile Include="Linq\NhRelinqQueryParser.cs" />
<Compile Include="Linq\ResultOperators\AggregateResultOperator.cs" />
@@ -681,6 +682,7 @@
<Compile Include="Linq\ReWriters\RemoveUnnecessaryBodyOperators.cs" />
<Compile Include="Linq\Clauses\LeftJoinClause.cs" />
<Compile Include="Linq\IntermediateHqlTree.cs" />
+ <Compile Include="Linq\Visitors\ResultOperatorProcessors\ProcessCacheable.cs" />
<Compile Include="Linq\Visitors\QuerySourceLocator.cs" />
<Compile Include="Linq\Visitors\ResultOperatorProcessors\ProcessFetch.cs" />
<Compile Include="Linq\Visitors\ResultOperatorProcessors\ProcessFetchMany.cs" />
Added: trunk/nhibernate/src/NHibernate.Test/Linq/QueryCacheableTests.cs
===================================================================
--- trunk/nhibernate/src/NHibernate.Test/Linq/QueryCacheableTests.cs (rev 0)
+++ trunk/nhibernate/src/NHibernate.Test/Linq/QueryCacheableTests.cs 2010-03-22 15:17:58 UTC (rev 4964)
@@ -0,0 +1,72 @@
+using System.Linq;
+using NHibernate.Cfg;
+using NHibernate.Linq;
+using NUnit.Framework;
+
+namespace NHibernate.Test.Linq
+{
+ [TestFixture]
+ public class QueryCacheableTests : LinqTestCase
+ {
+ protected override void Configure(Configuration cfg)
+ {
+ cfg.SetProperty(Environment.UseQueryCache, "true");
+ cfg.SetProperty(Environment.GenerateStatistics, "true");
+ base.Configure(cfg);
+ }
+
+ [Test]
+ public void QueryIsCacheable()
+ {
+ Sfi.Statistics.Clear();
+ Sfi.QueryCache.Clear();
+
+ var x = (from c in db.Customers
+ select c).Cacheable().ToList();
+
+ var x2 = (from c in db.Customers
+ select c).Cacheable().ToList();
+
+ Assert.That(Sfi.Statistics.QueryExecutionCount, Is.EqualTo(1));
+ Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(1));
+ Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(1));
+ }
+
+ [Test]
+ public void QueryIsCacheable2()
+ {
+ Sfi.Statistics.Clear();
+ Sfi.QueryCache.Clear();
+
+ var x = (from c in db.Customers
+ select c).Cacheable().ToList();
+
+ var x2 = (from c in db.Customers
+ select c).ToList();
+
+ Assert.That(Sfi.Statistics.QueryExecutionCount, Is.EqualTo(2));
+ Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(1));
+ Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(0));
+ }
+
+ [Test]
+ public void QueryIsCacheableWithRegion()
+ {
+ Sfi.Statistics.Clear();
+ Sfi.QueryCache.Clear();
+
+ var x = (from c in db.Customers
+ select c).Cacheable().CacheRegion("test").ToList();
+
+ var x2 = (from c in db.Customers
+ select c).Cacheable().CacheRegion("test").ToList();
+
+ var x3 = (from c in db.Customers
+ select c).Cacheable().CacheRegion("other").ToList();
+
+ Assert.That(Sfi.Statistics.QueryExecutionCount, Is.EqualTo(2));
+ Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(2));
+ Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(1));
+ }
+ }
+}
Modified: trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj
===================================================================
--- trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj 2010-03-17 17:16:04 UTC (rev 4963)
+++ trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj 2010-03-22 15:17:58 UTC (rev 4964)
@@ -420,6 +420,7 @@
<Compile Include="Linq\PatientTests.cs" />
<Compile Include="Linq\ProjectionsTests.cs" />
<Compile Include="Linq\PropertyMethodMappingTests.cs" />
+ <Compile Include="Linq\QueryCacheableTests.cs" />
<Compile Include="Linq\QueryReuseTests.cs" />
<Compile Include="Linq\ReadonlyTestCase.cs" />
<Compile Include="Linq\RegresstionTests.cs" />
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|