Skip to content

Commit 6dea781

Browse files
committed
Correctly resolve referenced query source for the lock
- Rename SetLockMode to WithLock - Add overload to support locking on collections in the query - Restrict tests only to dialects that do support locks - Ignore paging tests if dialect does not support combining locks with paging
1 parent e1ff88d commit 6dea781

16 files changed

+515
-27
lines changed
Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
//------------------------------------------------------------------------------
2+
// <auto-generated>
3+
// This code was generated by AsyncGenerator.
4+
//
5+
// Changes to this file may cause incorrect behavior and will be lost if
6+
// the code is regenerated.
7+
// </auto-generated>
8+
//------------------------------------------------------------------------------
9+
10+
11+
using System;
12+
using System.Linq;
13+
using System.Transactions;
14+
using NHibernate.Dialect;
15+
using NHibernate.DomainModel.Northwind.Entities;
16+
using NHibernate.Driver;
17+
using NHibernate.Engine;
18+
using NHibernate.Exceptions;
19+
using NHibernate.Linq;
20+
using NUnit.Framework;
21+
22+
23+
namespace NHibernate.Test.Linq
24+
{
25+
using System.Threading.Tasks;
26+
using System.Threading;
27+
[TestFixture]
28+
public class QueryLockAsync : LinqTestCase
29+
{
30+
protected override bool AppliesTo(Dialect.Dialect dialect)
31+
{
32+
return TestDialect.SupportsSelectForUpdate;
33+
}
34+
35+
protected override bool AppliesTo(ISessionFactoryImplementor factory)
36+
{
37+
return !(factory.ConnectionProvider.Driver is OdbcDriver);
38+
}
39+
40+
[Test]
41+
public async Task CanSetLockLinqQueriesOuterAsync()
42+
{
43+
using (session.BeginTransaction())
44+
{
45+
var result = await ((from e in db.Customers
46+
select e).WithLock(LockMode.Upgrade).ToListAsync());
47+
48+
Assert.That(result, Has.Count.EqualTo(91));
49+
Assert.That(session.GetCurrentLockMode(result[0]), Is.EqualTo(LockMode.Upgrade));
50+
await (AssertSeparateTransactionIsLockedOutAsync(result[0].CustomerId));
51+
}
52+
}
53+
54+
[Test]
55+
public async Task CanSetLockLinqQueriesAsync()
56+
{
57+
using (session.BeginTransaction())
58+
{
59+
var result = await ((from e in db.Customers.WithLock(LockMode.Upgrade)
60+
select e).ToListAsync());
61+
62+
Assert.That(result, Has.Count.EqualTo(91));
63+
Assert.That(session.GetCurrentLockMode(result[0]), Is.EqualTo(LockMode.Upgrade));
64+
await (AssertSeparateTransactionIsLockedOutAsync(result[0].CustomerId));
65+
}
66+
}
67+
68+
[Test]
69+
public async Task CanSetLockOnJoinHqlAsync()
70+
{
71+
using (session.BeginTransaction())
72+
{
73+
await (session
74+
.CreateQuery("select o from Customer c join c.Orders o")
75+
.SetLockMode("o", LockMode.Upgrade)
76+
.ListAsync());
77+
}
78+
}
79+
80+
[Test]
81+
public async Task CanSetLockOnJoinAsync()
82+
{
83+
using (session.BeginTransaction())
84+
{
85+
var result = await ((from c in db.Customers
86+
from o in c.Orders.WithLock(LockMode.Upgrade)
87+
select o).ToListAsync());
88+
89+
Assert.That(result, Has.Count.EqualTo(830));
90+
Assert.That(session.GetCurrentLockMode(result[0]), Is.EqualTo(LockMode.Upgrade));
91+
}
92+
}
93+
94+
[Test]
95+
public async Task CanSetLockOnJoinOuterAsync()
96+
{
97+
using (session.BeginTransaction())
98+
{
99+
var result = await ((from c in db.Customers
100+
from o in c.Orders
101+
select o).WithLock(LockMode.Upgrade).ToListAsync());
102+
103+
Assert.That(result, Has.Count.EqualTo(830));
104+
Assert.That(session.GetCurrentLockMode(result[0]), Is.EqualTo(LockMode.Upgrade));
105+
}
106+
}
107+
108+
[Test]
109+
public void CanSetLockOnJoinOuterNotSupportedAsync()
110+
{
111+
using (session.BeginTransaction())
112+
{
113+
var query = (
114+
from c in db.Customers
115+
from o in c.Orders
116+
select new {o, c}
117+
).WithLock(LockMode.Upgrade);
118+
119+
Assert.ThrowsAsync<NotSupportedException>(() => query.ToListAsync());
120+
}
121+
}
122+
123+
[Test]
124+
public async Task CanSetLockOnJoinOuter2HqlAsync()
125+
{
126+
using (session.BeginTransaction())
127+
{
128+
await (session
129+
.CreateQuery("select o, c from Customer c join c.Orders o")
130+
.SetLockMode("o", LockMode.Upgrade)
131+
.SetLockMode("c", LockMode.Upgrade)
132+
.ListAsync());
133+
}
134+
}
135+
136+
[Test]
137+
public async Task CanSetLockOnBothJoinAndMainAsync()
138+
{
139+
using (session.BeginTransaction())
140+
{
141+
var result = await ((
142+
from c in db.Customers.WithLock(LockMode.Upgrade)
143+
from o in c.Orders.WithLock(LockMode.Upgrade)
144+
select new {o, c}
145+
).ToListAsync());
146+
147+
Assert.That(result, Has.Count.EqualTo(830));
148+
Assert.That(session.GetCurrentLockMode(result[0].o), Is.EqualTo(LockMode.Upgrade));
149+
Assert.That(session.GetCurrentLockMode(result[0].c), Is.EqualTo(LockMode.Upgrade));
150+
}
151+
}
152+
153+
[Test]
154+
public async Task CanSetLockOnBothJoinAndMainComplexAsync()
155+
{
156+
using (session.BeginTransaction())
157+
{
158+
var result = await ((
159+
from c in db.Customers.Where(x => true).WithLock(LockMode.Upgrade)
160+
from o in c.Orders.Where(x => true).WithLock(LockMode.Upgrade)
161+
select new {o, c}
162+
).ToListAsync());
163+
164+
Assert.That(result, Has.Count.EqualTo(830));
165+
Assert.That(session.GetCurrentLockMode(result[0].o), Is.EqualTo(LockMode.Upgrade));
166+
Assert.That(session.GetCurrentLockMode(result[0].c), Is.EqualTo(LockMode.Upgrade));
167+
}
168+
}
169+
170+
[Test]
171+
public async Task CanSetLockOnLinqPagingQueryAsync()
172+
{
173+
Assume.That(TestDialect.SupportsSelectForUpdateWithPaging, Is.True, "Dialect does not support locking in subqueries");
174+
using (session.BeginTransaction())
175+
{
176+
var result = await ((from e in db.Customers
177+
select e).Skip(5).Take(5).WithLock(LockMode.Upgrade).ToListAsync());
178+
179+
Assert.That(result, Has.Count.EqualTo(5));
180+
Assert.That(session.GetCurrentLockMode(result[0]), Is.EqualTo(LockMode.Upgrade));
181+
182+
await (AssertSeparateTransactionIsLockedOutAsync(result[0].CustomerId));
183+
}
184+
}
185+
186+
[Test]
187+
public async Task CanLockBeforeSkipOnLinqOrderedPageQueryAsync()
188+
{
189+
Assume.That(TestDialect.SupportsSelectForUpdateWithPaging, Is.True, "Dialect does not support locking in subqueries");
190+
using (session.BeginTransaction())
191+
{
192+
var result = await ((from e in db.Customers
193+
orderby e.CompanyName
194+
select e)
195+
.WithLock(LockMode.Upgrade).Skip(5).Take(5).ToListAsync());
196+
197+
Assert.That(result, Has.Count.EqualTo(5));
198+
Assert.That(session.GetCurrentLockMode(result[0]), Is.EqualTo(LockMode.Upgrade));
199+
200+
await (AssertSeparateTransactionIsLockedOutAsync(result[0].CustomerId));
201+
}
202+
}
203+
204+
private Task AssertSeparateTransactionIsLockedOutAsync(string customerId, CancellationToken cancellationToken = default(CancellationToken))
205+
{
206+
try
207+
{
208+
using (new TransactionScope(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled))
209+
using (var s2 = OpenSession())
210+
using (s2.BeginTransaction())
211+
{
212+
// TODO: We should try to verify that the exception actually IS a locking failure and not something unrelated.
213+
Assert.ThrowsAsync<GenericADOException>(
214+
async () =>
215+
{
216+
var result2 = await ((
217+
from e in s2.Query<Customer>()
218+
where e.CustomerId == customerId
219+
select e
220+
).WithLock(LockMode.UpgradeNoWait)
221+
.WithOptions(o => o.SetTimeout(5))
222+
.ToListAsync(cancellationToken));
223+
Assert.That(result2, Is.Not.Null);
224+
},
225+
"Expected an exception to indicate locking failure due to already locked.");
226+
}
227+
return Task.CompletedTask;
228+
}
229+
catch (Exception ex)
230+
{
231+
return Task.FromException<object>(ex);
232+
}
233+
}
234+
235+
[Test]
236+
[Description("Verify that different lock modes are respected even if the query is otherwise exactly the same.")]
237+
public async Task CanChangeLockModeForQueryAsync()
238+
{
239+
// Limit to a few dialects where we know the "nowait" keyword is used to make life easier.
240+
Assume.That(Dialect is MsSql2000Dialect || Dialect is Oracle8iDialect || Dialect is PostgreSQL81Dialect);
241+
242+
using (session.BeginTransaction())
243+
{
244+
var result = await (BuildQueryableAllCustomers(db.Customers, LockMode.Upgrade).ToListAsync());
245+
Assert.That(result, Has.Count.EqualTo(91));
246+
247+
using (var logSpy = new SqlLogSpy())
248+
{
249+
// Only difference in query is the lockmode - make sure it gets picked up.
250+
var result2 = await (BuildQueryableAllCustomers(session.Query<Customer>(), LockMode.UpgradeNoWait)
251+
.ToListAsync());
252+
Assert.That(result2, Has.Count.EqualTo(91));
253+
254+
Assert.That(logSpy.GetWholeLog().ToLower(), Does.Contain("nowait"));
255+
}
256+
}
257+
}
258+
259+
private static IQueryable<Customer> BuildQueryableAllCustomers(
260+
IQueryable<Customer> dbCustomers,
261+
LockMode lockMode)
262+
{
263+
return (from e in dbCustomers select e).WithLock(lockMode).WithOptions(o => o.SetTimeout(5));
264+
}
265+
}
266+
}

0 commit comments

Comments
 (0)