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