diff --git a/src/NHibernate.Test/Async/NHSpecificTest/GH2710/Fixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/GH2710/Fixture.cs
new file mode 100644
index 00000000000..d8229fc5aa3
--- /dev/null
+++ b/src/NHibernate.Test/Async/NHSpecificTest/GH2710/Fixture.cs
@@ -0,0 +1,71 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by AsyncGenerator.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using NHibernate.Linq;
+using NUnit.Framework;
+
+namespace NHibernate.Test.NHSpecificTest.GH2710
+{
+ using System.Threading;
+ [TestFixture]
+ public class FixtureAsync : BugTestCase
+ {
+ protected override void OnSetUp()
+ {
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ var e = new Entity {MbrId = 1, MrcDailyMoved = "N"};
+ session.Save(e);
+
+ transaction.Commit();
+ }
+ }
+
+ protected override void OnTearDown()
+ {
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ session.CreateQuery("delete from Entity").ExecuteUpdate();
+ session.CreateQuery("delete from System.Object").ExecuteUpdate();
+
+ transaction.Commit();
+ }
+ }
+
+ [Test]
+ public async Task TestAsync()
+ {
+ var ids = Enumerable.Range(1, 10).ToList();
+ await (Task.WhenAll(Enumerable.Range(1, 50 - 1).Select(i =>
+ {
+ return UpdateEntityAsync(ids);
+ })));
+ }
+
+ private async Task UpdateEntityAsync(List ids, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ using (var session = OpenSession())
+ using (var t = session.BeginTransaction())
+ {
+ session.EnableFilter("Filter").SetParameter("MbrId", 5);
+ await (session.Query()
+ .Where(o => ids.Contains(o.Id))
+ .UpdateAsync(o => new Entity { MrcDailyMoved = "Y" }, cancellationToken));
+
+ await (t.CommitAsync(cancellationToken));
+ }
+ }
+ }
+}
diff --git a/src/NHibernate.Test/NHSpecificTest/GH2710/Entity.cs b/src/NHibernate.Test/NHSpecificTest/GH2710/Entity.cs
new file mode 100644
index 00000000000..bb0758de0dd
--- /dev/null
+++ b/src/NHibernate.Test/NHSpecificTest/GH2710/Entity.cs
@@ -0,0 +1,11 @@
+using System;
+
+namespace NHibernate.Test.NHSpecificTest.GH2710
+{
+ public class Entity
+ {
+ public virtual int Id { get; set; }
+ public virtual int MbrId { get; set; }
+ public virtual string MrcDailyMoved { get; set; } = "N";
+ }
+}
diff --git a/src/NHibernate.Test/NHSpecificTest/GH2710/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/GH2710/Fixture.cs
new file mode 100644
index 00000000000..f71eddf12ba
--- /dev/null
+++ b/src/NHibernate.Test/NHSpecificTest/GH2710/Fixture.cs
@@ -0,0 +1,60 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using NHibernate.Linq;
+using NUnit.Framework;
+
+namespace NHibernate.Test.NHSpecificTest.GH2710
+{
+ [TestFixture]
+ public class Fixture : BugTestCase
+ {
+ protected override void OnSetUp()
+ {
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ var e = new Entity {MbrId = 1, MrcDailyMoved = "N"};
+ session.Save(e);
+
+ transaction.Commit();
+ }
+ }
+
+ protected override void OnTearDown()
+ {
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ session.CreateQuery("delete from Entity").ExecuteUpdate();
+ session.CreateQuery("delete from System.Object").ExecuteUpdate();
+
+ transaction.Commit();
+ }
+ }
+
+ [Test]
+ public void Test()
+ {
+ var ids = Enumerable.Range(1, 10).ToList();
+ Parallel.For(1, 50, i =>
+ {
+ UpdateEntity(ids);
+ });
+ }
+
+ private void UpdateEntity(List ids)
+ {
+ using (var session = OpenSession())
+ using (var t = session.BeginTransaction())
+ {
+ session.EnableFilter("Filter").SetParameter("MbrId", 5);
+ session.Query()
+ .Where(o => ids.Contains(o.Id))
+ .Update(o => new Entity { MrcDailyMoved = "Y" });
+
+ t.Commit();
+ }
+ }
+ }
+}
diff --git a/src/NHibernate.Test/NHSpecificTest/GH2710/Mappings.hbm.xml b/src/NHibernate.Test/NHSpecificTest/GH2710/Mappings.hbm.xml
new file mode 100644
index 00000000000..b3db1418124
--- /dev/null
+++ b/src/NHibernate.Test/NHSpecificTest/GH2710/Mappings.hbm.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/NHibernate/Async/Hql/Ast/ANTLR/Exec/BasicExecutor.cs b/src/NHibernate/Async/Hql/Ast/ANTLR/Exec/BasicExecutor.cs
index 01c445dc51a..a82b629655c 100644
--- a/src/NHibernate/Async/Hql/Ast/ANTLR/Exec/BasicExecutor.cs
+++ b/src/NHibernate/Async/Hql/Ast/ANTLR/Exec/BasicExecutor.cs
@@ -45,13 +45,14 @@ public override async Task ExecuteAsync(QueryParameters parameters, ISessio
try
{
CheckParametersExpectedType(parameters); // NH Different behavior (NH-1898)
-
- var sqlString = FilterHelper.ExpandDynamicFilterParameters(sql, Parameters, session);
+ // Create a copy of Parameters as ExpandDynamicFilterParameters may modify it
+ var parameterSpecifications = Parameters.ToList();
+ var sqlString = FilterHelper.ExpandDynamicFilterParameters(sql, parameterSpecifications, session);
var sqlQueryParametersList = sqlString.GetParameters().ToList();
- SqlType[] parameterTypes = Parameters.GetQueryParameterTypes(sqlQueryParametersList, session.Factory);
+ SqlType[] parameterTypes = parameterSpecifications.GetQueryParameterTypes(sqlQueryParametersList, session.Factory);
st = await (session.Batcher.PrepareCommandAsync(CommandType.Text, sqlString, parameterTypes, cancellationToken)).ConfigureAwait(false);
- foreach (var parameterSpecification in Parameters)
+ foreach (var parameterSpecification in parameterSpecifications)
{
await (parameterSpecification.BindAsync(st, sqlQueryParametersList, parameters, session, cancellationToken)).ConfigureAwait(false);
}
diff --git a/src/NHibernate/Async/Hql/Ast/ANTLR/Exec/MultiTableDeleteExecutor.cs b/src/NHibernate/Async/Hql/Ast/ANTLR/Exec/MultiTableDeleteExecutor.cs
index b00757ffec0..478055877ec 100644
--- a/src/NHibernate/Async/Hql/Ast/ANTLR/Exec/MultiTableDeleteExecutor.cs
+++ b/src/NHibernate/Async/Hql/Ast/ANTLR/Exec/MultiTableDeleteExecutor.cs
@@ -44,7 +44,8 @@ public override async Task ExecuteAsync(QueryParameters parameters, ISessio
{
try
{
- var paramsSpec = Walker.Parameters;
+ // Create a copy of Parameters as ExpandDynamicFilterParameters may modify it
+ var paramsSpec = Walker.Parameters.ToList();
var sqlString = FilterHelper.ExpandDynamicFilterParameters(idInsertSelect, paramsSpec, session);
var sqlQueryParametersList = sqlString.GetParameters().ToList();
SqlType[] parameterTypes = paramsSpec.GetQueryParameterTypes(sqlQueryParametersList, session.Factory);
diff --git a/src/NHibernate/Hql/Ast/ANTLR/Exec/BasicExecutor.cs b/src/NHibernate/Hql/Ast/ANTLR/Exec/BasicExecutor.cs
index 0a9fa837077..87d31aa6251 100644
--- a/src/NHibernate/Hql/Ast/ANTLR/Exec/BasicExecutor.cs
+++ b/src/NHibernate/Hql/Ast/ANTLR/Exec/BasicExecutor.cs
@@ -61,13 +61,14 @@ public override int Execute(QueryParameters parameters, ISessionImplementor sess
try
{
CheckParametersExpectedType(parameters); // NH Different behavior (NH-1898)
-
- var sqlString = FilterHelper.ExpandDynamicFilterParameters(sql, Parameters, session);
+ // Create a copy of Parameters as ExpandDynamicFilterParameters may modify it
+ var parameterSpecifications = Parameters.ToList();
+ var sqlString = FilterHelper.ExpandDynamicFilterParameters(sql, parameterSpecifications, session);
var sqlQueryParametersList = sqlString.GetParameters().ToList();
- SqlType[] parameterTypes = Parameters.GetQueryParameterTypes(sqlQueryParametersList, session.Factory);
+ SqlType[] parameterTypes = parameterSpecifications.GetQueryParameterTypes(sqlQueryParametersList, session.Factory);
st = session.Batcher.PrepareCommand(CommandType.Text, sqlString, parameterTypes);
- foreach (var parameterSpecification in Parameters)
+ foreach (var parameterSpecification in parameterSpecifications)
{
parameterSpecification.Bind(st, sqlQueryParametersList, parameters, session);
}
diff --git a/src/NHibernate/Hql/Ast/ANTLR/Exec/MultiTableDeleteExecutor.cs b/src/NHibernate/Hql/Ast/ANTLR/Exec/MultiTableDeleteExecutor.cs
index 451ae14ba84..1964fcffc75 100644
--- a/src/NHibernate/Hql/Ast/ANTLR/Exec/MultiTableDeleteExecutor.cs
+++ b/src/NHibernate/Hql/Ast/ANTLR/Exec/MultiTableDeleteExecutor.cs
@@ -81,7 +81,8 @@ public override int Execute(QueryParameters parameters, ISessionImplementor sess
{
try
{
- var paramsSpec = Walker.Parameters;
+ // Create a copy of Parameters as ExpandDynamicFilterParameters may modify it
+ var paramsSpec = Walker.Parameters.ToList();
var sqlString = FilterHelper.ExpandDynamicFilterParameters(idInsertSelect, paramsSpec, session);
var sqlQueryParametersList = sqlString.GetParameters().ToList();
SqlType[] parameterTypes = paramsSpec.GetQueryParameterTypes(sqlQueryParametersList, session.Factory);