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