Skip to content

Commit f832a97

Browse files
maca88fredericDelaporte
authored andcommitted
Fix concurrency issue for update/delete commands when using filters
1 parent 30d9a2c commit f832a97

File tree

8 files changed

+172
-10
lines changed

8 files changed

+172
-10
lines changed
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
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.Collections.Generic;
12+
using System.Linq;
13+
using System.Threading.Tasks;
14+
using NHibernate.Linq;
15+
using NUnit.Framework;
16+
17+
namespace NHibernate.Test.NHSpecificTest.GH2710
18+
{
19+
using System.Threading;
20+
[TestFixture]
21+
public class FixtureAsync : BugTestCase
22+
{
23+
protected override void OnSetUp()
24+
{
25+
using (var session = OpenSession())
26+
using (var transaction = session.BeginTransaction())
27+
{
28+
var e = new Entity {MbrId = 1, MrcDailyMoved = "N"};
29+
session.Save(e);
30+
31+
transaction.Commit();
32+
}
33+
}
34+
35+
protected override void OnTearDown()
36+
{
37+
using (var session = OpenSession())
38+
using (var transaction = session.BeginTransaction())
39+
{
40+
session.CreateQuery("delete from Entity").ExecuteUpdate();
41+
session.CreateQuery("delete from System.Object").ExecuteUpdate();
42+
43+
transaction.Commit();
44+
}
45+
}
46+
47+
[Test]
48+
public async Task TestAsync()
49+
{
50+
var ids = Enumerable.Range(1, 10).ToList();
51+
await (Task.WhenAll(Enumerable.Range(1, 50 - 1).Select(i =>
52+
{
53+
return UpdateEntityAsync(ids);
54+
})));
55+
}
56+
57+
private async Task UpdateEntityAsync(List<int> ids, CancellationToken cancellationToken = default(CancellationToken))
58+
{
59+
using (var session = OpenSession())
60+
using (var t = session.BeginTransaction())
61+
{
62+
session.EnableFilter("Filter").SetParameter("MbrId", 5);
63+
await (session.Query<Entity>()
64+
.Where(o => ids.Contains(o.Id))
65+
.UpdateAsync(o => new Entity { MrcDailyMoved = "Y" }, cancellationToken));
66+
67+
await (t.CommitAsync(cancellationToken));
68+
}
69+
}
70+
}
71+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using System;
2+
3+
namespace NHibernate.Test.NHSpecificTest.GH2710
4+
{
5+
public class Entity
6+
{
7+
public virtual int Id { get; set; }
8+
public virtual int MbrId { get; set; }
9+
public virtual string MrcDailyMoved { get; set; } = "N";
10+
}
11+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using System.Threading.Tasks;
4+
using NHibernate.Linq;
5+
using NUnit.Framework;
6+
7+
namespace NHibernate.Test.NHSpecificTest.GH2710
8+
{
9+
[TestFixture]
10+
public class Fixture : BugTestCase
11+
{
12+
protected override void OnSetUp()
13+
{
14+
using (var session = OpenSession())
15+
using (var transaction = session.BeginTransaction())
16+
{
17+
var e = new Entity {MbrId = 1, MrcDailyMoved = "N"};
18+
session.Save(e);
19+
20+
transaction.Commit();
21+
}
22+
}
23+
24+
protected override void OnTearDown()
25+
{
26+
using (var session = OpenSession())
27+
using (var transaction = session.BeginTransaction())
28+
{
29+
session.CreateQuery("delete from Entity").ExecuteUpdate();
30+
session.CreateQuery("delete from System.Object").ExecuteUpdate();
31+
32+
transaction.Commit();
33+
}
34+
}
35+
36+
[Test]
37+
public void Test()
38+
{
39+
var ids = Enumerable.Range(1, 10).ToList();
40+
Parallel.For(1, 50, i =>
41+
{
42+
UpdateEntity(ids);
43+
});
44+
}
45+
46+
private void UpdateEntity(List<int> ids)
47+
{
48+
using (var session = OpenSession())
49+
using (var t = session.BeginTransaction())
50+
{
51+
session.EnableFilter("Filter").SetParameter("MbrId", 5);
52+
session.Query<Entity>()
53+
.Where(o => ids.Contains(o.Id))
54+
.Update(o => new Entity { MrcDailyMoved = "Y" });
55+
56+
t.Commit();
57+
}
58+
}
59+
}
60+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NHibernate.Test"
3+
namespace="NHibernate.Test.NHSpecificTest.GH2710">
4+
5+
<class name="Entity">
6+
<id name="Id" generator="native"/>
7+
<property name="MbrId" column="mbrid"/>
8+
<property name="MrcDailyMoved"/>
9+
<filter name="Filter" condition="mbrid = :MbrId"/>
10+
</class>
11+
12+
<filter-def name="Filter">
13+
<filter-param name="MbrId" type="int"/>
14+
</filter-def>
15+
16+
</hibernate-mapping>

src/NHibernate/Async/Hql/Ast/ANTLR/Exec/BasicExecutor.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,14 @@ public override async Task<int> ExecuteAsync(QueryParameters parameters, ISessio
4545
try
4646
{
4747
CheckParametersExpectedType(parameters); // NH Different behavior (NH-1898)
48-
49-
var sqlString = FilterHelper.ExpandDynamicFilterParameters(sql, Parameters, session);
48+
// Create a copy of Parameters as ExpandDynamicFilterParameters may modify it
49+
var parameterSpecifications = Parameters.ToList();
50+
var sqlString = FilterHelper.ExpandDynamicFilterParameters(sql, parameterSpecifications, session);
5051
var sqlQueryParametersList = sqlString.GetParameters().ToList();
51-
SqlType[] parameterTypes = Parameters.GetQueryParameterTypes(sqlQueryParametersList, session.Factory);
52+
SqlType[] parameterTypes = parameterSpecifications.GetQueryParameterTypes(sqlQueryParametersList, session.Factory);
5253

5354
st = await (session.Batcher.PrepareCommandAsync(CommandType.Text, sqlString, parameterTypes, cancellationToken)).ConfigureAwait(false);
54-
foreach (var parameterSpecification in Parameters)
55+
foreach (var parameterSpecification in parameterSpecifications)
5556
{
5657
await (parameterSpecification.BindAsync(st, sqlQueryParametersList, parameters, session, cancellationToken)).ConfigureAwait(false);
5758
}

src/NHibernate/Async/Hql/Ast/ANTLR/Exec/MultiTableDeleteExecutor.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ public override async Task<int> ExecuteAsync(QueryParameters parameters, ISessio
4444
{
4545
try
4646
{
47-
var paramsSpec = Walker.Parameters;
47+
// Create a copy of Parameters as ExpandDynamicFilterParameters may modify it
48+
var paramsSpec = Walker.Parameters.ToList();
4849
var sqlString = FilterHelper.ExpandDynamicFilterParameters(idInsertSelect, paramsSpec, session);
4950
var sqlQueryParametersList = sqlString.GetParameters().ToList();
5051
SqlType[] parameterTypes = paramsSpec.GetQueryParameterTypes(sqlQueryParametersList, session.Factory);

src/NHibernate/Hql/Ast/ANTLR/Exec/BasicExecutor.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,14 @@ public override int Execute(QueryParameters parameters, ISessionImplementor sess
6161
try
6262
{
6363
CheckParametersExpectedType(parameters); // NH Different behavior (NH-1898)
64-
65-
var sqlString = FilterHelper.ExpandDynamicFilterParameters(sql, Parameters, session);
64+
// Create a copy of Parameters as ExpandDynamicFilterParameters may modify it
65+
var parameterSpecifications = Parameters.ToList();
66+
var sqlString = FilterHelper.ExpandDynamicFilterParameters(sql, parameterSpecifications, session);
6667
var sqlQueryParametersList = sqlString.GetParameters().ToList();
67-
SqlType[] parameterTypes = Parameters.GetQueryParameterTypes(sqlQueryParametersList, session.Factory);
68+
SqlType[] parameterTypes = parameterSpecifications.GetQueryParameterTypes(sqlQueryParametersList, session.Factory);
6869

6970
st = session.Batcher.PrepareCommand(CommandType.Text, sqlString, parameterTypes);
70-
foreach (var parameterSpecification in Parameters)
71+
foreach (var parameterSpecification in parameterSpecifications)
7172
{
7273
parameterSpecification.Bind(st, sqlQueryParametersList, parameters, session);
7374
}

src/NHibernate/Hql/Ast/ANTLR/Exec/MultiTableDeleteExecutor.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@ public override int Execute(QueryParameters parameters, ISessionImplementor sess
8181
{
8282
try
8383
{
84-
var paramsSpec = Walker.Parameters;
84+
// Create a copy of Parameters as ExpandDynamicFilterParameters may modify it
85+
var paramsSpec = Walker.Parameters.ToList();
8586
var sqlString = FilterHelper.ExpandDynamicFilterParameters(idInsertSelect, paramsSpec, session);
8687
var sqlQueryParametersList = sqlString.GetParameters().ToList();
8788
SqlType[] parameterTypes = paramsSpec.GetQueryParameterTypes(sqlQueryParametersList, session.Factory);

0 commit comments

Comments
 (0)