From: <aye...@us...> - 2010-10-07 16:30:51
|
Revision: 5237 http://nhibernate.svn.sourceforge.net/nhibernate/?rev=5237&view=rev Author: ayenderahien Date: 2010-10-07 16:30:45 +0000 (Thu, 07 Oct 2010) Log Message: ----------- NH-2309 - Futures queries with Linq Modified Paths: -------------- trunk/nhibernate/src/NHibernate/Impl/DelayedEnumerator.cs trunk/nhibernate/src/NHibernate/Impl/FutureValue.cs trunk/nhibernate/src/NHibernate/Linq/LinqExtensionMethods.cs trunk/nhibernate/src/NHibernate/Linq/NhQueryProvider.cs trunk/nhibernate/src/NHibernate/Linq/Visitors/QueryModelVisitor.cs trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/Futures/Mappings.hbm.xml trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/Futures/Person.cs trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj Added Paths: ----------- trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/Futures/LinqFutureFixture.cs Modified: trunk/nhibernate/src/NHibernate/Impl/DelayedEnumerator.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Impl/DelayedEnumerator.cs 2010-10-07 13:48:36 UTC (rev 5236) +++ trunk/nhibernate/src/NHibernate/Impl/DelayedEnumerator.cs 2010-10-07 16:30:45 UTC (rev 5237) @@ -1,3 +1,4 @@ +using System; using System.Collections; using System.Collections.Generic; @@ -3,10 +4,14 @@ namespace NHibernate.Impl { - internal class DelayedEnumerator<T> : IEnumerable<T> + internal class DelayedEnumerator<T> : IEnumerable<T>, IDelayedValue { public delegate IEnumerable<T> GetResult(); private readonly GetResult result; + + public Delegate ExecuteOnEval { get; set;} + + public DelayedEnumerator(GetResult result) { @@ -18,7 +23,10 @@ { get { - foreach (T item in result()) + var value = result(); + if(ExecuteOnEval != null) + value = (IEnumerable<T>)ExecuteOnEval.DynamicInvoke(value); + foreach (T item in value) { yield return item; } @@ -39,4 +47,9 @@ #endregion } + + internal interface IDelayedValue + { + Delegate ExecuteOnEval { get; set; } + } } \ No newline at end of file Modified: trunk/nhibernate/src/NHibernate/Impl/FutureValue.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Impl/FutureValue.cs 2010-10-07 13:48:36 UTC (rev 5236) +++ trunk/nhibernate/src/NHibernate/Impl/FutureValue.cs 2010-10-07 16:30:45 UTC (rev 5237) @@ -1,3 +1,4 @@ +using System; using System.Collections; using System.Collections.Generic; @@ -3,5 +4,5 @@ namespace NHibernate.Impl { - internal class FutureValue<T> : IFutureValue<T> + internal class FutureValue<T> : IFutureValue<T>, IDelayedValue { public delegate IEnumerable<T> GetResult(); @@ -23,11 +24,24 @@ if (!enumerator.MoveNext()) { - return default(T); - } + var defVal = default(T); + if (ExecuteOnEval != null) + defVal = (T)ExecuteOnEval.DynamicInvoke(defVal); + return defVal; + } - return enumerator.Current; + var val = enumerator.Current; + + if (ExecuteOnEval != null) + val = (T)ExecuteOnEval.DynamicInvoke(val); + + return val; } } + + public Delegate ExecuteOnEval + { + get; set; + } } } \ No newline at end of file Modified: trunk/nhibernate/src/NHibernate/Linq/LinqExtensionMethods.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Linq/LinqExtensionMethods.cs 2010-10-07 13:48:36 UTC (rev 5236) +++ trunk/nhibernate/src/NHibernate/Linq/LinqExtensionMethods.cs 2010-10-07 16:30:45 UTC (rev 5237) @@ -1,5 +1,8 @@ +using System; +using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; +using NHibernate.Impl; namespace NHibernate.Linq { @@ -36,5 +39,30 @@ return new NhQueryable<T>(query.Provider, callExpression); } + + public static IEnumerable<T> ToFuture<T>(this IEnumerable<T> query) + { + var nhQueryable = query as NhQueryable<T>; + if (nhQueryable == null) + throw new NotSupportedException("You can also use the AsFuture() method on NhQueryable"); + + + var future = ((NhQueryProvider)nhQueryable.Provider).ExecuteFuture(nhQueryable.Expression); + return (IEnumerable<T>)future; + } + + public static IFutureValue<T> ToFutureValue<T>(this IEnumerable<T> query) + { + var nhQueryable = query as NhQueryable<T>; + if (nhQueryable == null) + throw new NotSupportedException("You can also use the AsFuture() method on NhQueryable"); + + var future = ((NhQueryProvider)nhQueryable.Provider).ExecuteFuture(nhQueryable.Expression); + if(future is DelayedEnumerator<T>) + { + return new FutureValue<T>(() => ((IEnumerable<T>) future)); + } + return (IFutureValue<T>)future; + } } } \ No newline at end of file Modified: trunk/nhibernate/src/NHibernate/Linq/NhQueryProvider.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Linq/NhQueryProvider.cs 2010-10-07 13:48:36 UTC (rev 5236) +++ trunk/nhibernate/src/NHibernate/Linq/NhQueryProvider.cs 2010-10-07 16:30:45 UTC (rev 5237) @@ -1,3 +1,4 @@ +using System; using System.Collections; using System.Collections.Generic; using System.Linq; @@ -19,38 +20,84 @@ public object Execute(Expression expression) { - var nhLinqExpression = new NhLinqExpression(expression); + IQuery query; + NhLinqExpression nhQuery; + NhLinqExpression nhLinqExpression = PrepareQuery(expression, out query, out nhQuery); + + return ExecuteQuery(nhLinqExpression, query, nhQuery); + } - var query = _session.CreateQuery(nhLinqExpression); + public object ExecuteFuture(Expression expression) + { + IQuery query; + NhLinqExpression nhQuery; + NhLinqExpression nhLinqExpression = PrepareQuery(expression, out query, out nhQuery); + return ExecuteFutureQuery(nhLinqExpression, query, nhQuery); + } - var nhQuery = query.As<ExpressionQueryImpl>().QueryExpression.As<NhLinqExpression>(); + private NhLinqExpression PrepareQuery(Expression expression, out IQuery query, out NhLinqExpression nhQuery) + { + var nhLinqExpression = new NhLinqExpression(expression); - SetParameters(query, nhLinqExpression.ParameterValuesByName); - SetResultTransformerAndAdditionalCriteria(query, nhQuery, nhLinqExpression.ParameterValuesByName); + query = _session.CreateQuery(nhLinqExpression); - var results = query.List(); + nhQuery = query.As<ExpressionQueryImpl>().QueryExpression.As<NhLinqExpression>(); - if (nhQuery.ExpressionToHqlTranslationResults.PostExecuteTransformer != null) + SetParameters(query, nhLinqExpression.ParameterValuesByName); + SetResultTransformerAndAdditionalCriteria(query, nhQuery, nhLinqExpression.ParameterValuesByName); + return nhLinqExpression; + } + + private object ExecuteFutureQuery(NhLinqExpression nhLinqExpression, IQuery query, NhLinqExpression nhQuery) + { + MethodInfo method; + if (nhLinqExpression.ReturnType == NhLinqExpressionReturnType.Sequence) + { + method = typeof (IQuery).GetMethod("Future").MakeGenericMethod(nhQuery.Type); + } + else { - try - { - return nhQuery.ExpressionToHqlTranslationResults.PostExecuteTransformer.DynamicInvoke(results.AsQueryable()); - } - catch (TargetInvocationException e) - { - throw e.InnerException; - } + method = typeof(IQuery).GetMethod("FutureValue").MakeGenericMethod(nhQuery.Type); } - if (nhLinqExpression.ReturnType == NhLinqExpressionReturnType.Sequence) - { - return results.AsQueryable(); - } + var result = method.Invoke(query, new object[0]); - return results[0]; - } - public TResult Execute<TResult>(Expression expression) + + if (nhQuery.ExpressionToHqlTranslationResults.PostExecuteTransformer != null) + { + ((IDelayedValue) result).ExecuteOnEval = nhQuery.ExpressionToHqlTranslationResults.PostExecuteTransformer; + } + + return result; + + } + + private object ExecuteQuery(NhLinqExpression nhLinqExpression, IQuery query, NhLinqExpression nhQuery) + { + var results = query.List(); + + if (nhQuery.ExpressionToHqlTranslationResults.PostExecuteTransformer != null) + { + try + { + return nhQuery.ExpressionToHqlTranslationResults.PostExecuteTransformer.DynamicInvoke(results.AsQueryable()); + } + catch (TargetInvocationException e) + { + throw e.InnerException; + } + } + + if (nhLinqExpression.ReturnType == NhLinqExpressionReturnType.Sequence) + { + return results.AsQueryable(); + } + + return results[0]; + } + + public TResult Execute<TResult>(Expression expression) { return (TResult) Execute(expression); } Modified: trunk/nhibernate/src/NHibernate/Linq/Visitors/QueryModelVisitor.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Linq/Visitors/QueryModelVisitor.cs 2010-10-07 13:48:36 UTC (rev 5236) +++ trunk/nhibernate/src/NHibernate/Linq/Visitors/QueryModelVisitor.cs 2010-10-07 16:30:45 UTC (rev 5237) @@ -80,8 +80,8 @@ ResultOperatorMap.Add<FetchManyRequest, ProcessFetchMany>(); ResultOperatorMap.Add<CacheableResultOperator, ProcessCacheable>(); ResultOperatorMap.Add<OfTypeResultOperator, ProcessOfType>(); - ResultOperatorMap.Add<CastResultOperator, ProcessCast>(); - } + ResultOperatorMap.Add<CastResultOperator, ProcessCast>(); + } private QueryModelVisitor(VisitorParameters visitorParameters, bool root, QueryModel queryModel) { Added: trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/Futures/LinqFutureFixture.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/Futures/LinqFutureFixture.cs (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/Futures/LinqFutureFixture.cs 2010-10-07 16:30:45 UTC (rev 5237) @@ -0,0 +1,164 @@ +using NHibernate.Impl; +using NUnit.Framework; +using NHibernate.Linq; +using System.Linq; + +namespace NHibernate.Test.NHSpecificTest.Futures +{ + using System.Collections; + + [TestFixture] + public class LinqFutureFixture : FutureFixture + { + + [Test] + public void CoalesceShouldWorkForFutures() + { + int personId; + using (ISession s = OpenSession()) + using (ITransaction tx = s.BeginTransaction()) + { + var p1 = new Person { Name = "inserted name" }; + var p2 = new Person { Name = null }; + + s.Save(p1); + s.Save(p2); + personId = p2.Id; + tx.Commit(); + } + + using (ISession s = OpenSession()) + using (s.BeginTransaction()) + { + var person = s.Query<Person>().Where(p => (p.Name ?? "e") == "e").ToFutureValue(); + Assert.AreEqual(personId, person.Value.Id); + } + + using (ISession s = OpenSession()) + using (ITransaction tx = s.BeginTransaction()) + { + s.Delete("from Person"); + tx.Commit(); + } + } + [Test] + public void CanUseFutureQuery() + { + using (var s = sessions.OpenSession()) + { + IgnoreThisTestIfMultipleQueriesArentSupportedByDriver(); + + var persons10 = s.Query<Person>() + .Take(10) + .ToFuture(); + var persons5 = s.Query<Person>() + .Take(5) + .ToFuture(); + + using (var logSpy = new SqlLogSpy()) + { + foreach (var person in persons5) + { + + } + + foreach (var person in persons10) + { + + } + + var events = logSpy.Appender.GetEvents(); + Assert.AreEqual(1, events.Length); + } + } + } + + [Test] + public void TwoFuturesRunInTwoRoundTrips() + { + using (var s = sessions.OpenSession()) + { + IgnoreThisTestIfMultipleQueriesArentSupportedByDriver(); + + using (var logSpy = new SqlLogSpy()) + { + var persons10 = s.Query<Person>() + .Take(10) + .ToFuture(); + + foreach (var person in persons10) { } // fire first future round-trip + + var persons5 = s.Query<Person>() + .Take(5) + .ToFuture(); + + foreach (var person in persons5) { } // fire second future round-trip + + var events = logSpy.Appender.GetEvents(); + Assert.AreEqual(2, events.Length); + } + } + } + + [Test] + public void CanCombineSingleFutureValueWithEnumerableFutures() + { + using (var s = sessions.OpenSession()) + { + IgnoreThisTestIfMultipleQueriesArentSupportedByDriver(); + + var persons = s.Query<Person>() + .Take(10) + .ToFuture(); + + var personCount = s.Query<Person>() + .Select(x=>x.Id) + .ToFutureValue(); + + using (var logSpy = new SqlLogSpy()) + { + long count = personCount.Value; + + foreach (var person in persons) + { + } + + var events = logSpy.Appender.GetEvents(); + Assert.AreEqual(1, events.Length); + } + } + } + + [Test] + public void CanExecuteMultipleQueriesOnSameExpression() + { + using (var s = sessions.OpenSession()) + { + IgnoreThisTestIfMultipleQueriesArentSupportedByDriver(); + + var meContainer = s.Query<Person>() + .Where(x=>x.Id == 1) + .ToFutureValue(); + + var possiblefriends = s.Query<Person>() + .Where(x => x.Id != 2) + .ToFuture(); + + using (var logSpy = new SqlLogSpy()) + { + var me = meContainer.Value; + + foreach (var person in possiblefriends) + { + } + + var events = logSpy.Appender.GetEvents(); + Assert.AreEqual(1, events.Length); + var wholeLog = logSpy.GetWholeLog(); + Assert.True(wholeLog.Contains("@p0 = 1 [Type: Int32 (0)], @p1 = 2 [Type: Int32 (0)]")); + } + } + + } + } +} Modified: trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/Futures/Mappings.hbm.xml =================================================================== --- trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/Futures/Mappings.hbm.xml 2010-10-07 13:48:36 UTC (rev 5236) +++ trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/Futures/Mappings.hbm.xml 2010-10-07 16:30:45 UTC (rev 5237) @@ -7,6 +7,7 @@ <id name="Id"> <generator class="native"/> </id> + <property name="Name"/> <many-to-one name="Parent" /> <list name="Children" cascade="all"> <key column="parent_id" /> Modified: trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/Futures/Person.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/Futures/Person.cs 2010-10-07 13:48:36 UTC (rev 5236) +++ trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/Futures/Person.cs 2010-10-07 16:30:45 UTC (rev 5237) @@ -9,6 +9,8 @@ private int id; private Person parent; + public virtual string Name { get; set; } + public virtual Person Parent { get { return parent; } Modified: trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj =================================================================== --- trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj 2010-10-07 13:48:36 UTC (rev 5236) +++ trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj 2010-10-07 16:30:45 UTC (rev 5237) @@ -456,6 +456,7 @@ <Compile Include="Logging\LoggerProviderTest.cs" /> <Compile Include="NHSpecificTest\EntityNameAndCompositeId\Fixture.cs" /> <Compile Include="NHSpecificTest\EntityNameWithFullName\Fixture.cs" /> + <Compile Include="NHSpecificTest\Futures\LinqFutureFixture.cs" /> <Compile Include="NHSpecificTest\NH1136\Address.cs" /> <Compile Include="NHSpecificTest\NH1136\FeeMatrixType.cs" /> <Compile Include="NHSpecificTest\NH1136\Fixture.cs" /> This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |