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);