Skip to content

Commit 8e126ff

Browse files
OnurGumusoskarb
authored andcommitted
NH-2285: Support for LockMode in linq provider
1 parent a6bedf4 commit 8e126ff

File tree

9 files changed

+142
-5
lines changed

9 files changed

+142
-5
lines changed

src/NHibernate.Test/Linq/QueryLock.cs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
using System.Linq;
2+
using NHibernate.AdoNet;
3+
using NHibernate.Cfg;
4+
using NHibernate.Engine;
5+
using NHibernate.Linq;
6+
using NUnit.Framework;
7+
8+
namespace NHibernate.Test.Linq
9+
{
10+
public class QueryLock : LinqTestCase
11+
{
12+
13+
[Test]
14+
public void CanSetLockLinqQueries()
15+
{
16+
var result = (from e in db.Customers
17+
where e.CompanyName == "Corp"
18+
select e).SetLockMode(LockMode.Upgrade).ToList();
19+
20+
}
21+
22+
23+
[Test]
24+
public void CanSetLockOnLinqPagingQuery()
25+
{
26+
var result = (from e in db.Customers
27+
where e.CompanyName == "Corp"
28+
select e).Skip(5).Take(5).SetLockMode(LockMode.Upgrade).ToList();
29+
}
30+
31+
32+
[Test]
33+
public void CanLockBeforeSkipOnLinqOrderedPageQuery()
34+
{
35+
var result = (from e in db.Customers
36+
orderby e.CompanyName
37+
select e)
38+
.SetLockMode(LockMode.Upgrade).Skip(5).Take(5).ToList();
39+
40+
41+
}
42+
43+
44+
}
45+
46+
}
47+

src/NHibernate.Test/NHibernate.Test.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,7 @@
543543
<Compile Include="Linq\DateTimeTests.cs" />
544544
<Compile Include="Linq\ExpressionSessionLeakTest.cs" />
545545
<Compile Include="Linq\LoggingTests.cs" />
546+
<Compile Include="Linq\QueryLock.cs" />
546547
<Compile Include="Linq\QueryTimeoutTests.cs" />
547548
<Compile Include="Linq\JoinTests.cs" />
548549
<Compile Include="Linq\CustomExtensionsExample.cs" />

src/NHibernate/Linq/GroupBy/AggregatingGroupByRewriter.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ public static class AggregatingGroupByRewriter
4040
typeof (AnyResultOperator),
4141
typeof (AllResultOperator),
4242
typeof (TimeoutResultOperator),
43-
typeof (CacheableResultOperator)
43+
typeof (CacheableResultOperator),
44+
typeof (LockResultOperator),
4445
};
4546

4647
public static void ReWrite(QueryModel queryModel)
@@ -60,8 +61,8 @@ public static void ReWrite(QueryModel queryModel)
6061
{
6162
FlattenSubQuery(queryModel, subQueryExpression.QueryModel, groupBy);
6263
RemoveCostantGroupByKeys(queryModel, groupBy);
63-
}
64-
}
64+
}
65+
}
6566
}
6667
}
6768

@@ -112,7 +113,7 @@ private static void RemoveCostantGroupByKeys(QueryModel queryModel, GroupResultO
112113
{
113114
// Remove the Group By clause completely if all the keys are constant (redundant)
114115
queryModel.ResultOperators.Remove(groupBy);
115-
}
116+
}
116117
else
117118
{
118119
// Re-write the KeySelector as an object array of the non-constant keys

src/NHibernate/Linq/LinqExtensionMethods.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,16 @@ public static IQueryable<T> Cacheable<T>(this IQueryable<T> query)
4040
return new NhQueryable<T>(query.Provider, callExpression);
4141
}
4242

43+
public static IQueryable<T> SetLockMode<T>(this IQueryable<T> query, LockMode lockMode)
44+
{
45+
var method = ReflectionHelper.GetMethodDefinition(() => SetLockMode<object>(null, LockMode.Read)).MakeGenericMethod(typeof(T));
46+
47+
var callExpression = Expression.Call(method, query.Expression, Expression.Constant(lockMode));
48+
49+
return new NhQueryable<T>(query.Provider, callExpression);
50+
}
51+
52+
4353
public static IQueryable<T> CacheMode<T>(this IQueryable<T> query, CacheMode cacheMode)
4454
{
4555
var method = ReflectionHelper.GetMethodDefinition(() => CacheMode<object>(null, NHibernate.CacheMode.Normal)).MakeGenericMethod(typeof(T));

src/NHibernate/Linq/NhRelinqQueryParser.cs

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,14 @@ public NHibernateNodeTypeProvider()
7878
}, typeof (TimeoutExpressionNode)
7979
);
8080

81+
methodInfoRegistry.Register(
82+
new[]
83+
{
84+
ReflectionHelper.GetMethodDefinition(() => LinqExtensionMethods.SetLockMode<object>(null, LockMode.Read)),
85+
}, typeof(LockExpressionNode)
86+
);
87+
88+
8189
var nodeTypeProvider = ExpressionTreeParser.CreateDefaultNodeTypeProvider();
8290
nodeTypeProvider.InnerProviders.Add(methodInfoRegistry);
8391
defaultNodeTypeProvider = nodeTypeProvider;
@@ -137,6 +145,30 @@ protected override ResultOperatorBase CreateResultOperator(ClauseGenerationConte
137145
}
138146
}
139147

148+
internal class LockExpressionNode : ResultOperatorExpressionNodeBase
149+
{
150+
private readonly MethodCallExpressionParseInfo _parseInfo;
151+
private readonly ConstantExpression _lockMode;
152+
153+
public LockExpressionNode(MethodCallExpressionParseInfo parseInfo, ConstantExpression lockMode)
154+
: base(parseInfo, null, null)
155+
{
156+
_parseInfo = parseInfo;
157+
_lockMode = lockMode;
158+
}
159+
160+
public override Expression Resolve(ParameterExpression inputParameter, Expression expressionToBeResolved, ClauseGenerationContext clauseGenerationContext)
161+
{
162+
return Source.Resolve(inputParameter, expressionToBeResolved, clauseGenerationContext);
163+
}
164+
165+
protected override ResultOperatorBase CreateResultOperator(ClauseGenerationContext clauseGenerationContext)
166+
{
167+
return new LockResultOperator(_parseInfo, _lockMode);
168+
}
169+
}
170+
171+
140172
public class CacheableResultOperator : ResultOperatorBase
141173
{
142174
public MethodCallExpressionParseInfo ParseInfo { get; private set; }
@@ -222,4 +254,35 @@ public override void TransformExpressions(Func<Expression, Expression> transform
222254
{
223255
}
224256
}
225-
}
257+
internal class LockResultOperator : ResultOperatorBase
258+
{
259+
public MethodCallExpressionParseInfo ParseInfo { get; private set; }
260+
public ConstantExpression LockMode { get; private set; }
261+
262+
public LockResultOperator(MethodCallExpressionParseInfo parseInfo, ConstantExpression lockMode)
263+
{
264+
ParseInfo = parseInfo;
265+
LockMode = lockMode;
266+
}
267+
268+
public override IStreamedData ExecuteInMemory(IStreamedData input)
269+
{
270+
throw new NotImplementedException();
271+
}
272+
273+
public override IStreamedDataInfo GetOutputDataInfo(IStreamedDataInfo inputInfo)
274+
{
275+
return inputInfo;
276+
}
277+
278+
public override ResultOperatorBase Clone(CloneContext cloneContext)
279+
{
280+
throw new NotImplementedException();
281+
}
282+
283+
public override void TransformExpressions(Func<Expression, Expression> transformation)
284+
{
285+
}
286+
}
287+
288+
}

src/NHibernate/Linq/ReWriters/ResultOperatorRewriter.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ private class ResultOperatorExpressionRewriter : ExpressionTreeVisitor
6767
typeof(OfTypeResultOperator),
6868
typeof(CacheableResultOperator),
6969
typeof(TimeoutResultOperator),
70+
typeof (LockResultOperator),
7071
typeof(CastResultOperator), // see ProcessCast class
7172
};
7273

src/NHibernate/Linq/Visitors/QueryModelVisitor.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ static QueryModelVisitor()
124124
ResultOperatorMap.Add<FetchManyRequest, ProcessFetchMany>();
125125
ResultOperatorMap.Add<CacheableResultOperator, ProcessCacheable>();
126126
ResultOperatorMap.Add<TimeoutResultOperator, ProcessTimeout>();
127+
ResultOperatorMap.Add<LockResultOperator, ProcessLock>();
127128
ResultOperatorMap.Add<OfTypeResultOperator, ProcessOfType>();
128129
ResultOperatorMap.Add<CastResultOperator, ProcessCast>();
129130
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+

2+
namespace NHibernate.Linq.Visitors.ResultOperatorProcessors
3+
{
4+
internal class ProcessLock : IResultOperatorProcessor<LockResultOperator>
5+
{
6+
public void Process(LockResultOperator resultOperator, QueryModelVisitor queryModelVisitor, IntermediateHqlTree tree)
7+
{
8+
tree.AddAdditionalCriteria((q, p) => q.SetLockMode(queryModelVisitor.Model.MainFromClause.ItemName, (LockMode)resultOperator.LockMode.Value));
9+
}
10+
}
11+
}
12+

src/NHibernate/NHibernate.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,7 @@
331331
<Compile Include="Linq\Visitors\QueryExpressionSourceIdentifer.cs" />
332332
<Compile Include="Linq\Visitors\QuerySourceIdentifier.cs" />
333333
<Compile Include="Linq\Visitors\MemberExpressionJoinDetector.cs" />
334+
<Compile Include="Linq\Visitors\ResultOperatorProcessors\ProcessLock.cs" />
334335
<Compile Include="Linq\Visitors\ResultOperatorProcessors\ProcessTimeout.cs" />
335336
<Compile Include="Linq\Visitors\ResultOperatorProcessors\ProcessAggregateFromSeed.cs" />
336337
<Compile Include="Linq\NestedSelects\NestedSelectRewriter.cs" />

0 commit comments

Comments
 (0)