Skip to content

Commit ab6d623

Browse files
committed
Trying to correctly resolve referenced query source for lock
1 parent fd05c2e commit ab6d623

File tree

5 files changed

+111
-17
lines changed

5 files changed

+111
-17
lines changed

src/NHibernate.Test/Linq/QueryLock.cs

Lines changed: 87 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Linq;
1+
using System;
2+
using System.Linq;
23
using System.Transactions;
34
using NHibernate.Dialect;
45
using NHibernate.DomainModel.Northwind.Entities;
@@ -12,7 +13,7 @@ namespace NHibernate.Test.Linq
1213
public class QueryLock : LinqTestCase
1314
{
1415
[Test]
15-
public void CanSetLockLinqQueries()
16+
public void CanSetLockLinqQueriesOuter()
1617
{
1718
using (session.BeginTransaction())
1819
{
@@ -26,21 +27,21 @@ public void CanSetLockLinqQueries()
2627
}
2728

2829
[Test]
29-
public void CanSetLockOnSubquery()
30+
public void CanSetLockLinqQueries()
3031
{
3132
using (session.BeginTransaction())
3233
{
33-
var result = (from c in db.Customers
34-
from o in c.Orders.DefaultIfEmpty()
35-
select o).ToList();
34+
var result = (from e in db.Customers.WithLock(LockMode.Upgrade)
35+
select e).ToList();
3636

3737
Assert.That(result, Has.Count.EqualTo(91));
3838
Assert.That(session.GetCurrentLockMode(result[0]), Is.EqualTo(LockMode.Upgrade));
39+
AssertSeparateTransactionIsLockedOut(result[0].CustomerId);
3940
}
4041
}
4142

4243
[Test]
43-
public void CanSetLockOnSubqueryHql()
44+
public void CanSetLockOnJoinHql()
4445
{
4546
using (session.BeginTransaction())
4647
{
@@ -51,6 +52,79 @@ public void CanSetLockOnSubqueryHql()
5152
}
5253
}
5354

55+
[Test]
56+
public void CanSetLockOnJoin()
57+
{
58+
using (session.BeginTransaction())
59+
{
60+
var result = (from c in db.Customers
61+
from o in c.Orders.WithLock(LockMode.Upgrade)
62+
select o).ToList();
63+
64+
Assert.That(result, Has.Count.EqualTo(830));
65+
Assert.That(session.GetCurrentLockMode(result[0]), Is.EqualTo(LockMode.Upgrade));
66+
}
67+
}
68+
69+
[Test]
70+
public void CanSetLockOnJoinOuter()
71+
{
72+
using (session.BeginTransaction())
73+
{
74+
var result = (from c in db.Customers
75+
from o in c.Orders
76+
select o).WithLock(LockMode.Upgrade).ToList();
77+
78+
Assert.That(result, Has.Count.EqualTo(830));
79+
Assert.That(session.GetCurrentLockMode(result[0]), Is.EqualTo(LockMode.Upgrade));
80+
}
81+
}
82+
83+
[Test]
84+
public void CanSetLockOnJoinOuterNotSupported()
85+
{
86+
using (session.BeginTransaction())
87+
{
88+
var query = (
89+
from c in db.Customers
90+
from o in c.Orders
91+
select new {o, c}
92+
).WithLock(LockMode.Upgrade);
93+
94+
Assert.Throws<NotSupportedException>(() => query.ToList());
95+
}
96+
}
97+
98+
[Test]
99+
public void CanSetLockOnJoinOuter2Hql()
100+
{
101+
using (session.BeginTransaction())
102+
{
103+
session
104+
.CreateQuery("select o, c from Customer c join c.Orders o")
105+
.SetLockMode("o", LockMode.Upgrade)
106+
.SetLockMode("c", LockMode.Upgrade)
107+
.List();
108+
}
109+
}
110+
111+
[Test]
112+
public void CanSetLockOnBothJoinAndMain()
113+
{
114+
using (session.BeginTransaction())
115+
{
116+
var result = (
117+
from c in db.Customers.WithLock(LockMode.Upgrade)
118+
from o in c.Orders.WithLock(LockMode.Upgrade)
119+
select new {o, c}
120+
).ToList();
121+
122+
Assert.That(result, Has.Count.EqualTo(830));
123+
Assert.That(session.GetCurrentLockMode(result[0].o), Is.EqualTo(LockMode.Upgrade));
124+
Assert.That(session.GetCurrentLockMode(result[0].c), Is.EqualTo(LockMode.Upgrade));
125+
}
126+
}
127+
54128
[Test]
55129
public void CanSetLockOnLinqPagingQuery()
56130
{
@@ -61,7 +135,9 @@ public void CanSetLockOnLinqPagingQuery()
61135

62136
Assert.That(result, Has.Count.EqualTo(5));
63137
Assert.That(session.GetCurrentLockMode(result[0]), Is.EqualTo(LockMode.Upgrade));
64-
AssertSeparateTransactionIsLockedOut(result[0].CustomerId);
138+
139+
if (!(Dialect is MsSql2000Dialect) && !(Dialect is MsSqlCeDialect))
140+
AssertSeparateTransactionIsLockedOut(result[0].CustomerId);
65141
}
66142
}
67143

@@ -77,7 +153,9 @@ orderby e.CompanyName
77153

78154
Assert.That(result, Has.Count.EqualTo(5));
79155
Assert.That(session.GetCurrentLockMode(result[0]), Is.EqualTo(LockMode.Upgrade));
80-
AssertSeparateTransactionIsLockedOut(result[0].CustomerId);
156+
157+
if (!(Dialect is MsSql2000Dialect) && !(Dialect is MsSqlCeDialect))
158+
AssertSeparateTransactionIsLockedOut(result[0].CustomerId);
81159
}
82160
}
83161

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,23 @@
11
using System;
22
using System.Linq.Expressions;
33
using Remotion.Linq.Clauses;
4+
using Remotion.Linq.Clauses.Expressions;
45
using Remotion.Linq.Parsing.Structure.IntermediateModel;
56

67
namespace NHibernate.Linq
78
{
89
internal class LockExpressionNode : ResultOperatorExpressionNodeBase
910
{
10-
private readonly MethodCallExpressionParseInfo _parseInfo;
11+
private static readonly ParameterExpression Parameter = Expression.Parameter(typeof(object));
12+
1113
private readonly ConstantExpression _lockMode;
14+
private readonly ResolvedExpressionCache<Expression> _cache;
1215

1316
public LockExpressionNode(MethodCallExpressionParseInfo parseInfo, ConstantExpression lockMode)
1417
: base(parseInfo, null, null)
1518
{
16-
_parseInfo = parseInfo;
1719
_lockMode = lockMode;
20+
_cache = new ResolvedExpressionCache<Expression>(this);
1821
}
1922

2023
public override Expression Resolve(ParameterExpression inputParameter, Expression expressionToBeResolved, ClauseGenerationContext clauseGenerationContext)
@@ -24,7 +27,15 @@ public override Expression Resolve(ParameterExpression inputParameter, Expressio
2427

2528
protected override ResultOperatorBase CreateResultOperator(ClauseGenerationContext clauseGenerationContext)
2629
{
27-
return new LockResultOperator(_parseInfo, _lockMode);
30+
//Resolve identity expression (_=>_). Normally this would be resolved into QuerySourceReferenceExpression.
31+
32+
var expression = _cache.GetOrCreate(
33+
r => r.GetResolvedExpression(Parameter, Parameter, clauseGenerationContext));
34+
35+
if (!(expression is QuerySourceReferenceExpression qsrExpression))
36+
throw new NotSupportedException($"WithLock is not supported on {expression}");
37+
38+
return new LockResultOperator(qsrExpression, _lockMode);
2839
}
2940
}
3041
}

src/NHibernate/Linq/LockResultOperator.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
11
using System;
22
using System.Linq.Expressions;
33
using Remotion.Linq.Clauses;
4+
using Remotion.Linq.Clauses.Expressions;
45
using Remotion.Linq.Clauses.StreamedData;
56
using Remotion.Linq.Parsing.Structure.IntermediateModel;
67

78
namespace NHibernate.Linq
89
{
910
internal class LockResultOperator : ResultOperatorBase
1011
{
11-
public MethodCallExpressionParseInfo ParseInfo { get; }
12+
private readonly QuerySourceReferenceExpression _qsrExpression;
13+
14+
public string Alias => _qsrExpression.ReferencedQuerySource.ItemName;
15+
1216
public ConstantExpression LockMode { get; }
1317

14-
public LockResultOperator(MethodCallExpressionParseInfo parseInfo, ConstantExpression lockMode)
18+
public LockResultOperator(QuerySourceReferenceExpression qsrExpression, ConstantExpression lockMode)
1519
{
16-
ParseInfo = parseInfo;
20+
_qsrExpression = qsrExpression;
1721
LockMode = lockMode;
1822
}
1923

src/NHibernate/Linq/ReWriters/QueryReferenceExpressionFlattener.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ public class QueryReferenceExpressionFlattener : RelinqExpressionVisitor
1616
internal static readonly System.Type[] FlattenableResultOperators =
1717
{
1818
typeof(FetchOneRequest),
19-
typeof(FetchManyRequest)
19+
typeof(FetchManyRequest),
20+
typeof(LockResultOperator)
2021
};
2122

2223
private QueryReferenceExpressionFlattener(QueryModel model)

src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessLock.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ internal class ProcessLock : IResultOperatorProcessor<LockResultOperator>
44
{
55
public void Process(LockResultOperator resultOperator, QueryModelVisitor queryModelVisitor, IntermediateHqlTree tree)
66
{
7-
tree.AddAdditionalCriteria((q, p) => q.SetLockMode(queryModelVisitor.Model.MainFromClause.ItemName, (LockMode)resultOperator.LockMode.Value));
7+
tree.AddAdditionalCriteria((q, p) => q.SetLockMode(resultOperator.Alias, (LockMode) resultOperator.LockMode.Value));
88
}
99
}
1010
}

0 commit comments

Comments
 (0)